larablog 系列文章 03 - 文章模型:使用 Eloquent 建立模型与数据填充

本章将开始探索博客的模型。该模型将使用 Eloquent 对象关系映射(ORM)实现,还会介绍如何填充测试数据,它是一个有利于我们进行开发和测试数据库的好途径。在文章结束后,你将会了解如何定义模型,更新数据库并创建一些可用于测试的填充数据,还将了解如何构建用于展示博客页面的基础知识。

Eloquent

对于我们的博客来说,我们需要一种持久化数据的方法。而 Laravel 的 Eloquent ORM 提供了漂亮、简洁的 ActiveRecord 实现来和数据库进行交互。每个数据库表都有一个对应的「模型」可用来跟数据表进行交互。让给你可以通过模型查找数据表内的数据,以及将记录添加到数据表中。

对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。

ORM 有助于将关系数据库(如MySQL)的数据转换为可以操作的 PHP 对象。这使我们能够在类中的表上封装我们需要的功能。想想一个用户表,它可能有用户名,密码等字段。使用ORM,我们将操作的是一个包含成员用户名,密码等内容的类,它允许我们调用相应的方法来对数据实体进行操作。随着我们在本教程中的进展,你将更加熟悉 ORM 的内容。

文章模型

让我们先来创建文章模型。

模型通常放在 app 目录中,不过你可以将他们随意放在任何可通过 composer.json 自动加载的地方。所有的 Eloquent 模型都继承自 Illuminate\Database\Eloquent\Model 类。

通过命令行进入项目所在目录,执行如下命令:

php artisan make:model Post -m

命令会生成模型 app/Post.php 和数据库迁移文件 database/migrations/xxxx_xx_xx_xxxxxx_create_posts_table.php

如果你留意,在 database/migrations 下已存在 2014_10_12_000000_create_users_table.php2014_10_12_100000_create_password_resets_table.php,这是 Laravel 框架自带的默认用户功能的一部分,这个自带的数据迁移可以让你快速构建用户和认证信息的存储,如果你用不到用户功能,是可以删除它们的。

关于数据库

我们在项目创建之初就已经建立了数据库 larablog,此时我们定义了模型,我们将通过数据迁移来建立和维护其中的数据表。

文章表

找到我们生成 Post 模型时生成的迁移文件 database/migrations/xxxx_xx_xx_xxxxxx_create_posts_table.php。更改为如下内容:

<?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->string('author', 100);
            $table->text('content');
            $table->string('image');
            $table->text('tags');
            $table->timestamps();
        });
    }

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

在迁移文件中,我们定义了 posts 表的结构,设置了不同字段的类型和长度。

关于数据迁移的更多内容可以查阅文档 数据迁移

接下来,我们要需要通过命令 php artisan migrate 来执行数据迁移,创建我们需要的表。

执行效果如下:

larablog php artisan migrate
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2017_07_10_075949_create_posts_table

其中 userspassword_resets 这 2 个迁移之前提到过是框架自带,我们可以看创建表 posts 的迁移也已经执行。我们可以通过 http://localhost/phpmyadmin/ 去查看我们的数据 larablog 现在的情况。可以看到我们想要的表 posts 已经成功的创建了。

posts 表的结构

默认情况下,Eloquent 会预计你的数据表中有 created_at 和 updated_at 字段。如果你不希望让 Eloquent 来自动维护这两个字段,可在模型内将 $timestamps 属性设置为 false:
详细信息可以查阅 eloquent 时间戳

整合模型和视图

现在我们创建了文章模型,并更新了数据库创建了相应的数据表,我们可以开始将模型集成到视图中。将开始构建我们博客的展示页面。

路由

首先打开 app/Http/routes.php 文件,增加文章相关的路由配置:

Route::resource('posts','PostsController');

可以发现,这和之前定义的 GET, POST 请求路由不同,这是一个资源路由。
这条资源路由声明会创建多个路由,用来处理各式各样和相片资源相关的的 RESTful 行为。同样地,生成的控制器有着各种和这些行为绑定的方法,包含要处理的 URI 及方法对应的注释。

更多关于资源路由的信息可以查阅这里的文档 RESTful-资源控制器

