四、建立个人博客

在本章中,我们将用 Laravel 编写一个简单的个人博客。我们还将介绍 Laravel 的内置身份验证、分页机制和命名路由。我们将详细介绍 Laravel 附带的一些快速开发方法,例如创建路由 URL。本章将涵盖以下主题:

  • 创建和迁移帖子数据库
  • 创建帖子模型
  • 创建和迁移作者数据库
  • 创建仅限成员的区域
  • 保存博客文章
  • 将博客文章分配给用户
  • 列出文章
  • 对内容分页

创建和迁移帖子数据库

我们假设您已经在app/config/database.php文件中定义了数据库凭证。对于这个应用,我们需要一个数据库。您可以简单地创建并运行以下 SQL 命令,或者基本上可以使用您的数据库管理界面,类似于 phpMyAdmin:

CREATE DATABASE laravel_blog

成功为应用创建数据库后,首先我们需要创建一个 posts 表并将其安装在数据库中。为此,请打开您的终端,浏览项目文件夹,并运行以下命令:

php artisan migrate:make create_posts_table --table=posts --create

该命令将在app/database/migrations下生成一个迁移文件,用于在我们的laravel_blog数据库中生成一个名为posts的新 MySQL 表。

要定义我们的表列和规格,我们需要编辑这个文件。编辑迁移文件后,它应该如下所示:

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePostsTable extends Migration {

  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('posts', function(Blueprint $table)
    {
      $table->increments('id');
      $table->string('title');
      $table->text('content');
      $table->integer('author_id');
      $table->timestamps();
    });
  }

  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::drop('posts');
  }
}

保存文件后,我们需要使用一个简单的artisan命令来执行迁移:

php artisian migrate

如果没有出现错误,检查posts表和列的laravel_blog数据库。

创建帖子模型

正如你所知,对于任何与 Laravel 上的数据库操作相关的事情,使用模型是最好的做法。我们将受益于雄辩的 ORM。

将该代码保存在app/models/下名为Posts.php的文件中:

<?php
class Post extends Eloquent {

protected $table = 'posts';

protected $fillable = array('title','content','author_id');

public $timestamps = true;

public function Author(){

      return $this->belongsTo('User','author_id');
}

}

我们已经用受保护的$table变量设置了数据库表名。我们还用$fillable变量设置了可编辑列,用$timestamps变量设置了时间戳,正如我们在前面的章节中已经看到和使用的那样。模型中定义的变量足以使用拉威尔的雄辩表单。我们将在本章的为用户分配博文部分介绍公共Author()功能。

我们的帖子模型已经准备好了。现在我们需要一个作者模型和数据库来分配博客文章给作者。让我们研究一下 Laravel 的内置身份验证机制。

创建和迁移作者数据库

与大多数 PHP 框架相反, Laravel 有一个基本的身份验证类。身份验证类对于快速开发应用非常有帮助。首先,我们需要一个应用的密钥。应用密钥对于我们应用的安全性非常重要,因为所有数据都是用这个密钥散列的。artisan 命令可以通过一个命令行为我们生成这个密钥:

php artisian key:generate

如果没有出现错误,您将看到一条消息,告诉您密钥已成功生成。生成密钥后,如果您在打开 Laravel 应用时遇到问题,只需清除浏览器缓存,然后重试。接下来,我们应该编辑身份验证类的配置文件。为了使用 Laravel 的内置认证类,我们需要编辑配置文件,该文件位于app/config/auth.php。该文件包含身份验证工具的几个选项。如果需要更改表名等等,可以在这个文件下进行更改。默认情况下,Laravel 自带User车型。你可以看到位于app/models/User.php文件。使用 Laravel 4,我们需要定义哪些字段可以在我们的Users模型中填充。让我们编辑位于app/models/User.php并添加“可填充”数组:

<?php

use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;

class User extends Eloquent implements UserInterface, RemindableInterface {

  /**
   * The database table used by the model.
   *
   * @var string
   */
  protected $table = 'users';

  /**
   * The attributes excluded from the model's JSON form.
   *
   * @var array
   */
  protected $hidden = array('password');

  //Add to the "fillable" array
   protected $fillable = array('email', 'password', 'name');

  /**
   * Get the unique identifier for the user.
   *
   * @return mixed
   */
  public function getAuthIdentifier()
  {
    return $this->getKey();
  }

