三、用户模型

三、用户模型

一、开发目标

1、MVC的M

在前面讲的 MVC 模式中,我们一直没讲到其中的 M - Model(模型),本章我们将构建一个基本的用户模型来实现用户数据的存储,并了解 Laravel 如何对模型对象进行增删改查操作。后面我们还会在此用户模型基础上添加用户注册和登录功能,并对用户身份进行权限认证,让管理员用户可以对用户进行删除操作。接着我们还会构建一套用户账号激活和密码找回系统,只有成功进行邮箱激活的用户才能在网站上进行登录,激活成功后的用户如果出现密码丢失的情况,可以使用已认证的邮箱进行密码找回。

2、用户认证系统

事实上,Laravel 已默认为我们集成了一整套用户登录注册功能,并提供了一些非常方便的 API,让我们可以快速构建自己的用户认证系统。如果完全借助 Laravel 提供的用户认证系统,我们能够在短短几分钟便完成整个登录注册功能的开发,包括用户数据库的整个构建过程。

但这不利于新手的学习,整个功能的具体实现代码被 Laravel 隐藏起来,像一个黑盒一样,你很难去理解这里面整个实现和调用过程。因此,本教程将不会完全借助 Laravel 提供的用户认证系统,而是借助 Laravel 提供的一些简便的 API 来独自开发一个完整的用户认证系统,帮助新手更好的学习用户认证系统的运行机制。但在实际项目的开发中,如果对注册登录功能的定制性不高,还是建议你使用 Laravel 默认提供的用户认证系统来操作。

3、Eloquent ORM

在接下来几章要实现的用户注册功能需要用到数据库来进行数据存储,用于放置用户的基本信息。在这期间,还需要用到数据模型- Model,利用 Laravel 提供的 Eloquent ORM 跟数据库进行交互,实现用户数据的增删改查操作。Eloquent 提供了简洁优雅的 ActiveRecord 实现来跟数据库进行交互。Active Record 是一种领域模型模式,该模式由 Martin Fowler 在 2003 年出版的《企业应用架构模式》一书中进行了详细叙述并命名。其特点是一个模型类对应关系型数据库中的一个表,模型类的一个实例对应表中的一行记录。Active Record 最大优点是允许我们简单, 直观地操作数据层。

4,准备好舞台

现在让我们照例先来创建一个新分支。

$ git checkout master
$ git checkout -b modeling-users


二、数据库迁移

1、什么时候数据库迁移?

在 Laravel 中,我们使用 数据库迁移 来管理数据库表结构,迁移就像是数据库中的版本控制,它让团队成员之间能够轻松的修改跟共享应用程序的数据库结构,而不用担心并行更新数据结构而造成冲突等问题。同时也让 Laravel 项目的部署变得很方便。不仅如此,Migration 建表要比直接手动创建表或者 .sql 文件具备额外的管理数据库的功能,如:回滚/重置/更新等。Migration 的建表方法大部分情况下能兼容 MySQL, PostgreSQL, SQLite 甚至是 Oracle 等主流数据库系统。

总结下迁移的好处:

  • 多人并行开发;

  • 代码版本管理;

  • 数据库版本控制 —— 如:回滚/重置/更新等;

  • 兼容多种数据库系统;

  • 部署方便。

2、默认迁移文件

所有创建的迁移文件都被统一放在 database/migrations 文件夹里。打开该文件夹我们可以看到,Laravel 已默认为我们创建好了两个迁移文件:

  • database/migrations/2014_10_12_000000_create_users_table.php

  • database/migrations/2014_10_12_100000_create_password_resets_table.php

从上面迁移文件的命名可以看出,Laravel 在创建迁移文件时会默认在生成的文件名前面加上一个当前时间戳,用于指明该文件的创建时间。加上时间戳来命名迁移文件的好处在于,当我们进行团队协作开发时,如果有多个成员生成了相同名字的迁移文件,也不会出现文件冲突的问题,除非这几个成员都在同一时间生成了相同名称的迁移文件,不过这种概率太低了,基本可以忽略不计。

