laravel学习教程之关联模型
《:laravel学习教程之关联模型》要点: Eloquent: 关联模型 简介 数据库中的表经常性的关联其它的表.比如,一个博客文章可以有很多的评论,或者一个订单会关联一个用户.Eloquent 使管理和协作这些关系变的非常的容易,并且支持多种不同类型的关联:PHP实战 ??? 一对一 ??? 一对多 ??? 多对多 ??? 远程一对多 ??? 多态关联 ??? 多态多对多关联 定义关联 Eloquent 关联可以像定义方法一样在 Eloquent 模型类中进行定义.同时,它就像 Eloquent 模型自身一样也提供了强大的查询生成器.这允许关联模型可以链式的执行查询能力.比如:PHP实战 $user->posts()->where('active',1)->get(); 但是,在更深入的使用关联之前,让我们先来学习一下如何定义各种类型的关联.PHP实战 一对一 一对一的关联是最基础的关联.比如,一个 <?php namespace App; use IlluminateDatabaseEloquentModel; class User extends Model { /** * Get the phone record associated with the user. */ public function phone() { return $this->hasOne('AppPhone'); } } 传递到 $phone = User::find(1)->phone; Eloquent 假定所关联的外键是基于模型的名称的.在这个前提下, return $this->hasOne('AppPhone','foreign_key'); 另外,Eloquent 也会假定外键应该在其上层模型上拥有一个匹配的 return $this->hasOne('AppPhone','foreign_key','local_key'); 定义相对的关联PHP实战 那么,我们可以从我们的 <?php namespace App; use IlluminateDatabaseEloquentModel; class Phone extends Model { /** * Get the user that owns the phone. */ public function user() { return $this->belongsTo('AppUser'); } } 在上面的例子中,Eloquent 将会尝试从 /** * Get the user that owns the phone. */ public function user() { return $this->belongsTo('AppUser','foreign_key'); } 如果你的上级模型并没有使用 /** * Get the user that owns the phone. */ public function user() { return $this->belongsTo('AppUser','other_key'); } 一对多 一个一对多的关联常常用来定义一个模型拥有其他任意数目的模型.比如,一个博客文章可以拥有很多条评论.就像其他的 Eloquent 关联一样,一对多关联在 Eloquent 模型中通过方法来进行定义:PHP实战 <?php namespace App; use IlluminateDatabaseEloquentModel; class Post extends Model { /** * Get the comments for the blog post. */ public function comments() { return $this->hasMany('AppComment'); } } 记住,Eloquent 会自动的根据 一旦关联定义完成之后,我们可以通过 $comments = AppPost::find(1)->comments; foreach ($comments as $comment) { // } 当然,由于所有的关联都提供了查询生成器的功能,所以你可以在调用 $comments = AppPost::find(1)->comments()->where('title','foo')->first(); 就像 return $this->hasMany('AppComment','foreign_key'); return $this->hasMany('AppComment','local_key'); 定义相对的关联PHP实战 现在我们可以访问文章中所有的评论了,让我们为评论定义一个关联使其可以访问它的上层文章模型.为了定义一个 <?php namespace App; use IlluminateDatabaseEloquentModel; class Comment extends Model { /** * Get the post that owns the comment. */ public function post() { return $this->belongsTo('AppPost'); } } 一旦关联被定义完成,我们就可以通过 $comment = AppComment::find(1); echo $comment->post->title; 在上面的例子中, /** * Get the post that owns the comment. */ public function post() { return $this->belongsTo('AppPost','foreign_key'); } 如果上层模型并没有使用 id 作为主键,或者你想在下层模型中关联其他的列,你可以传递第三个参数到 belongsTo 方法中:PHP实战 /** * Get the post that owns the comment. */ public function post() { return $this->belongsTo('AppPost','other_key'); } 多对多 多对多的关联比 多对多关联需要编写一个方法调用基础 <?php namespace App; use IlluminateDatabaseEloquentModel; class User extends Model { /** * The roles that belong to the user. */ public function roles() { return $this->belongsToMany('AppRole'); } } 一旦关联被定义,你可以通过 $user = AppUser::find(1); foreach ($user->roles as $role) { // } 当然,就像其他类型的关联,你可以调用 $roles = AppUser::find(1)->roles()->orderBy('name')->get(); 就如先前所提到的,Eloquent 会合并两个关联模型并依照字母顺序进行命名.当然你也可以随意的重写这个约定,你可以传递第二个参数到 return $this->belongsToMany('AppRole','role_user'); 除了自定义合并数据表的名称之外,你也可以通过往 return $this->belongsToMany('AppRole','role_user','user_id','role_id'); 定义相对关联PHP实战 你只需要在相对应的关联模型里放置其他的方法来调用 <?php namespace App; use IlluminateDatabaseEloquentModel; class Role extends Model { /** * The users that belongs to the role. */ public function users() { return $this->belongsToMany('AppUser'); } } 就如你所看到的,这个关联的定义与用户的关联定义完全相同.因为我们重复的使用了 检索中间表字段PHP实战 正如你已经了解到的.定义多对多的关联需要引入一个中间表.Eloquent 提供了几种非常有帮助的方式来与这个表进行交互.比如,让我们假定我们的 $user = AppUser::find(1); foreach ($user->roles as $role) { echo $role->pivot->created_at; } 注意我们取出的每个 默认的,只有模型的键会被 return $this->belongsToMany('AppRole')->withPivot('column1','column2'); 如果你想要中间表自动维护 return $this->belongsToMany('AppRole')->withTimestamps(); 通过中间表字段过滤关系PHP实战 你可以通过在定义关联时使用 return $this->belongsToMany('AppRole')->wherePivot('approved',1); return $this->belongsToMany('AppRole')->wherePivotIn('approved',[1,2]); 远程一对多 远程一对多关联提供了简短便捷的方法通过中间关联件来访问远端的关联.比如,一个 countries id - integer name - string users id - integer country_id - integer name - string posts id - integer user_id - integer title - string 远端的 现在我们已经明确了关联表的结构,那么让我们来在 Country 模型上定义关联:PHP实战 <?php namespace App; use IlluminateDatabaseEloquentModel; class Country extends Model { /** * Get all of the posts for the country. */ public function posts() { return $this->hasManyThrough('AppPost','AppUser'); } } 传递到 当使用关联查询时,通常 Eloquent 会遵循外键约定.如果你希望对关联的键进行自定义,你可以传递第三和第四个参数到 class Country extends Model { public function posts() { return $this->hasManyThrough( 'AppPost','AppUser','country_id','id' ); } } 多态关联PHP实战 表结构PHP实战 多态关联允许一个模型在单个关联中从属一个或多个其它模型.比如,想象一下应用中的用户可以喜欢文章及其评论.如果使用多态关联,那么你就可以使用一个单独的 posts id - integer title - string body - text comments id - integer post_id - integer body - text likes id - integer likeable_id - integer likeable_type - string 你需要注意到的两个在 模型结构PHP实战 接着,让我们检查一下这个关联所需要的模型定义:PHP实战 <?php namespace App; use IlluminateDatabaseEloquentModel; class like extends Model { /** * Get all of the owning likeable models. */ public function likeable() { return $this->morphTo(); } } class Post extends Model { /** * Get all of the post's likes. */ public function likes() { return $this->morphMany('AppLike','likeable'); } } class Comment extends Model { /** * Get all of the comment's likes. */ public function likes() { return $this->morphMany('AppLike','likeable'); } } 获取多态关联PHP实战 一旦数据库表和模型都定义完成,你就可以在你的模型中访问这些关联.比如,你可以使用 $post = AppPost::find(1); foreach ($post->likes as $like) { // } 你也可以通过在模型上调用提供 $like = AppLike::find(1); $likeable = $like->likeable;
自定义多态类型PHP实战 默认的,Laravel 会使用包完全限定类名来存储所关联模型的类型.比如,上面的例子中 use IlluminateDatabaseEloquentRelationsRelation; Relation::morphMap([ AppPost::class,AppComment::class,]); 或者,你可以指定一个自定的字符串与每个模型进行关联:PHP实战 use IlluminateDatabaseEloquentRelationsRelation; Relation::morphMap([ 'posts' => AppPost::class,'likes' => AppLike::class,]); 你可以在你的 多态多对多关联 表结构PHP实战 除了传统的多态关联,你也可以定义多对多的多态关联.比如,一个博客的 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 模型结构PHP实战 接着,我们来定义模型中的关联. <?php namespace App; use IlluminateDatabaseEloquentModel; class Post extends Model { /** * Get all of the tags for the post. */ public function tags() { return $this->morphToMany('AppTag','taggable'); } } 定义相对的关联PHP实战 接着,在 <?php namespace App; use IlluminateDatabaseEloquentModel; class Tag extends Model { /** * Get all of the posts that are assigned this tag. */ public function posts() { return $this->morphedByMany('AppPost','taggable'); } /** * Get all of the videos that are assigned this tag. */ public function videos() { return $this->morphedByMany('AppVideo','taggable'); } } 获取关联PHP实战 当定义完成数据表和模型之后,你就可以通过模型来访问其关联.比如,你可以简单的使用 $post = AppPost::find(1); foreach ($post->tags as $tag) { // } 你也可以通过访问模型中提供执行 $tab = AppTag::find(1); foreach ($tag->videos as $video) { // } 关联查询 由于所有的 Eloquent 关联类型都是通过方法定义的,所以你可以调用这些方法来获取所关联的模型的实例而无需实际的执行关联查询.另外,所有的 Eloquent 关联也都提供了查询生成器服务,这允许你可以继续的链式执行查询操作.PHP实战 比如,想象一下博客系统中 <?ph namespace App; use IlluminateDatabaseEloquentModel; class User extends Model { /** * Get all of the posts for the user. */ public function posts() { return $this->hasMany('AppPost'); } } 你可以查询 $user = AppUser::find(1); $user->posts()->where('active',1)->get(); 你应该注意到了,你可以在关联中使用任何的查询生成器的方法.PHP实战 关联方法 Vs. 动态属性PHP实战 如果你不需要在进行 Eloquent 关联查询时添加额外的约束,你可以简单的像它的属性一样进行访问.比如,我们继续使用 $user = AppUser::find(1); foreach ($user->posts as $post) { // } 动态属性是惰性加载的,这意味着在你实际访问他们之前,其关联数据是不会加载的.正因为如此,开发的时候通常使用预加载来进行加载一些即将用到的关联模型.预加载要求必须加载一个模型的关系,这有效的减少了查询的次数.PHP实战 查询关联是否存在PHP实战 当访问一个模型的记录时,你可能会希望基于关联的记录是否存在来对结果进行限制.比如,想象一下你希望获取最少有一条评论的博客文章.你可以传递关联的名称到 // Retrieve all posts that have at least one comment... $posts = AppPost::has('comments')->get(); 你也可以指定操作符,和数量来进一步定制查询:PHP实战 // Retrieve all posts that have three or more comments... $posts = Post::has('comments','>=',3)->get(); 你也可以使用 . 语法来构造嵌套的 // Retrieve all posts that hava at least one comment with votes... $posts = Post::has('comments.votes')->get(); 如果你需要更高的控制,你可以使用 // Retrieve all posts with at least one comment containing words like foo% $posts = Post::whereHas('comments',function ($query) { $query->where('content','like','foo%'); })->get(); 统计关联结果PHP实战 如果你希望统计关联的结果而不实际的加载它们,你可以使用 $posts = AppPost::withCount('comments')->get(); foreach ($posts as $post) { echo $post->comments_count; } 你也可以同时检索多个关联的统计,以及添加查询约束:PHP实战 $posts = Post::withCount(['votes','comments' => function ($query) { $query->where('content','foo%'); }])->get(); echo $posts[0]->votes_count; echo $posts[0]->comments_count; 预加载PHP实战 当通过属性访问 Eloquent 关联时,该关联的数据会被延迟加载.这意味着该关联数据只有在你真实的访问属性时才会进行加载.事实上,Eloquent 可以在上层模型中一次性预加载的.预加载有效避免了 N + 1 的查找问题.要说明 N + 1 查找问题,我们可以来看一个 <?php namespace App; use IlluminateDatabaseEloquentModel; class Book extends Model { /** * Get the author that wrote the book. */ public function author() { return $this->belongsTo('AppAuthor'); } } 现在,让我们检索所有的书籍和他们的作者:PHP实战 $books = AppBook::all(); foreach ($books as $book) { echo $book->author->name; } 这个循环会执行一次查找回所有的书籍,接着每本书会运行一次查找作者的操作.所以,如果我们拥有 25 本书,那么循环将会进行 26 次查询:1 次查询所有的书籍,25 次查询相关书籍的作者.PHP实战 非常幸运的,我们可以使用预加载来将查询有效的控制在 2 次.当查询时,使用 $books = AppBook::with('author')->get(); foreach ($books as $book) { echo $book->author->name; } 对于这个操作,只会执行两个查询:PHP实战 select * from books select * from authors where id in (1,2,3,4,5,...) 预加载多个关联PHP实战 有时候你可能需要在一个操作中预加载多个关联,你只需要传递额外的参数到 $books = AppBook::with('author','publisher')->get(); 嵌套的预加载PHP实战 你可以使用 . 语法来加载嵌套的关联.比如,让我们在一个 Eloquent 语句中一次加载所有书籍的作者以及作者的死人通讯簿:PHP实战 $books = AppBook::with('author.contacts')->get(); 预加载约束 有时候你可能希望预加载一些关联,但是也需要对预加载查询指定额外的约束,这里有个示例:PHP实战 $users = AppUser::with(['posts' => function ($query) { $query->where('title','%first%'); }])->get(); 在这个例子中, $users = AppUser::with(['posts' => function ($query) { $query->orderBy('created_at','desc'); }])->get(); 延迟预加载 有时候你可能需要在上层模型被获取后才预加载其关联.当你需要来动态决定是否加载关联模型时尤其有用:PHP实战 $books = AppBook::all(); if ($someCondition) { $books->load('author','publisher'); } 如果你需要对预加载做一些查询约束,你可以传递 $books->load(['author' => function ($query) { $query->orderBy('published_date','asc'); }]); 插入关系模型 Save 方法PHP实战
$comment = new AppComment(['message' => 'A new comment.']); $post = AppPost::find(1); $post->comments()->save($comment); 注意上面我们并没有使用关联模型的动态属性的方式来访问 如果你需要一次添加多个关联模型,你需要使用 $post = AppPost::find(1); $post->comments()->saveMany([ new AppComment(['message' => 'A new comment.']),new AppComment(['message' => 'Another comment.']),]); Save & 多对多关联PHP实战 当与多对多关联互动时, AppUser::find(1)->roles()->save($role,['expires' => $expires]); Create 方法PHP实战 除了 $post = AppPost::find(1); $comment = $post->comments()->create([ 'message' => 'A new comment.',]); 在使用 更新从属关联模型PHP实战 当更新一个 $account = AppAccount::find(10); $user->account()->associate($account); $user->save(); 当删除 $user->account()->dissociate(); $user->save(); 多对多关联 附加 / 抽离PHP实战 当使用多对多关联时,Eloquent 提供了一些额外的帮助方法来更方便的管理关联模型.比如,让我们想象一下用户可以有很多角色并且角色可以有很多用户.你可以使用 $user = AppUser::find(1); $user->roles()->attach($roleId);
$user->roles()->attach($roleId,['expires' => $expires]); 当然,有时候你可能需要从用户中删除一个角色.你可以使用 // Detach a single role from the user... $user->roles()->detach($roleId); // Detach all roles from the user... $user->roles()->detach(); 为了更加的便捷, $user = AppUser::find(1); $user->roles()->detach([1,3]); $user->roles()->attach([1 => ['expires' => $expires],3]); 更新中间表的记录 如果你需要更新中间表中存在的行,你可以使用 $user = AppUser::find(1); $user->roles()->updateExistingPivot($roleId,$attributes); 便利的同步PHP实战 你也可以使用 $user->roles()->sync([1,3]); 你也可以同时传递额外的中间表的键值对:PHP实战 $user->roles()->sync([1 => ['expires' => true],3]); 联动上层模型时间戳PHP实战 当一个模型 <?php namespace App; use IlluminateDatabaseEloquentModel; class Comment extends Model { /** * All of the relationships to be touched. * * @var array */ protected $touches = ['post']; /** * Get the post that the comment belongs to. */ public function post() { return $this->belongsTo('AppPost'); } } 现在,当你更新 $comment = AppComment::find(1); $comment->text = 'Edit to this comment!'; $comment->save(); 以上就是laravel学习教程之关联模型的全部内容,希望对大家学习php有所帮助.PHP实战 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |