咱们先从创建一个 Eloquent 模型开始。模型一般放在 app
目录下,可是您能够将它们放在任何地方,只要能经过 composer.json 自动载入。全部的 Eloquent 模型都继承于Illuminate\Database\Eloquent\Model
。php
class User extends Model {}
你也能够经过 make:model
命令自动生成 Eloquent 模型:数据库
php artisan make:model User
注意咱们并无告诉 Eloquent User 模型会使用哪一个数据库表。若没有特别指定,系统会默认自动对应名称为「类名称的小写复数形态」的数据库表。因此,在上面的例子中, Eloquent 会假设 User
模型将把数据存在 users
数据库表。您也能够在类中定义table
属性自定义要对应的数据库表。json
class User extends Model { protected $table = 'my_users'; }
注意: Eloquent 也会假设每一个数据库表都有一个字段名称为
id
的主键。您能够在类里定义primaryKey
属性来重写。一样的,您也能够定义connection
属性,指定模型链接到指定的数据库链接。数组
定义好模型以后,您就能够从数据库表新增及获取数据了。注意在默认状况下,在数据库表里须要有 updated_at
和 created_at
两个字段。若是您不想设定或自动更新这两个字段,则将类里的 $timestamps
属性设为 false便可。缓存
$users = User::all();
$user = User::find(1); var_dump($user->name);
提示: 全部查询构造器里的方法,查询 Eloquent 模型时也可使用。安全
有时, 您可能想要在找不到模型数据时抛出异常,经过 firstOrFail
方法。闭包
$model = User::findOrFail(1); $model = User::where('votes', '>', 100)->firstOrFail();
Doing this will let you catch the exception so you can log and display an error page as necessary. To catch the ModelNotFoundException
, add some logic to yourapp/Exceptions/Handler.php
file.app
use Illuminate\Database\Eloquent\ModelNotFoundException; class Handler extends ExceptionHandler { public function render($request, Exception $e) { if ($e instanceof ModelNotFoundException) { // Custom logic for model not found... } return parent::render($request, $e); } }
$users = User::where('votes', '>', 100)->take(10)->get(); foreach ($users as $user) { var_dump($user->name); }
固然,您也可使用查询构造器的聚合查询方法。composer
$count = User::where('votes', '>', 100)->count();
若是没办法使用流畅接口产生出查询语句,也可使用 whereRaw
方法:ide
$users = User::whereRaw('age > ? and votes = 100', [25])->get();
若是您要处理很是多(数千条)Eloquent 查询结果,使用 chunk
方法可让您顺利工做而不会消耗大量内存:
User::chunk(200, function($users) { foreach ($users as $user) { // } });
传到方法里的第一个参数表示每次「拆分」要取出的数据数量。第二个参数的闭合函数会在每次取出数据时被调用。
您也能够指定在执行 Eloquent 查询时要使用哪一个数据库链接。只要使用 on
方法:
$user = User::on('connection-name')->find(1);
若是您在使用 读取 / 写入链接, 您能够经过以下命令来强制查询使用 写入
链接:
$user = User::onWriteConnection()->find(1);
在创建一个新的模型时,您把属性以数组的方式传入模型的构造方法,这些属性值会经由批量赋值存成模型数据。这一点很是方便,然而,若盲目地将用户输入存到模型时,可能会形成严重的安全隐患。若是盲目的存入用户输入,用户能够随意的修改任何以及全部模型的属性。基于这个理由,全部的 Eloquent 模型默认会阻止批量赋值 。
咱们以在模型里设定 fillable
或 guarded
属性做为开始。
Fillable
属性fillable
属性指定了哪些字段支持批量赋值 。能够设定在类的属性里或是实例化后设定。
class User extends Model { protected $fillable = ['first_name', 'last_name', 'email']; }
在上面的例子里,只有三个属性容许批量赋值。
Guarded
属性guarded
与 fillable
相反,是做为「黑名单」而不是「白名单」:
class User extends Model { protected $guarded = ['id', 'password']; }
注意: 使用
guarded
时,Input::get()
或任何用户能够控制的未过滤数据,永远不该该传入save
或update
方法,由于没有在「黑名单」内的字段可能被更新。
上面的例子中, id
和 password
属性不会被批量赋值,而全部其余的属性则容许批量赋值。您也可使用 guard 属性阻止全部属性被批量赋值:
protected $guarded = ['*'];
要从模型新增一条数据到数据库,只要创建一个模型实例并调用 save
方法便可。
$user = new User; $user->name = 'John'; $user->save();
注意: 一般 Eloquent 模型主键值会自动递增。可是您若想自定义主键,将
incrementing
属性设成 false 。
也可使用 create
方法存入新的模型数据,新增完后会返回新增的模型实例。可是在新增前,须要先在模型类里设定好 fillable
或 guarded
属性,由于 Eloquent 默认会防止批量赋值。
在新模型数据被储存或新增后,若模型有自动递增主键,能够从对象取得 id
属性值:
$insertedId = $user->id;
class User extends Model { protected $guarded = ['id', 'account_id']; }
// 在数据库中创建一个新的用户... $user = User::create(['name' => 'John']); // 以属性找用户,若没有则新增并取得新的实例... $user = User::firstOrCreate(['name' => 'John']); // 以属性找用户,若没有则创建新的实例... $user = User::firstOrNew(['name' => 'John']);
要更新模型,能够取出它,更改属性值,而后使用 save
方法:
$user = User::find(1); $user->email = 'john@foo.com'; $user->save();
有时您可能不仅想要储存模型自己,也想要储存关联的数据。您可使用 push
方法达到目的:
$user->push();
您能够结合查询语句,批次更新模型:
$affectedRows = User::where('votes', '>', 100)->update(['status' => 2]);
**注意: ** 若使用 Eloquent 查询构造器批次更新模型,则不会触发模型事件。
要删除模型,只要使用实例调用 delete
方法:
$user = User::find(1); $user->delete();
User::destroy(1); User::destroy([1, 2, 3]); User::destroy(1, 2, 3);
固然,您也能够结合查询语句批次删除模型:
$affectedRows = User::where('votes', '>', 100)->delete();
若是您只想要更新模型的时间戳,您可使用 touch
方法:
$user->touch();
经过软删除方式删除了一个模型后,模型中的数据并非真的从数据库被移除。而是会设定 deleted_at
时间戳。要让模型使用软删除功能,只要在模型类里加入SoftDeletingTrait
便可:
use Illuminate\Database\Eloquent\SoftDeletes; class User extends Model { use SoftDeletes; protected $dates = ['deleted_at']; }
要加入 deleted_at
字段到数据库表,能够在迁移文件里使用 softDeletes
方法:
$table->softDeletes();
如今当您使用模型调用 delete
方法时, deleted_at
字段会被更新成如今的时间戳。在查询使用软删除功能的模型时,被「删除」的模型数据不会出如今查询结果里。
要强制让已被软删除的模型数据出如今查询结果里,在查询时使用 withTrashed
方法:
$users = User::withTrashed()->where('account_id', 1)->get();
withTrashed
也能够用在关联查询:
$user->posts()->withTrashed()->get();
若是您只想查询被软删除的模型数据,可使用 onlyTrashed
方法:
$users = User::onlyTrashed()->where('account_id', 1)->get();
要把被软删除的模型数据恢复,使用 restore
方法:
$user->restore();
您也能够结合查询语句使用 restore
:
User::withTrashed()->where('account_id', 1)->restore();
如同 withTrashed
, restore
方法也能够用在关联对象:
$user->posts()->restore();
若是想要真的从模型数据库删除,使用 forceDelete
方法:
$user->forceDelete();
forceDelete
方法也能够用在关联对象:
$user->posts()->forceDelete();
要确认模型是否被软删除了,可使用 trashed
方法:
if ($user->trashed()) { // }
默认 Eloquent 会自动维护数据库表的 created_at
和 updated_at
字段。只要把这两个「时间戳」字段加到数据库表, Eloquent 就会处理剩下的工做。若是不想让 Eloquent 自动维护这些字段,把下面的属性加到模型类里:
class User extends Model { protected $table = 'users'; public $timestamps = false; }
若是想要自定义时间戳格式,能够在模型类里重写 getDateFormat
方法:
class User extends Model { protected function getDateFormat() { return 'U'; } }
范围查询可让您轻松的重复利用模型的查询逻辑。要设定范围查询,只要定义有scope
前缀的模型方法:
class User extends Model { public function scopePopular($query) { return $query->where('votes', '>', 100); } public function scopeWomen($query) { return $query->whereGender('W'); } }
$users = User::popular()->women()->orderBy('created_at')->get();
有时您可能想要定义可接受参数的范围查询方法。只要把参数加到方法里:
class User extends Model { public function scopeOfType($query, $type) { return $query->whereType($type); } }
而后把参数值传到范围查询方法调用里:
$users = User::ofType('member')->get();
有时您可能但愿定义一个 scope 能够用于模型的全部查询中。本质上,这也是 Eloquent 的"软删除"功能的实现原理。Global scopes 是经过 PHP traits 的组合以及实现 Illuminate\Database\Eloquent\ScopeInterface
接口来定义的。
首先,咱们须要定义一个 trait。 这里咱们用 Laravel 的 SoftDeletes
举例:
trait SoftDeletes { /** * Boot the soft deleting trait for a model. * * @return void */ public static function bootSoftDeletes() { static::addGlobalScope(new SoftDeletingScope); } }
若是一个 Eloquent 模型引入了一个 trait ,而这个 trait 中带有符合 bootNameOfTrait
惯例命名的方法 ,那么这个方法会在 Eloquent 模型启动的时候调用, 您能够在此时注册 global scope ,或者作一些其余您想要的操做。定义的 scope 必须实现ScopeInterface
接口,这个接口提供了两个方法:apply
和 remove
。
apply
方法接受一个 Illuminate\Database\Eloquent\Builder
查询构造器对象以及它所应用的 Model
,用来添加这个 scope 所需的额外的 where
子句。而remove
方法一样接受一个 Builder
对象以及 Model
,用来反向的执行 apply
操做。也就是说,remove
方法应该移除已经添加的 where
子句 (或者其余查询子句)。所以,咱们的SoftDeletingScope
的方法应该以下:
/** * Apply the scope to a given Eloquent query builder. * * @param \Illuminate\Database\Eloquent\Builder $builder * @param \Illuminate\Database\Eloquent\Model $model * @return void */ public function apply(Builder $builder, Model $model) { $builder->whereNull($model->getQualifiedDeletedAtColumn()); $this->extend($builder); } /** * Remove the scope from the given Eloquent query builder. * * @param \Illuminate\Database\Eloquent\Builder $builder * @param \Illuminate\Database\Eloquent\Model $model * @return void */ public function remove(Builder $builder, Model $model) { $column = $model->getQualifiedDeletedAtColumn(); $query = $builder->getQuery(); foreach ((array) $query->wheres as $key => $where) { // If the where clause is a soft delete date constraint, we will remove it from // the query and reset the keys on the wheres. This allows this developer to // include deleted model in a relationship result set that is lazy loaded. if ($this->isSoftDeleteConstraint($where, $column)) { unset($query->wheres[$key]); $query->wheres = array_values($query->wheres); } } }
固然,您的数据库表极可能跟另外一张表相关联。例如,一篇 blog 文章可能有不少评论,或是一张订单跟下单客户相关联。 Eloquent 让管理和处理这些关联变得很容易。 Laravel 有不少种关联类型:
一对一关联是很基本的关联。例如一个 User
模型会对应到一个 Phone
。 在 Eloquent 里能够像下面这样定义关联:
class User extends Model { public function phone() { return $this->hasOne('App\Phone'); } }
传到 hasOne
方法里的第一个参数是关联模型的类名称。定义好关联以后,就可使用 Eloquent 的动态属性取得关联对象:
$phone = User::find(1)->phone;
SQL 会执行以下语句:
select * from users where id = 1 select * from phones where user_id = 1
注意, Eloquent 假设对应的关联模型数据库表里,外键名称是基于模型名称。在这个例子里,默认 Phone
模型数据库表会以 user_id
做为外键。若是想要更改这个默认,能够传入第二个参数到 hasOne
方法里。更进一步,您能够传入第三个参数,指定关联的外键要对应到自己的哪一个字段:
return $this->hasOne('App\Phone', 'foreign_key'); return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
要在 Phone
模型里定义相对的关联,可使用 belongsTo
方法:
class Phone extends Model { public function user() { return $this->belongsTo('App\User'); } }
在上面的例子里, Eloquent 默认会使用 phones
数据库表的 user_id
字段查询关联。若是想要本身指定外键字段,能够在 belongsTo
方法里传入第二个参数:
class Phone extends Model { public function user() { return $this->belongsTo('App\User', 'local_key'); } }
除此以外,也能够传入第三个参数指定要参照上层数据库表的哪一个字段:
class Phone extends Model { public function user() { return $this->belongsTo('App\User', 'local_key', 'parent_key'); } }
一对多关联的例子如,一篇 Blog 文章可能「有不少」评论。能够像这样定义关联:
class Post extends Model { public function comments() { return $this->hasMany('App\Comment'); } }
如今能够经由动态属性取得文章的评论:
$comments = Post::find(1)->comments;
若是须要增长更多条件限制,能够在调用 comments
方法后面经过链式查询条件方法:
$comments = Post::find(1)->comments()->where('title', '=', 'foo')->first();
一样的,您能够传入第二个参数到 hasMany
方法更改默认的外键名称。以及,如同hasOne
关联,能够指定自己的对应字段:
return $this->hasMany('App\Comment', 'foreign_key'); return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
要在 Comment
模型定义相对应的关联,可以使用 belongsTo
方法:
class Comment extends Model { public function post() { return $this->belongsTo('App\Post'); } }
多对多关联更为复杂。这种关联的例子如,一个用户( user )可能用有不少身份( role ),而一种身份可能不少用户都有。例如不少用户都是「管理者」。多对多关联须要用到三个数据库表: users
, roles
,和 role_user
。 role_user
枢纽表命名是以相关联的两个模型数据库表,依照字母顺序命名,枢纽表里面应该要有 user_id
和role_id
字段。
可使用 belongsToMany
方法定义多对多关系:
class User extends Model { public function roles() { return $this->belongsToMany('App\Role'); } }
如今咱们能够从 User
模型取得 roles:
$roles = User::find(1)->roles;
若是不想使用默认的枢纽数据库表命名方式,能够传递数据库表名称做为belongsToMany
方法的第二个参数:
return $this->belongsToMany('App\Role', 'user_roles');
也能够更改默认的关联字段名称:
return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'foo_id');
固然,也能够在 Role
模型定义相对的关联:
class Role extends Model { public function users() { return $this->belongsToMany('App\User'); } }
「远层一对多关联」提供了方便简短的方法,能够经由多层间的关联取得远层的关联。例如,一个 Country
模型可能经过 Users
关联到不少 Posts
模型。 数据库表间的关系可能看起来以下:
countries
id - integer name - string users id - integer country_id - integer name - string posts id - integer user_id - integer title - string
虽然 posts
数据库表自己没有 country_id
字段,但 hasManyThrough
方法让咱们可使用 $country->posts
取得 country 的 posts。咱们能够定义如下关联:
class Country extends Model { public function posts() { return $this->hasManyThrough('App\Post', 'App\User'); } }
若是想要手动指定关联的字段名称,能够传入第三和第四个参数到方法里:
class Country extends Model { public function posts() { return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id'); } }
多态关联能够用一个简单的关联方法,就让一个模型同时关联多个模型。例如,您可能想让 photo 模型同时和一个 staff 或 order 模型关联。能够定义关联以下:
class Photo extends Model { public function imageable() { return $this->morphTo(); } } class Staff extends Model { public function photos() { return $this->morphMany('App\Photo', 'imageable'); } } class Order extends Model { public function photos() { return $this->morphMany('App\Photo', 'imageable'); } }
如今咱们能够从 staff 或 order 模型取得多态关联对象:
$staff = Staff::find(1); foreach ($staff->photos as $photo) { // }
然而,多态关联真正神奇的地方,在于要从 Photo
模型取得 staff 或 order 对象时:
$photo = Photo::find(1); $imageable = $photo->imageable;
Photo 模型里的 imageable
关联会返回 Staff
或 Order
实例,取决于这是哪种模型拥有的照片。
为了理解多态关联的运做机制,来看看它们的数据库表结构:
staff
id - integer name - string orders id - integer price - integer photos id - integer path - string imageable_id - integer imageable_type - string
要注意的重点是 photos
数据库表的 imageable_id
和 imageable_type
。在上面的例子里, ID 字段会包含 staff 或 order 的 ID,而 type 是拥有者的模型类名称。这就是让 ORM 在取得 imageable
关联对象时,决定要哪种模型对象的机制。
除了通常的多态关联,也可使用多对多的多态关联。例如,Blog 的 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
模型均可以经由 tags
方法创建morphToMany
关联:
class Post extends Model { public function tags() { return $this->morphToMany('App\Tag', 'taggable'); } }
在 Tag
模型里针对每一种关联创建一个方法:
class Tag extends Model { public function posts() { return $this->morphedByMany('App\Post', 'taggable'); } public function videos() { return $this->morphedByMany('App\Video', 'taggable'); } }
在取得模型数据时,您可能想要以关联模型做为查询限制。例如,您可能想要取得全部「至少有一篇评论」的Blog 文章。可使用 has
方法达成目的:
$posts = Post::has('comments')->get();
也能够指定运算符和数量:
$posts = Post::has('comments', '>=', 3)->get();
也可使用"点号"的形式来获取嵌套的 has
声明:
$posts = Post::has('comments.votes')->get();
若是想要更进阶的用法,可使用 whereHas
和 orWhereHas
方法,在 has
查询里设置 "where" 条件 :
$posts = Post::whereHas('comments', function($q) { $q->where('content', 'like', 'foo%'); })->get();
Eloquent 能够经由动态属性取得关联对象。 Eloquent 会自动进行关联查询,并且会很聪明的知道应该要使用 get
(用在一对多关联)或是 first
(用在一对一关联)方法。能够经由和「关联方法名称相同」的动态属性取得对象。例如,以下面的模型对象 $phone
:
class Phone extends Model { public function user() { return $this->belongsTo('App\User'); } } $phone = Phone::find(1);
您能够不用像下面这样打印用户的 email :
echo $phone->user()->first()->email;
而能够简写以下:
echo $phone->user->email;
注意: 若取得的是许多关联对象,会返回
Illuminate\Database\Eloquent\Collection
对象。
预载入是用来减小 N + 1 查询问题。例如,一个 Book
模型数据会关联到一个 Author
。关联会像下面这样定义:
class Book extends Model { public function author() { return $this->belongsTo('App\Author'); } }
如今考虑下面的代码:
foreach (Book::all() as $book) { echo $book->author->name; }
上面的循环会执行一次查询取回全部数据库表上的书籍,然而每本书籍都会执行一次查询取得做者。因此若咱们有 25 本书,就会进行 26次查询。
很幸运地,咱们可使用预载入大量减小查询次数。使用 with
方法指定想要预载入的关联对象:
foreach (Book::with('author')->get() as $book) { echo $book->author->name; }
如今,上面的循环总共只会执行两次查询:
select * from books select * from authors where id in (1, 2, 3, 4, 5, ...)
使用预载入能够大大提升程序的性能。
固然,也能够同时载入多种关联:
$books = Book::with('author', 'publisher')->get();
甚至能够预载入巢状关联:
$books = Book::with('author.contacts')->get();
上面的例子中, author
关联会被预载入, author 的 contacts
关联也会被预载入。
有时您可能想要预载入关联,同时也想要指定载入时的查询限制。下面有一个例子:
$users = User::with(['posts' => function($query) { $query->where('title', 'like', '%first%'); }])->get();
上面的例子里,咱们预载入了 user 的 posts 关联,并限制条件为 post 的 title 字段需包含 "first" 。
固然,预载入的闭合函数里不必定只能加上条件限制,也能够加上排序:
$users = User::with(['posts' => function($query) { $query->orderBy('created_at', 'desc'); }])->get();
也能够直接从模型的 collection 预载入关联对象。这对于须要根据状况决定是否载入关联对象时,或是跟缓存一块儿使用时颇有用。
$books = Book::all(); $books->load('author', 'publisher');
你能够传入一个闭包来对查询构建器进行条件限制:
$books->load(['author' => function($query) { $query->orderBy('published_date', 'asc'); }]);
您经常会须要加入新的关联模型。例如新增一个 comment 到 post 。除了手动设定模型的 post_id
外键,也能够从上层的 Post
模型新增关联的 comment :
$comment = new Comment(['message' => 'A new comment.']); $post = Post::find(1); $comment = $post->comments()->save($comment);
上面的例子里,新增的 comment 模型中 post_id
字段会被自动设定。
若是想要同时新增不少关联模型:
$comments = [ new Comment(['message' => 'A new comment.']), new Comment(['message' => 'Another comment.']), new Comment(['message' => 'The latest comment.']) ]; $post = Post::find(1); $post->comments()->saveMany($comments);
要更新 belongsTo
关联时,可使用 associate
方法。这个方法会设定子模型的外键:
$account = Account::find(10); $user->account()->associate($account); $user->save();
您也能够新增多对多的关联模型。让咱们继续使用 User
和 Role
模型做为例子。咱们可使用 attach
方法简单地把 roles 附加给一个 user:
$user = User::find(1); $user->roles()->attach(1);
也能够传入要存在枢纽表中的属性数组:
$user->roles()->attach(1, ['expires' => $expires]);
固然,有 attach
方法就会有相反的 detach
方法:
$user->roles()->detach(1);
attach
和 detach
均可以接受ID数组做为参数:
$user = User::find(1); $user->roles()->detach([1, 2, 3]); $user->roles()->attach([1 => ['attribute1' => 'value1'], 2, 3]);
您也可使用 sync
方法附加关联模型。 sync
方法会把根据 ID 数组把关联存到枢纽表。附加完关联后,枢纽表里的模型只会关联到 ID 数组里的 id :
$user->roles()->sync([1, 2, 3]);
也能够在把每一个 ID 加入枢纽表时,加入其余字段的数据:
$user->roles()->sync([1 => ['expires' => true]]);
有时您可能想要使用一个命令,在创建新模型数据的同时附加关联。可使用 save
方法达成目的:
$role = new Role(['name' => 'Editor']); User::find(1)->roles()->save($role);
上面的例子里,新的 Role
模型对象会在储存的同时关联到 user
模型。也能够传入属性数组把数据加到关联数据库表:
User::find(1)->roles()->save($role, ['expires' => $expires]);
当模型 belongsTo
另外一个模型时,比方说一个 Comment
属于一个 Post
,若是能在子模型被更新时,更新上层的时间戳,这将会颇有用。例如,当 Comment
模型更新时,您可能想要可以同时自动更新 Post
的 updated_at
时间戳。 Eloquent 让事情变得很简单。只要在子关联的类里,把关联方法名称加入 touches
属性便可:
class Comment extends Model { protected $touches = ['post']; public function post() { return $this->belongsTo('App\Post'); } }
如今,当您更新 Comment
时,对应的 Post
会自动更新 updated_at
字段:
$comment = Comment::find(1); $comment->text = 'Edit to this comment!'; $comment->save();
如您所知,要操做多对多关联须要一个中间的数据库表。 Eloquent 提供了一些有用的方法能够和这张表互动。例如,假设 User
对象关联到不少 Role
对象。取出这些关联对象时,咱们能够在关联模型上取得 pivot
数据库表的数据:
$user = User::find(1); foreach ($user->roles as $role) { echo $role->pivot->created_at; }
注意咱们取出的每一个 Role
模型对象会自动给一个 pivot
属性。这属性包含了枢纽表的模型数据,能够像通常的 Eloquent 模型同样使用。
默认 pivot
对象只会有关联键的属性。若是您想让 pivot 能够包含其余枢纽表的字段,能够在定义关联方法时指定那些字段:
return $this->belongsToMany('App\Role')->withPivot('foo', 'bar');
如今能够在 Role
模型的 pivot
对象上取得 foo
和 bar
属性了。
若是您想要能够自动维护枢纽表的 created_at
和 updated_at
时间戳,在定义关联方法时加上 withTimestamps
方法:
return $this->belongsToMany('App\Role')->withTimestamps();
要删除模型在枢纽表的全部关联数据,可使用 detach
方法:
User::find(1)->roles()->detach();
注意,如上的操做不会移除 roles
数据库表里面的数据,只会移除枢纽表里的关联数据。
有时您只想更新枢纽表的数据,而没有要移除关联。若是您想更新枢纽表,能够像下面的例子使用 updateExistingPivot
方法:
User::find(1)->roles()->updateExistingPivot($roleId, $attributes);
Laravel 容许您自定义枢纽模型。要自定义模型,首先要创建一个继承 Eloquent 的「基本」模型类。在其余的 Eloquent 模型继承这个自定义的基本类,而不是默认的 Eloquent 。在基本模型类里,加入下面的方法返回自定义的枢纽模型实例:
public function newPivot(Model $parent, array $attributes, $table, $exists) { return new YourCustomPivot($parent, $attributes, $table, $exists); }
全部 Eloquent 查询返回的数据,若是结果多于一条,不论是经由 get
方法或是relationship
,都会转换成集合对象返回。这个对象实现了 IteratorAggregate
PHP 接口,因此能够像数组通常进行遍历。而集合对象自己还拥有不少有用的方法能够操做模型数据。
例如,咱们可使用 contains
方法,确认结果数据中,是否包含主键为特定值的对象。
$roles = User::find(1)->roles; if ($roles->contains(2)) { // }
集合也能够转换成数组或 JSON:
$roles = User::find(1)->roles->toArray(); $roles = User::find(1)->roles->toJson();
若是集合被转换成字符串类型,会返回 JSON 格式:
$roles = (string) User::find(1)->roles;
Eloquent 集合里包含了一些有用的方法能够进行循环或是进行过滤:
$roles = $user->roles->each(function($role) { // });
过滤集合时,回调函数的使用方式和 array_filter 里同样。
$users = $users->filter(function($user) { return $user->isAdmin(); });
注意: 若是要在过滤集合以后转成 JSON,转换以前先调用
values
方法重设数组的键值。
$roles = User::find(1)->roles; $roles->each(function($role) { // });
$roles = $roles->sortBy(function($role) { return $role->created_at; }); $roles = $roles->sortByDesc(function($role) { return $role->created_at; });
$roles = $roles->sortBy('created_at'); $roles = $roles->sortByDesc('created_at');
有时您可能想要返回自定义的集合对象,让您能够在集合类里加入想要的方法。能够在 Eloquent 模型类里重写 newCollection
方法:
class User extends Model { public function newCollection(array $models = []) { return new CustomCollection($models); } }
Eloquent 提供了一种便利的方法,能够在获取或设定属性时进行转换。要定义获取器,只要在模型里加入相似 getFooAttribute
的方法。注意方法名称应该使用驼峰式大小写命名,而对应的 database 字段名称是下划线分隔小写命名:
class User extends Model { public function getFirstNameAttribute($value) { return ucfirst($value); } }
上面的例子中, first_name 字段设定了一个获取器。注意传入方法的参数是本来的字段数据。
修改器的定义方式也是相似的:
class User extends Model { public function setFirstNameAttribute($value) { $this->attributes['first_name'] = strtolower($value); } }
默认 Eloquent 会把 created_at
和 updated_at
字段属性转换成 Carbon 实例,它提供了不少有用的方法,并继承了 PHP 原生的 DateTime
类。
您能够经过重写模型的 getDates
方法,自定义哪一个字段能够被自动转换,或甚至彻底关闭这个转换:
public function getDates() { return ['created_at']; }
当字段是表示日期的时候,能够将值设为 UNIX timestamp 、日期字符串( Y-m-d )、 日期时间( date-time )字符串,固然还有 DateTime
或 Carbon
实例。
要彻底关闭日期转换功能,只要从 getDates
方法返回空数组便可:
public function getDates() { return []; }
若是您想要某些属性始终转换成另外一个数据类型, 您能够在模型中增长 casts
属性。不然,您须要为每一个属性定义修改器,这样会增长更多的时间开销。这里有一个使用casts
属性的例子:
/** * 须要被转换成基本类型的属性值。 * * @var array */ protected $casts = [ 'is_admin' => 'boolean', ];
如今当你获取 is_admin
属性时始终会是布尔类型,甚至在数据库中存储的这个值是一个整型也会被转换。其余支持的类型转换值有: integer
, real
, float
, double
,string
, boolean
, object
和 array
。
若是您存储的值是一个序列化的 JSON 时,那么 array
类型转换将会很是有用。好比,您的数据表里有一个 TEXT 类型的字段存储着序列化后的 JSON 数据, 经过增长array
类型转换, 当获取这个属性的时候会自动反序列化成 PHP 的数组:
/** * 须要被转换成基本类型的属性值。 * * @var array */ protected $casts = [ 'options' => 'array', ];
如今,当你使用 Eloquent 模型时:
$user = User::find(1); // $options 是一个数组... $options = $user->options; // options 会自动序列化成 JSON... $user->options = ['foo' => 'bar'];
Eloquent 模型有不少事件能够触发,让您能够在模型操做的生命周期的不一样时间点,使用下列方法绑定事件: creating
, created
, updating
, updated
, saving
, saved
,deleting
, deleted
, restoring
, restored
。
当一个对象初次被储存到数据库, creating
和 created
事件会被触发。若是不是新对象而调用了 save
方法, updating
/ updated
事件会被触发。而二者的 saving
/saved
事件都会被触发。
若是 creating
、 updating
、 saving
、 deleting
事件返回 false 的话,就会取消数据库操做
User::creating(function($user) { if ( ! $user->isValid()) return false; });
您能够在 EventServiceProvider
中注册您的模型事件绑定。好比:
/** * Register any other events for your application. * * @param \Illuminate\Contracts\Events\Dispatcher $events * @return void */ public function boot(DispatcherContract $events) { parent::boot($events); User::creating(function($user) { // }); }
要整合模型的事件处理,能够注册一个模型观察者。观察者类里要设定对应模型事件的方法。例如,观察者类里可能有 creating
、 updating
、 saving
方法,还有其余对应模型事件名称的方法:
例如,一个模型观察者类可能看起来以下:
class UserObserver { public function saving($model) { // } public function saved($model) { // } }
可使用 observe
方法注册一个观察者实例:
User::observe(new UserObserver);
当你把一个模型实例传递给 route
或者 action
方法时,模型的主键会被插入到生成的 URI 中。好比:
Route::get('user/{user}', 'UserController@show'); action('UserController@show', [$user]);
在这个例子中 $user->id
属性会被插入到生成的 URL 的 {user}
这个占位符中。不过,若是你想使用其余的属性而不是 ID 的话,你能够覆盖模型的 getRouteKey
方法:
public function getRouteKey() { return $this->slug; }
当构建 JSON API 时,您可能经常须要把模型和关联对象转换成数组或JSON。因此Eloquent里已经包含了这些方法。要把模型和已载入的关联对象转成数组,可使用toArray
方法:
$user = User::with('roles')->first(); return $user->toArray();
注意也能够把整个的模型集合转换成数组:
return User::all()->toArray();
要把模型转换成 JSON,可使用 toJson
方法:
return User::find(1)->toJson();
注意当模型或集合被转换成字符串类型时会自动转换成 JSON 格式,这意味着您能够直接从路由返回 Eloquent 对象!
Route::get('users', function() { return User::all(); });
有时您可能想要限制能出如今数组或 JSON 格式的属性数据,好比密码字段。只要在模型里增长 hidden
属性便可
class User extends Model { protected $hidden = ['password']; }
注意: 要隐藏关联数据,要使用关联的方法名称,而不是动态获取的属性名称。
此外,可使用 visible
属性定义白名单:
protected $visible = ['first_name', 'last_name'];
有时候您可能想要增长不存在数据库字段的属性数据。这时候只要定义一个获取器便可:
public function getIsAdminAttribute() { return $this->attributes['admin'] == 'yes'; }
定义好获取器以后,再把对应的属性名称加到模型里的 appends
属性:
protected $appends = ['is_admin'];
把属性加到 appends
数组以后,在模型数据转换成数组或 JSON 格式时就会有对应的值。在 appends
数组中定义的值一样遵循模型中 visible
和 hidden
的设定。