Laravel 默认创建的两个迁移文件,一个用于构建用户表,一个用于构建密码重置表。密码重置的功能我们在后面章节再进行讲解,本章我们先来看下 Laravel 为我们生成的创建用户迁移文件里面都包含了什么内容。

database/migrations/2014_10_12_000000_create_users_table.php

<?php

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

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

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

可以看到在该文件里面定义了一个 CreateUsersTable 类,并继承自 Migration 基类。CreateUsersTable 有两个方法 up 和 down :

  • 当我们运行迁移时,up 方法会被调用;

  • 当我们回滚迁移时,down 方法会被调用。

3、创建数据库表

在 up 方法里面,我们通过调用 Schema 类的 create 方法来创建 users 表

Schema::create('users', function (Blueprint $table) {
    ...
});

create 方法会接收两个参数:一个是数据表的名称,另一个则是接收 $table(Blueprint 实例)的闭包。

4、定义数据表字段

CreateUsersTable 类中通过 Blueprint 的实例 $table 为 users 表创建所需的数据库字段。接下来让我们来详细讲解 Blueprint 实例 $table 的基本用法:

$table->increments('id');

由 increments 方法创建了一个 integer 类型的自增长 id。

$table->string('name');

由 string 方法创建了一个 name 字段,用于保存用户名称。

$table->string('email')->unique();

由 string 方法创建了一个 email 字段,且在最后指定该字段的值为唯一值,用于保存用户邮箱。

$table->timestamp('email_verified_at')->nullable();

Email 验证时间,空的话意味着用户还未验证邮箱。

$table->string('password', 60);

由 string 方法创建了一个 password 字段,且在 string 方法中指定保存的值最大长度为 60,用于保存用户密码。

$table->rememberToken();

由 rememberToken 方法为用户创建一个 remember_token 字段,用于保存『记住我』的相关信息。

$table->timestamps();

由 timestamps 方法创建了一个 created_at 和一个 updated_at 字段,分别用于保存用户的创建时间和更新时间。

最终该迁移文件生成的用户表字段信息如下所示:

字段名称字段类型
idinteger
namestring
emailstring
email_verified_atdatetime
passwordstring
remember_tokenstring
created_atdatetime
updated_atdatetime

若要了解更多 $table 的可用方法,可查阅 官方文档

4、回滚迁移

down 方法会在回滚命令发起时被调用,是 up 方法的逆向操作。在上面的代码中,up 创建了 users 表,那么这里将会通过调用 Schema 的 drop 方法来删除 users 表。

Schema::dropIfExists('users');

三、查看数据库表

1、数据库工具选择


开发时,我们使用的是 Homestead 虚拟机里的 MySQL 数据库。为了方便调试和查看数据,我们需要在主机中安装 MySQL 数据库查看工具,然后连接到 虚拟机里的 MySQL 数据库服务器。

Homestead 虚拟机里的 MySQL 数据库服务器连接方式为:

  • Host: 127.0.0.1

  • Port: 33060

  • User: homestead

  • Pass: secret

注意此处使用了 VirtualBox 虚拟机的『端口转发』功能,Homestead 脚本默认将本机端口 33060 转发到虚拟机里的 3306 端口。所以,只要我们连接本机的 33060 端口,即可读取虚拟机中的 MySQL 数据库。

接下来详细讲下 Mac 和 Win 平台下的 MySQL 数据库查看工具的安装。

2、Windows

数据库管理工具:我使用Navicat 。下载地址:https://pan.baidu.com/s/1o7Byyee
image.png

3、数据库迁移

我们需要运行下面的命令来生成执行迁移,migrate 命令会执行所有未被执行过的迁移。

$ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table

我们可以看到有两个迁移文件的迁移被成功执行。这代表我们已成功将两个表(用户表和重置密码表)添加到数据库中。

