2

本节将使用用户创建博文来学习数据模型关联的有关知识。

一、Laravel常用知识总结

1.提示信息汉化

如果用Laravel原生的表单提示错误信息,则是英文的,如果需要中文,则需要修改resources/lang/en的英文,这样比较麻烦,不过,我们可以使用github开源的汉化包,然后引入该文件夹,在config/app.php配置文件里边修改语言包引入即可使用。

2.Carbon日期友好化处理

<span class="timestamp">
    {{ $status->created_at->diffForHumans() }}
  </span>

该方法的作用是将日期进行友好化处理,我们可以使用 tinker 来查看该方法的具体输出情况。

$ php artisan tinker

在 tinker 中输出第一位用户的创建时间如下。

>>> $created_at = App\Models\User::first()->created_at
=> Carbon\Carbon {#704
     +"date": "1998-12-06 03:15:31.000000",
     +"timezone_type": 3,
     +"timezone": "UTC",
   }

在 tinker 中调用 diffForHumans 方法来输出,结果如下。

>>> $created_at->diffForHumans()
=> "17 years ago"

我们发现 diffForHumans 为我们生成的时间是英文的,如果要使用中文时间,则需要对 Carbon 进行本地化设置。Carbon 是 PHP DateTime 的一个简单扩展,Laravel 将其默认集成到了框架中。

三、Eloquent:关联

数据表之间经常会互相进行关联。例如,一篇博客文章可能会有多条评论,或是一张订单可能对应一个下单客户。Eloquent 让管理和处理这些关联变得很容易,同时也支持多种类型的关联。

定义关联

你可在 Eloquent 模型类内将 Eloquent 关联定义为函数。因为关联像 Eloquent 模型一样也可以作为强大的 查询语句构造器(数据库:查询构造器),定义关联为函数提供了强而有力的链式调用及查找功能。例如:

$user->posts()->where('active', 1)->get();

不过,在深入了解使用关联之前,先让我们来学习如何定义每个类型:

1.一对一

一对一关联是很基本的关联。例如一个 User 模型也许会对应一个 Phone。要定义这种关联,我们必须将 phone 方法放置于 User 模型上。phone 方法应该要返回基类 Eloquent 上的 hasOne 方法的结果:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 获取与指定用户互相关联的电话纪录。
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

传到 hasOne 方法里的第一个参数是关联模型的类名称。定义好关联之后,我们就可以使用 Eloquent 的动态属性来获取关联纪录。动态属性让你能够访问关联函数,就像他们是在模型中定义的属性:

$phone = User::find(1)->phone;

Eloquent 会假设对应关联的外键名称是基于模型名称的。在这个例子里,它会自动假设 Phone 模型拥有 user_id 外键。如果你想要重写这个约定,则可以传入第二个参数到 hasOne 方法里。

return $this->hasOne('App\Phone', 'foreign_key');

此外,Eloquent 的默认外键在上层模型的 id 字段会有个对应值。换句话说,Eloquent 会寻找用户的 id 字段与 Phone 模型的 user_id 字段的值相同的纪录。如果你想让关联使用 id 以外的值,则可以传递第三个参数至 hasOne 方法来指定你自定义的键:

return $this->hasOne('App\Phone', 'foreign_key', 'local_key');

定义相对的关联

所以,我们可以从 User 访问到 Phone 模型。现在,让我们在 Phone 模型上定义一个关联,此关联能够让我们访问拥有此电话的 User。我们可以定义与 hasOne 关联相对应的 belongsTo 方法:


<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Phone extends Model
{
    /**
     * 获取拥有此电话的用户。
     */
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}

2. 一对多

一个「一对多」关联使用于定义单个模型拥有任意数量的其它关联模型。例如,一篇博客文章可能会有无限多个评论。就像其它的 Eloquent 关联一样,可以通过放置一个函数到 Eloquent 模型上来定义一对多关联:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * 获取博客文章的评论。
     */
    public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}

切记,Eloquent 会自动判断 Comment 模型上正确的外键字段。按约定来说,Eloquent 会取用自身模型的「蛇形命名」后的名称,并在后方加上 _id。所以,以此例来说,Eloquent 会假设 Comment 模型的外键是 post_id