  /**
   * Get the password for the user.
   *
   * @return string
   */
  public function getAuthPassword()
  {
    return $this->password;
  }

  /**
   * Get the e-mail address where password reminders are sent.
   *
   * @return string
   */
  public function getReminderEmail()
  {
    return $this->email;
  }

}

基本上,我们需要三个专栏给我们的作者。这些是:

  • email:此栏存储作者的电子邮件
  • password:此栏存储作者密码
  • name:此栏存储作者姓名

现在我们需要几个迁移文件来创建users表,并将作者添加到我们的数据库中。要创建迁移文件,请给出如下命令:

php artisan migrate:make create_users_table --table=users --create

打开最近创建的位于app/database/migrations/的迁移文件。我们需要编辑up()功能如下:

  public function up()
  {
    Schema::create('users', function(Blueprint $table)
    {
      $table->increments('id');
      $table->string('email');
      $table->string('password');
      $table->string('name');
      $table->timestamps();
    });
  }

编辑完迁移文件后,运行 migrate命令:

php artisian migrate

如您所知,命令创建了users表及其列。如果没有出现错误,请查看users表和列的laravel_blog数据库。

现在我们需要创建一个新的迁移文件,以便向数据库中添加一些作者。我们可以通过运行以下命令来实现:

php artisan migrate:make add_some_users

打开迁移文件,编辑up()功能如下:

  public function up()
  {
    User::create(array(
            'email' => 'your@email.com',
            'password' => Hash::make('password'),
            'name' => 'John Doe'
        ));
  }

我们在up()函数中使用了一个新的类,命名为Hash。Laravel 有一个基于安全加密的哈希生成器/校验器类。Bcrypt 是一种用于密码等重要数据的公认的安全哈希方法。

我们在本章开头用 artisan 工具为其创建了一个应用密钥的类用于盐析。因此,要应用迁移,我们需要使用以下 artisan 命令进行迁移:

php artisian migrate

现在,查看users表进行记录。如果您勾选password列,您将看到如下存储的记录:

$2y$08$ayylAhkVNCnkfj2rITbQr.L5pd2AIfpeccdnW6.BGbA.1VtJ6Sdqy

安全存储用户密码及其关键数据非常重要。不要忘记,如果更改应用密钥,所有现有的哈希记录都将不可用,因为Hash类在验证和存储给定数据时使用应用密钥作为盐密钥。

创建仅限成员的区域

如您所知,我们的博客系统是基于会员的。正因为如此,我们需要一些只能被成员访问的区域,用于向博客添加新帖子。我们有两种不同的方法来做到这一点。第一个是路由过滤方法,我们将在接下来的章节中详细介绍。第二个是基于模板的授权检查。这种方法是理解使用Auth类和 刀片模板系统的更有效的方法。

使用Auth类,我们只需一行代码就可以检查访问者的授权状态:

Auth::check();

基于Auth类的check()函数总是返回truefalse。因此,这意味着我们可以在代码中的if/else语句中轻松使用该函数。正如您在前面章节中所知道的,使用刀片模板系统,我们能够在模板文件中使用这种 PHP 语句。

在创建模板文件之前,我们需要编写我们的路线。我们的应用需要四条路线。这些是:

  • 处理登录请求的登录路径
  • 处理新帖子请求的新帖子路由
  • 显示新帖子表单和登录表单的管理路径
  • 列出帖子的索引路径

命名路由是用于快速开发的 Laravel 框架的另一个惊人的特性。命名路由允许在生成重定向或 URL 时更方便地引用路由。您可以为路由指定如下名称:

Route::get('all/posts', array('as' => 'posts', function()
{
    //
}));

您也可以为控制器指定路由名称:

Route::get('all/posts', array('as' => 'allposts', , 'uses' => 'PostController@showPosts'));

得益于命名路由,我们可以轻松地为我们的应用创建 URL:

$url = URL::route('allposts');

我们还可以使用命名路由来重定向:

$redirect = Redirect::route('allposts');

打开位于app/routes.php的路由配置文件,添加如下代码:

Route::get('/', array('as' => 'index', 'uses' => 'PostsController@getIndex'));
Route::get('/admin', array('as' => 'admin_area', 'uses' => 'PostsController@getAdmin'));
Route::post('/add', array('as' => 'add_new_post', 'uses' => 'PostsController@postAdd'));
Route::post('/login', array('as' => 'login', 'uses' => 'UsersController@postLogin'));
Route::get('/logout', array('as' => 'logout', 'uses' => 'UsersController@getLogout'));