注:我们一开始在创建 weibo 项目时,已经为其创建了数据库,并在 .env 文件中做了配置,所以这里会直接使用这些数据库设置。

接下来查看下生成的数据库表:

image.png

migrations 表主要两个字段:

  • migration 是迁移文件的名称;

  • batch 是用来记录迁移对应的批次,以此来实现 回滚迁移 的功能。

4、数据库回滚

在日常开发中,我们有时候也需要通过下面的方式来回滚到最近一次执行的迁移。回滚执行成功后,两个表都将被删除。

$ php artisan migrate:rollback

回到数据库管理工具的视图,刷新以后会发现只剩下 migrations 表:

image.png

如果你尝试了回滚迁移的操作,请再次运行迁移,以方便后面章节的顺利进行。

$ php artisan migrate

四、模型文件

1、用户模型

Laravel 默认为我们生成了用户模型文件,代码如下所示:

app/User.php

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

可以看到 Laravel 默认为我们生成的用户模型中包含了不少代码,其中 Notifiable 是消息通知相关功能引用,Authenticatable 是授权相关功能的引用。接下来我们主要将精力放在用户模型中定义的三个属性 tablefillablehidden 上。我们在前面提到过,Eloquent 模型可以让我们很方便的与数据库进行交互,因此我们需要在 Eloquent 模型中借助对 table 属性的定义,来指明要进行数据库交互的数据库表名称,在用户模型中,我们对应要交互的数据库表为 users,因此定义的代码如下:

protected $table = 'users';

在使用 Laravel 进行项目开发时,我们需要考虑到,当一些不怀好意的用户将类似 is_admin 这样的字段也嵌入到表单中进行提交时,会有怎样的后果?其后果是用户能够将自己指定为管理员,并进行一些只有管理员才能执行的操作,如删除用户,删除帖子等,这即是我们常说的『批量赋值』的错误,后面我将在后面章节『进行微博模型构建』时,为你演示『批量赋值』的报错。为了提高应用的安全性,Laravel 在用户模型中默认为我们添加了 fillable 在过滤用户提交的字段,只有包含在该属性中的字段才能够被正常更新:

protected $fillable = [
        'name', 'email', 'password',
    ];

最后,当我们需要对用户密码或其它敏感信息在用户实例通过数组或 JSON 显示时进行隐藏,则可使用 hidden 属性

protected $hidden = [
        'password', 'remember_token',
    ];

2、使用 App\Models 命名空间

Laravel 为我们默认创建的模型文件放置在 app 文件夹下,为了让新手能够更好理解 MVC 模式的开发流程,本教程中将统一使用 app/Models 文件夹来放置所有的模型文件。现在让我们先来创建一个 app/Models 文件夹,并将 User.php 文件放置到其中。

$ mkdir app/Models
$ mv app/User.php app/Models/User.php

在执行完这一步的操作之后,我们还需要执行下面这两个操作:

(1)、修改 User.php 文件,更改 namespace 为我们新创建的文件夹路径:

app/Models/User.php

<?php
namespace App\Models;
.
.
.

(2)、编辑器全局搜索 App\User 替换为 App\Models\User搜索下总共有几处需要修改:

image.png

3、Article 模型的例子


模型文件可通过多种方式进行创建,下面让我们以文章模型(Article)为例,为你讲解模型文件的创建。

一般情况下,如果我们要自己手动创建一个模型文件,最简单的方式是通过 make:model 来创建。需要注意的一点是,模型类名称使用 单数 形式来命名:

$ php artisan make:model Article

4、指定命名空间

这种方式创建的模型默认是放置在 app 文件夹下,本教程我们因遵循 MVC 的最佳实践而需要把模型文件放置于 app/Models 目录下。首先我们移除刚刚错误创建的文件,然后再为创建模型命令指定命名空间:

$ rm app/Article.php
$ php artisan make:model Models/Article