一旦关联被定义,则可以通过 comments 属性来访问评论的集合。切记,因为 Eloquent 提供了「动态属性」,因此我们可以对关联函数进行访问,就像他们是在模型中定义的属性一样:

$comments = App\Post::find(1)->comments;

foreach ($comments as $comment) {
    //
}

定义相对的关联

现在我们已经能访问到所有文章的评论,让我们来接着定义一个通过评论访问上层文章的关联。若要定义相对于 hasMany 的关联,可在下层模型定义一个叫做 belongsTo 方法的关联函数:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    /**
     * 获取拥有此评论的文章。
     */
    public function post()
    {
        return $this->belongsTo('App\Post');
    }
}

3. 多对多

多对多关联要稍微比 hasOnehasMany 关联复杂。如一个用户可能拥有多种身份,而一种身份能同时被多个用户拥有。举例来说,很多用户都拥有「管理者」的身份。要定义这种关联,需要使用三个数据表:usersrolesrole_userrole_user 表命名是以相关联的两个模型数据表来依照字母顺序命名,并包含了 user_id 和 role_id 字段

多对多关联通过编写一个在自身 Eloquent 类调用的 belongsToMany 的方法来定义。举个例子,让我们在 User 模型定义 roles 方法:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 属于该用户的身份。
     */
    public function roles()
    {
        return $this->belongsToMany('App\Role');
    }
}

定义相对的关联

要定义相对于多对多的关联,只需简单的放置另一个名为 belongsToMany 的方法到你关联的模型上。让我们接着以用户身份为例,在 Role 模型中定义 users 方法:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Role extends Model
{
    /**
     * 属于该身份的用户。
     */
    public function users()
    {
        return $this->belongsToMany('App\User');
    }
}

如你所见,此定义除了简单的参考 AppUser 模型外,与 User 的对应完全相同。因为我们重复使用了 belongsToMany 方法,当定义相对于多对多的关联时,所有常用的自定义数据表与键的选项都是可用的。

关联总结

1.一对一

$this->hasOne('App\Phone');
$this->belongsTo('App\User');

hasOne 关联相对应的 belongsTo 方法

2.一对多

$this->hasMany('App\Comment');
$this->belongsTo('App\Post');

hasMany 关联相对应的 belongsTo 方法

3.多对多

$this->belongsToMany('App\Role');
$this->belongsToMany('App\User');

belongsToMany 关联相对应的 belongsToMany 方法

4.查找关联

你可以查找 posts 关联并增加额外的条件至关联,像这样:

$user = App\User::find(1);
$user->posts()->where('active', 1)->get();

关联方法与动态属性

如果你不需要增加额外的条件至 Eloquent 的关联查找,则可以简单的像访问属性一样来访问关联。例如我们刚刚的 User 及 Post 模型示例,我们可以像这样来访问所有用户的文章:

$user = App\User::find(1);

foreach ($user->posts as $post) {
    //
}

预加载

当通过属性访问 Eloquent 关联时,该关联数据会被「延迟加载」。意味着该关联数据只有在你使用属性访问它时才会被加载。不过,Eloquent 可以在你查找上层模型时「预加载」关联数据。预加载避免了 N + 1 查找的问题。要说明 N + 1 查找的问题,可试想一个关联到 Author 的 Book 模型,如下所示:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
    /**
     * 获取编写该书的作者。
     */
    public function author()
    {
        return $this->belongsTo('App\Author');
    }
}

现在,让我们来获取所有书籍及其作者的数据:

$books = App\Book::all();

foreach ($books as $book) {
    echo $book->author->name;
}

上方的循环会运行一次查找并取回所有数据表上的书籍,接着每本书会运行一次查找作者的操作。因此,若存在着 25 本书,则循环就会执行 26 次查找:1 次是查找所有书籍,其它 25 次则是在查找每本书的作者。

很幸运地,我们可以使用预加载来将查找的操作减少至 2 次。可在查找时使用 with 方法来指定想要预加载的关联数据:

$books = App\Book::with('author')->get();

foreach ($books as $book) {
    echo $book->author->name;
}

对于该操作则只会运行两次查找:

select * from books

select * from authors where id in (1, 2, 3, 4, 5, ...)

Corwien
6.3k 声望1.6k 粉丝

为者常成,行者常至。


下一篇 »
Java学习