接下来我们生成控制器 PostsController。通过命令行进入项目所在目录,执行命令:

php artisan make:controller PostsController

生成的 PostsController 就是一个资源类型的控制器,可以看到它内部已经帮我们定义好了很多控制器方法。

接下来看看我们定义路由,继续执行如下命令:

php artisan route:list

将会呈现应用的所有路由信息:

➜  larablog php artisan route:list
+--------+----------+--------------------+---------------+-------------------------------------------------+------------+
| Domain | Method   | URI                | Name          | Action                                          | Middleware |
+--------+----------+--------------------+---------------+-------------------------------------------------+------------+
|        | GET|HEAD | /                  | homepage      | App\Http\Controllers\PageController@index       |            |
|        | GET|HEAD | about              | about         | App\Http\Controllers\PageController@about       |            |
|        | GET|HEAD | contact            | contact       | App\Http\Controllers\PageController@contact     |            |
|        | POST     | contact            |               | App\Http\Controllers\PageController@postContact |            |
|        | GET|HEAD | posts              | posts.index   | App\Http\Controllers\PostsController@index      |            |
|        | POST     | posts              | posts.store   | App\Http\Controllers\PostsController@store      |            |
|        | GET|HEAD | posts/create       | posts.create  | App\Http\Controllers\PostsController@create     |            |
|        | GET|HEAD | posts/{posts}      | posts.show    | App\Http\Controllers\PostsController@show       |            |
|        | PUT      | posts/{posts}      | posts.update  | App\Http\Controllers\PostsController@update     |            |
|        | PATCH    | posts/{posts}      |               | App\Http\Controllers\PostsController@update     |            |
|        | DELETE   | posts/{posts}      | posts.destroy | App\Http\Controllers\PostsController@destroy    |            |
|        | GET|HEAD | posts/{posts}/edit | posts.edit    | App\Http\Controllers\PostsController@edit       |            |
+--------+----------+--------------------+---------------+-------------------------------------------------+------------+

现在我们能清晰的看到我们所定义的资源路由 posts 的明细。考虑到我们后续应该会对文章进行操作一些操作,比如;编辑,删除。资源路由就包含了这些操作文章的请求,我们将文章做为一种资源为其定义资源路由很符合我们的需要,如果你在定义路由时,尽可能考虑你是否要需要这么定义路由信息。

控制方法

我们已经建立了控制器 app/Http/Controllers/PostsController.php,接下来我们定位到 show 方法:

public function show($id)
{
    $post = Post::findOrFail($id);

    return view('posts.show', ['post' => $post]);
}

这里,我们通过模型来查找相应 id 的文章,并渲染视图显示内容。如果文章不存在将会抛出异常 NotFoundHttpException

如果是生产环境,这样的异常信息最终都会以 404 页呈现给用户,这需要我们自己定制相关的异常错误处理页面,关于这方面的内容这里就不展开了。

视图显示

现在我们已建立控制方法 show 来处理文章的显示。我们需要创建文件 resources/views/posts/show.blade.php 并添加如下内容:

@extends('layouts.app')

@section('title', $post->title)

@section('body')
    <article class="blog">
        <header>
            <div class="date">{{ $post->created_at->format('l, F j, Y') }}</div>
            <h2>{{ $post->title }}</h2>
        </header>
        <img src="{{ asset('images/'.$post->image) }}" alt="{{ $post->title }} image not found" class="large" />
        <div>
            <p>{{ $post->content }}</p>
        </div>
    </article>
@endsection

我们将文章使用的图片信息存放在了 public/images 下,你可以通过下面的链接获得这些静态图片资源。
图片资源

CSS

同时我们要为博客的内容增加样式信息,打开文件 public/css/blog.css,追加如下内容:

.date { margin-bottom: 20px; border-bottom: 1px solid #ccc; font-size: 24px; color: #666; line-height: 30px }
.blog { margin-bottom: 20px; }
.blog img { width: 190px; float: left; padding: 5px; border: 1px solid #ccc; margin: 0 10px 10px 0; }
.blog .meta { clear: left; margin-bottom: 20px; }
.blog .snippet p.continue { margin-bottom: 0; text-align: right; }
.blog .meta { font-style: italic; font-size: 12px; color: #666; }
.blog .meta p { margin-bottom: 5px; line-height: 1.2em; }
.blog img.large { width: 300px; min-height: 165px; }

填充数据

Laravel 可以简单的使用 seed 类来给数据库填充测试数据。所有的 seed 类都放在 database/seeds 目录下。你可以任意地为 Seed 类命名,但是应该遵守某些大小写规范,可用类似 UserTableSeeder 之类的命名。 Laravel 默认为你定义了一个 DatabaseSeeder 类。你可以在这个类中使用 call 方法来运行其它的 seed 类,以借此控制数据填充的顺序。

接下来我们就创建编写数据填充类。命令行下进入项目所在目录,执行命令:

php artisan make:seeder PostsTableSeeder

执行完命令,文章数据填充类就会生成,文件位于 database/seeds/PostsTableSeeder.php,内容如下:

<?php

use Illuminate\Database\Seeder;

class PostsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        //
    }
}

在 seeder 类里只有一个默认方法:run。当运行 db:seed Artisan 命令 时就会调用此方法。你可以在 run 方法中给数据库添加任何数据。你可使用 查询语句构造器 或 Eloquent 模型工厂 来手动添加数据。

这个 run 方法中我们可以添加如下方法手动插入数据:

/**
 * 运行数据库填充。
 *
 * @return void
 */
public function run()
{
    DB::table('users')->insert([
        'name' => str_random(10),
        'email' => str_random(10).'@gmail.com',
        'password' => bcrypt('secret'),
    ]);
}

这只是个样例写法,很显然我们每次都要一条条的插入测试数据会不方便,当我们需要填充很多测试数据时,我们可以利用填充数据的模型工厂来建立数据模板。

数据模板

打开 database/factories/ModelFactory.php 文件,可以看到默认存在了 User 模型的数据模板,构建用户数据时就可以很方便的生成相应的数据。现在让我们来构建 Post 模型的数据模版,在文件中添加如下内容:

$factory->define(App\Post::class, function (Faker\Generator $faker) {
    return [
        'title' => $faker->sentence(6),
        'content' => $faker->text(600),
        'image' => $faker->randomElement(['beach.jpg', 'misdirection.jpg', 'one_or_zero.jpg', 'pool_leak.jpg', 'the_grid.jpg']),
        'author' => $faker->name,
        'tags' => join(',', $faker->words(rand(3, 6)))
    ];
});

在这里,我们定义了一个 Post 模型的数据模板,接下来我们会在填充数据时根据数据模板来生成特定数量的数据。

数据模板中有很多数据生成的方法这是通过集成的组件完成的.
Faker is a PHP library that generates fake data for you.
你可以通过这里了解它 Faker

更新我们的文件 database/seeds/PostsTableSeeder.php,修改 run 方法的内容:

public function run()
{
    factory(App\Post::class, 50)->create();
}

修改 database/seeds/DatabaseSeeder.php,同样修改 run 方法:

public function run()
{
    Model::unguard();

    $this->call(PostsTableSeeder::class);

    Model::reguard();
}

这里,数据填充器将会调用文章填充类通过文章模型数据模板生成 50 个样板数据到数据库。

命令

最终的数据填充需要执行命令来完成,让我们在命令行下进入项目所在目录执行 php artisan db:seed:

➜  larablog php artisan db:seed
Seeded: PostsTableSeeder

命令执行完成,我们可以通过 http://localhost/phpmyadmin 来查看生成的填充数据。

有了填充数据,我们的博客内容可以显示了。打开浏览器访问 http://localhost:8000/posts/1 即可查看编号为 1 的博客内容(当然你还可以试试别的编号)。

至此,我们完成了博客内容的显示,并通过填充测试数据很轻松的就让显示效果得以展现。

总结

在本章的内容中我们提及了一些 Eloquent 的概念和基本用法,我们也看到了填充数据可以让我们可以快速的为应用添加测试数据。

接下来,我们会试着通过添加评论来延伸更多模型功能,我们会开始建立首页,将继续探讨数据迁移的使用以及如何通过 Eloquent 模型将评论发布到博客中。

推荐阅读更多精彩内容