5、同时创建迁移文件

如果需要在创建模型的同时顺便创建数据库迁移,则可以使用 --migration 或 -m 选项,让我们将刚刚生成的模型进行删除,尝试生成迁移文件:

$ rm app/Models/Article.php
$ php artisan make:model Models/Article -m
Model created successfully.
Created Migration: 2018_12_12_090551_create_articles_table

可看到模型文件和迁移文件都一并生成了。

6、Eloquent 数据模型

正常情况下,一个最小代码的 Eloquent 模型如下所示:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    //
}

7、Eloquent 表命名约定

在该文件中,Eloquent Article 模型默认情况下会使用类的「下划线命名法」与「复数形式名称」来作为数据表的名称生成规则。如:

  • Article 数据模型类对应 articles 表;

  • User 数据模型类对应 users 表;

  • BlogPost 数据模型类对应 blog_posts 表;

因此 Eloquent 将会假设 Article 模型被存储记录在 articles 数据表中。如果你需要指定自己的数据表,则可以通过 table 属性来定义,如:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    protected $table = 'my_articles';
}

8、『约定优于配置』解释

『约定优于配置』(convention over configuration),也称作按约定编程,这是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。如果所用工具的约定与你的期待相符,便可省去配置;反之,你可以配置来达到你所期待的方式。

Eloquent 数据表命名约定机制即属于『约定优于配置』,数据模型类 Article 按照系统约定对应于 articles 数据表,如果我们因为特殊原因需要使用其他表名称,只需要通过配置 $table 即可达到预期。

『约定优于配置』能极大提高开发效率,并且也更有利于团队协作。Laravel 项目中大量的使用了『约定优于配置』这种设计范式,这也是 Laravel 的另一个可爱之处。

9、收拾舞台

由于我们刚刚创建的文章模型仅作演示用,因此我们无需对这几个文件的更改进行保存,可通过下面命令进行移除:

$ git add -A
$ git checkout -f

五、创建用户对象

1、创建用户对象

在了解 Eloquent 模型的基本原理之后,接下来让我们尝试使用 Eloquent 模型来创建一个用户对象,并将该用户对象存储到数据库。虽然我们现在还没有用户注册表单,但是通过 Laravel 提供的 Tinker 环境可以让我们完成对用户对象创建。Tinker 是一个 REPL (read-eval-print-loop),REPL 指的是一个简单的、可交互式的编程环境,通过执行用户输入的命令,并将执行结果直接打印到命令行界面上来完成整个操作。REPL 对于学习一门新的编程语言具有很大的帮助,因为它能立刻对初学者做出的动作进行响应。接下来我们将使用 Tinker 来操作用户对象。

首先让我们使用此命令进入 Tinker 环境:

$ php artisan tinker

如果中途想要退出 Tinker,可使用 ctrl + c 快捷键。

通过下面命令我们可以很轻松的创建一个用户对象:

>>> App\Models\User::create(['name'=> 'Summer', 'email'=>'summer@example.com','password'=>bcrypt('password')])

在以上命令中,我们使用 App\Models\User Eloquent 模型提供的 create 方法,通过传入一个关联数组来新建一个用户对象。在我们对用户的 password 进行赋值时,调用了一个叫 bcrypt 的方法,将 password 的值进行加密。这是因为所有保存到数据库的用户密码在经过加密保存后安全性更高,这样当我们的数据库不幸被黑客脱库时,泄露出去的用户密码也都是有经过加密处理的。在读取用户密码的时候,Laravel 会先对密码进行解密再返回,这块的具体操作逻辑我们不必太担心,因为 Laravel 已为我们做好了一切防范措施。

注意:这里我们又多了一种命令行提示符 - >>> ,在接下来的章节里,请记住 >>> 代表着 artisan tinker命令行环境。

image.png