现在我们需要为我们的应用的控制器端和模板编写代码。首先,我们可以从管理区开始编码。让我们在app/views/下创建一个名为addpost.blade.php的文件。我们的管理模板应该如下所示:

<html>
<head>
<title>Welcome to Your Blog</title>
<link rel="stylesheet" type="text/css" href="/img/css/style.css">
<!--[if lt IE 9]><script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
</head>
<body>
@if(Auth::check())
<section class="container">
<div class="content">
<h1>Welcome to Admin Area, {{Auth::user()->name}} ! - <b>{{link_to_route('logout','Logout')}}</b></h1>
<form name="add_post" method="POST" action="{{URL::route('add_new_post')}}">
<p><input type="text" name="title" placeholder="Post Title" value=""/></p>
<p><textarea name="content" placeholder="Post Content"></textarea></p>
<p><input type="submit" name="submit" /></p>
</div>
</section>
@else
<section class="container">
<div class="login">
<h1>Please Login</h1>
<form name="login" method="POST" action="{{URL::route('login')}}">
<p><input type="text" name="email" value="" placeholder="Email"></p>
<p><input type="password" name="password" value="" placeholder="Password"></p>
<p class="submit"><input type="submit" name="commit" value="Login"></p>
</form>
</div>
</section>
@endif
</body>
</html>

正如您在代码中看到的,我们在模板中使用if / else语句来检查用户的登录凭据。从本节开始,我们已经知道我们使用Auth::check()功能来检查用户的登录状态。此外,我们还使用了一种新方法来获取当前登录用户的姓名:

Auth::user()->name;

我们可以通过user方法获取当前用户的任何信息:

Auth::user()->id; 
Auth::user()->email;

模板代码首先检查访问者的登录状态。如果访问者已经登录,模板会显示一个新的帖子表单;否则它会显示一个登录表单。

现在我们必须对博客应用的控制器端进行编码。让我们从用户控制器开始。在app/controller/下创建一个文件,命名为UsersContoller.php。控制器的最终代码如下:

<?php

class UsersController extends BaseController{

  public function postLogin()
  {
    Auth::attempt(array('email' => Input::get('email'),'password' => Input::get('password')));
  return Redirect::route('add_new_post');

  }

  public function getLogout()
  {
    Auth::logout();
    return Redirect::route('index');
  }
}

控制器有两个功能:第一个是postLogin()功能。该功能主要是检查发布的表单数据以供用户登录,然后将访问者重定向到add_new_post路线以显示新的发布表单。第二个功能处理注销请求并重定向到index路线。

保存博文

现在我们需要多一个控制器来控制我们的博客文章。所以,在app/controller/下创建一个文件,命名为PostsContoller.php。控制器的最终代码如下:

<?php
class PostsController extends BaseController{

  public function getIndex()
  {

  $posts = Post::with('Author')-> orderBy('id', 'DESC')->get();
  return View::make('index')->with('posts',$posts);

  }
  public function getAdmin()
  {
  return View::make('addpost');
  }
  public function postAdd()
  {
  Post::create(array(
              'title' => Input::get('title'),
              'content' => Input::get('content'),
              'author_id' => Auth::user()->id
   ));
  return Redirect::route('index');
  }
}

给用户分配博文

postAdd()功能处理在数据库上的新博文创建请求。如您所见,我们可以通过前面提到的方法获得作者的 ID:

Auth::user()->id

通过这种方法,我们可以为当前用户分配一篇博文。正如您将看到的,我们在查询中有一个新方法:

Post::with('Author')->

如果你还记得,我们已经在Posts模型中定义了一个公共Author ()函数:

public function Author(){

      return $this->belongsTo('User','author_id');
}

belongsTo()方法是一个Eloquent功能来创建表格之间的关系。基本上,该函数需要一个必需变量和一个可选变量。第一个变量(必需)定义了目标Model。第二个可选变量是定义当前模型表的源列。如果没有定义可选变量,Eloquent类将搜索targetModelName_id列。在posts表中,我们将作者的 id 存储在author_id列中,而不是名为user_id的列中。因此,我们需要在函数中定义第二个可选变量。通过这种方法,我们可以将我们的博客文章及其所有作者的信息传递给模板文件。您可以将该方法视为某种 SQL 连接方法。

当我们想在查询中使用这些关系函数时,我们可以很容易地按如下方式调用它们:

Books::with('Categories')->with('Author')->get();

用较少的变量管理模板文件很容易。现在我们只有一个变量来传递模板文件,它与所有必要的数据相结合。因此,我们需要第二个模板文件来列出我们的博客文章。这个模板将在我们博客的前端工作。

物品清单

在本章前面的小节中,我们已经学习了在刀片模板文件中使用 PHP if/else语句。Laravel 将数据作为数组传递给模板文件。所以我们需要使用foreach循环将数据解析成模板文件。我们也可以在模板文件中使用foreach循环。所以在app/views/下创建一个名为index.blade.php的文件。代码应该如下所示:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My Awesome Blog</title>
<link rel="stylesheet" href="/img/blog/css/styles.css" type="text/css" media="screen" />
<link rel="stylesheet" type="text/css" href="/img/blog/css/print.css" media="print" />
<!--[if IE]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
</head>
<body>
<div id="wrapper">
<header>
<h1><a href="/">My Awesome Blog</a></h1>
<p>Welcome to my awesome blog</p>
</header>
<section id="main">
<section id="content">
@foreach($posts as $post)
<article>
<h2>{{$post->title}}</h2>
<p>{{$post->content}}</p>
<p><small>Posted by <b>{{$post->Author->name}}</b> at <b>{{$post->created_at}}</b></small></p>
</article>

@endforeach          
</section>
</aside>
</section>
<footer>
<section id="footer-area">
<section id="footer-outer-block">
<aside class="footer-segment">
<h4>My Awesome Blog</h4>
</aside>
</section>
</section>
</footer>
</div>
</body>
</html>

让我们挖掘代码。我们在模板文件中使用了一个foreach循环来解析所有的博客文章数据。同样,我们在foreach循环中看到合并的作者数据使用。大家可能还记得,我们通过模型端的belongsTo()方法获取作者信息。整个关系数据解析是在一个数组中完成的,这个数组被称为关系函数名。例如,如果我们有一个名为Categories()的第二个关系函数,那么控制器端的查询将如下所示:

$books = Books::with('Author')-> with('Categories')->orderBy('id', 'DESC')->get();

foreach循环如下所示:

@foreach($books as $book)

<article>
<h2>{{$book->title}}</h2>
<p>Author: <b>{{$book->Author->name}}</b></p>
<p>Category: <b>{{$book->Category->name}}</b></p>
</article>

@endforeach

对内容进行分页

雄辩的 get()方法,我们已经在Eloquent查询中的控制器端使用过,在给定条件下从数据库中获取所有数据。通常,我们需要为用户友好的前端或者更少的页面加载和优化对内容进行分页。Eloquent类有一个有用的方法可以快速做到这一点,叫做paginate()。此方法获取分页的数据,并仅用一行代码在模板中生成分页链接。打开app/controllers/PostsController.php文件,更改查询如下:

$posts = Post::with('Author')->orderBy('id', 'DESC')->paginate(5);

paginate()方法用给定的数值对数据进行分页。所以,博文会将每一页分页为5博文。我们还必须改变我们的模板来显示分页链接。打开app/views/index.blade.php并在foreach循环后添加以下代码:

{{$posts->links()}}

模板中以 i̇d 为"main",的部分应如下所示:

<section id="main">
<section id="content">
@foreach($posts as $post)

<article>
<h2>{{$post->title}}</h2>
<p>{{$post->content}}</p>
<p><small>Posted by <b>{{$post->Author->name}}</b> at <b>{{$post->created_at}}</b></small></p>
</article>
@endforeach

</section>
{{$posts->links()}}
</section>

links()功能会自动生成分页链接,如果有足够的数据可以分页。否则,该函数不显示任何内容。

总结

在这一章中,我们创建了一个简单的博客,其中包含了 Laravel 的内置函数和雄辩的数据库驱动程序。我们已经学习了如何对数据进行分页以及雄辩的基本数据关系机制。我们还介绍了 Laravel 的内置身份验证机制。在接下来的章节中,我们将学习如何处理更复杂的表和关系数据。