你也可以通过在模型上调用提供 morphTo 的方法来获取多态模型其关系所有者。在上面的例子中,指的就是 Like 模型中的 likeable 方法。所以,我们可以像使用动态属性一样使用方法来进行访问:
$like = App\Like::find(1); $likeable = $like->likeable;
Like 模型的 likeable 关联将会返回一个 Post 或者 Comment 实例,这取决于其所属者的类型。
自定义多态类型
默认的,Laravel 会使用包完全限定类名来存储所关联模型的类型。比如,上面的例子中 Like 可以属于 Post 或者 Comment。默认的 likeable_type 应该是 App\Post 或者 App\Comment。事实上,你可能希望从你的应用程序的内部结构分离数据库。在这个例子中,你可以定义一个关联的多态映射来指导 Eloquent 使用模型关联的表名称来替代类名:
use Illuminate\Database\Eloquent\Relations\Relation; Relation::morphMap([ App\Post::class, App\Comment::class, ]);
或者,你可以指定一个自定的字符串与每个模型进行关联:
use Illuminate\Database\Eloquent\Relations\Relation; Relation::morphMap([ 'posts' => App\Post::class, 'likes' => App\Like::class, ]);
你可以在你的 AppServiceProvider 或者一个分离的服务提供者的 boot 方法中注册你的 morphMap。
多态多对多关联
表结构
除了传统的多态关联,你也可以定义多对多的多态关联。比如,一个博客的 Post 和 Video 模型应该可以共享一个多态关联的 Tag 模型。使用多对多的多态关联可以允许你的博客文章和视频能够共享独特标签的单个列表。首先,让我们来看一下表结构:
posts id - integer name - string videos id - integer name - string tags id - integer name - string taggables tag_id - integer taggable_id - integer taggable_type - string
模型结构
接着,我们来定义模型中的关联。Post 和 Video 模型将都会包含调用基础 Eloquent 类的 morphToMany 方法的 tags 方法:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { /** * Get all of the tags for the post. */ public function tags() { return $this->morphToMany('App\Tag', 'taggable'); } }
定义相对的关联
接着,在 Tag 模型中,你应该为所有关联模型定义相应的方法。所以,在这个例子中,我们将定义 posts 方法和 videos 方法:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Tag extends Model { /** * Get all of the posts that are assigned this tag. */ public function posts() { return $this->morphedByMany('App\Post', 'taggable'); } /** * Get all of the videos that are assigned this tag. */ public function videos() { return $this->morphedByMany('App\Video', 'taggable'); } }
获取关联
当定义完成数据表和模型之后,你就可以通过模型来访问其关联。比如,你可以简单的使用 tags 动态属性来访问文章的所有标签模型:
$post = App\Post::find(1); foreach ($post->tags as $tag) { // }
你也可以通过访问模型中提供执行 morphedByMany 方法的方法来获取关联模型的所属模型。在上面的例子中,就是 Tag 模型上的 posts 或者 videos 方法。所以,你可以像动态属性一样访问这些方法:
$tab = App\Tag::find(1); foreach ($tag->videos as $video) { // }
关联查询
由于所有的 Eloquent 关联类型都是通过方法定义的,所以你可以调用这些方法来获取所关联的模型的实例而无需实际的执行关联查询。另外,所有的 Eloquent 关联也都提供了查询生成器服务,这允许你可以继续的链式执行查询操作。
比如,想象一下博客系统中 User 模型拥有很多 Post 关联的模型:
<?ph namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * Get all of the posts for the user. */ public function posts() { return $this->hasMany('App\Post'); } }
你可以查询 posts 关联的同时添加一些额外的查询约束:
$user = App\User::find(1); $user->posts()->where('active', 1)->get();
你应该注意到了,你可以在关联中使用任何的查询生成器的方法。
关联方法 Vs. 动态属性
如果你不需要在进行 Eloquent 关联查询时添加额外的约束,你可以简单的像它的属性一样进行访问。比如,我们继续使用 User 和 Post 示例模型。我们可以像这样来访问用户的所有文章:
$user = App\User::find(1); foreach ($user->posts as $post) { // }