六、查找用户对象
在前面的章节中我们学过,当使用了 use 对类进行引用之后,便可以对类进行直接调用。为了让接下来的命令可读性更高,我们使用 use 来引用 App\Models\User Eloquent 模型类:

>>> use App\Models\User

这样我们就可以在后面的操作中直接调用 User 而不是完整的类名 App\Models\User

注意:上面的 use 引用命令只会对当前会话有效,如果中途退出了 Tinker 的话,需要重新再执行一次上面的命令,才能继续使用 User 类名缩写。

使用 Eloquent 模型,能让我们更加轻松的与数据库进行交互。当我们要查找一个 id 为 1 的用户时,可以使用下面这种方法:

>>> User::find(1)

当你传给 find 方法的 id 不存在时,Tinker 将会返回 null

>>> User::find(5)
=> null

如果你想在查询用户不存在时触发报错的话,可使用 findOrFail:

>>> User::findOrFail(5)
Illuminate\Database\Eloquent\ModelNotFoundException with message 'No query results for model [App\Models\User] 5'

如果要查找用户表中的首个用户,可以使用 first 方法:

>>> User::first()

我们还可以用 all 方法取出所有的用户数据:

>>> User::all()

七、更新用户对象

1、更新用户对象


在用户创建成功之后,我们还可以对用户信息进行更新。有两种方式可以完成用户的更新操作:

  • 第一种是通过给用户对象属性进行赋值,赋值成功后再调用 save 方法进行保存更新;

  • 第二种则是直接调用 update 方法进行更新。

一般较为常用的是第二种更新方式。

让我们以刚刚新建的用户对象为例,对用户名进行更新。

首先,让我们将用户对象从数据库中取出,并赋值给 user 变量。

>>> $user = User::first()

2、通过save方法更新
让我们使用属性赋值更新的方法对用户名进行更新,将其名字更改为 Monkey

>>> $user->name = 'Monkey'

这时,新的用户名已赋值成功,但是 尚未 保存到数据库,因此查看用户信息时我们能够看到,用户名显示的还是之前的 Summer。

>>> User::first()=> App\Models\User {#2906
     id: 1,
     name: "Summer",
     email: "summer@example.com",
     email_verified_at: null,
     created_at: "2018-12-12 09:09:53",
     updated_at: "2018-12-12 09:09:53",
   }

让我们来调用 save 方法对用户信息进行保存。

>>> $user->save()

这时再次对用户信息进行查询,我们能看到用户名已成功更改。

>>> User::first()=> App\Models\User {#2909
     id: 1,
     name: "Monkey",
     email: "summer@example.com",
     email_verified_at: null,
     created_at: "2018-12-12 09:09:53",
     updated_at: "2018-12-12 09:21:27",
   }

3、通过update 方法更新

下面让我们再来使用 update 方法将用户名更改回 Summer:

>>> $user->update(['name'=>'Summer'])

再次查询用户信息,可看到改动成功。

>>> User::first()=> App\Models\User {#2903
     id: 1,
     name: "Summer",
     email: "summer@example.com",
     email_verified_at: null,
     created_at: "2018-12-12 09:09:53",
     updated_at: "2018-12-12 09:21:53",
   }

至此,用户对象已成功存入数据库。Eloquent 除 createupdate 之外还提供了很多其它的方法来方便我们跟数据库进行交互,后面的教程中会讲到更多关于 Eloquent 模型的基本使用,你也可以通过查阅 相关文档 来进行深入学习。

八、小结

现在让我们将代码切回到主分支中进行合并。

$ git checkout master
$ git merge modeling-users

接着我们再将代码推送到 GitHub 和 Heroku 上。

$ git push
$ git push heroku master

经过本章节的学习,我们学到了以下内容:

  • Eloquent 模型的定义与应用;

  • 数据库迁移的创建、数据表生成、数据表回滚;

  • 模型的基本使用;

  • Tinker 的基本使用;


回复列表



回复操作

正在加载验证码......

请先拖动验证码到相应位置