Laravel 5.8 模型和模型事件使用

一、基础使用

如何定义模型和使用artisan命令来创建模型、以及在创建模型的时候生成数据库迁移文件

// 定义一个 Eloquent 模型
class User extends Model {
}

// 生成一个 Eloquent 模型
php artisan make:model User

// 生成一个 Eloquent 模型的时候,顺便生成迁移文件
php artisan make:model User --migration OR -m

// 指定一个自定义的数据表名称
class User extends Model {
  protected $table = 'my_users';
}

二、More

// 创建一条数据
Model::create(array('key' => 'value'));

// 通过属性找到第一条相匹配的数据或创造一条新数据
Model::firstOrCreate(array('key' => 'value'));

// 通过属性找到第一条相匹配的数据或实例化一条新数据
Model::firstOrNew(array('key' => 'value'));

// 通过属性找到相匹配的数据并更新,如果不存在即创建
Model::updateOrCreate(array('search_key' => 'search_value'), array('key' => 'value'));

// 使用属性的数组来填充一个模型, 用的时候要小心「Mass Assignment」安全问题 !
Model::fill($attributes);

// 通过主键删除模型
// 在上面的例子中,在调用 delete 之前需要先去数据库中查找对应的模型。
// 事实上,如果你知道了模型的主键,你可以直接使用 destroy 方法来删除模型,而不用先去数据库中查找。 
// destroy 方法除了接受单个主键作为参数之外,还接受多个主键,或者使用数组,集合来保存多个主键
Model::destroy(1);
Model::destroy(1, 2, 3);
Modelt::destroy([1, 2, 3]);
Model::destroy(collect([1, 2, 3]));

// Eloquent 的 all 方法会返回模型中所有的结果
Model::all();

// 检索单个模型 / 集合
// 除了从指定的数据表检索所有记录外,你可以使用 find 或 first 方法来检索单条记录。这些方法返回单个模型实例,而不是返回模型集合
Model::find(1);

// 使用双主键进行查找
Model::find(array('first', 'last'));

// 查找失败时抛出异常
Model::findOrFail(1);

// 使用双主键进行查找, 失败时抛出异常
Model::findOrFail(array('first', 'last'));

// 获取所有数据 (使用 get 方法获取结果)
Model::where('foo', '=', 'bar')->get();

// 查询一条数据(从数据表中获取单行或单列)
Model::where('foo', '=', 'bar')->first();

// 除了通过 count 方法可以确定查询条件的结果是否存在之外,还可以使用 exists 和 doesntExist 方法
Model::where('foo', '=', 'bar')->exists();

// 动态属性查找
Model::whereFoo('bar')->first();

// 查找失败时抛出异常
Model::where('foo', '=', 'bar')->firstOrFail();

//    快速复制当前的数据,记得用 save 保存。
Model::find(1)->replicate()->save();

//  聚合方法 
Model::where('foo', '=', 'bar')->count();

// 删除where条件中所有数据
Model::where('foo', '=', 'bar')->delete();

// 输出原始的查询语句
Model::where('foo', '=', 'bar')->toSql();

Model::whereRaw('foo = bar and cars = 2', array(20))->get();
Model::on('connection-name')->find(1);
Model::with('relation')->get();
Model::all()->take(10);
Model::all()->skip(10);

// 默认的 Eloquent 排序是上升排序
Model::all()->orderBy('column');
Model::all()->orderBy('column','desc');

//针对Time排序(默认使用 created_at 列作为排序)
Model::all()->latest();
Model::all()->oldest();

// 查询 json 数据
Model::where('options->language', 'en')->get(); # 字段是字符串
Model::whereJsonContains('options->languages', 'en')->get(); # 字段是数, 使用 whereJsonContains 来查询 JSON 数组
Model::whereJsonLength('options->languages', 0)->get(); # 字段长度为 0, 使用 whereJsonLength 来查询 JSON 数组的长度
Model::whereJsonDoesntContain('options->languages', 'en')->get(); # 字段是数组, 不包含

三、软删除

一般除了真实删除数据库记录, Eloquent 也可以「软删除」模型。软删除的模型并不是真的从数据库中删除了。事实上,是在模型上设置了 deleted_at 属性并将其值写入数据库。如果 deleted_at 值非空,代表这个模型已被软删除。如果要开启模型软删除功能,你需要在模型上使用 Illuminate\Database\Eloquent\SoftDeletes trait:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Flight extends Model
{
    use SoftDeletes;
}

查询软删除模型

// 包括已软删除的模型
Model::withTrashed()->where('cars', 2)->get();

// 在查询结果中包括带被软删除的模型
// 在查询中使用 restore 方法,从而快速恢复多个模型。和其他批量」操作一样,这个操作不会触发模型的任何事件
Model::withTrashed()->where('cars', 2)->restore();

// 永久删除
// 要真实删除数据时,使用 forceDelete 方法即可
Model::where('cars', 2)->forceDelete();

// onlyTrashed 方法 只获取已软删除的模型(检索软删除模型)
Model::onlyTrashed()->where('cars', 2)->get();

四、模型关联

// 一对一 - User::phone()
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
// 一对一 - Phone::user(), 定义相对的关联
return $this->belongsTo('App\User', 'foreign_key', 'other_key');

// 一对多 - Post::comments()
return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
//  一对多 - Comment::post()
return $this->belongsTo('App\Post', 'foreign_key', 'other_key');

// 多对多 - User::roles();
return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'role_id');
// 多对多 - Role::users();
return $this->belongsToMany('App\User');
// 多对多 - Retrieving Intermediate Table Columns
$role->pivot->created_at;
// 多对多 - 中介表字段
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
// 多对多 - 自动维护 created_at 和 updated_at 时间戳
return $this->belongsToMany('App\Role')->withTimestamps();

// 远层一对多 - Country::posts(), 一个 Country 模型可能通过中介的 Users
// 模型关联到多个 Posts 模型(User::country_id)
return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id');

// 多态关联 - Photo::imageable()
return $this->morphTo();
// 多态关联 - Staff::photos()
return $this->morphMany('App\Photo', 'imageable');
// 多态关联 - Product::photos()
return $this->morphMany('App\Photo', 'imageable');
// 多态关联 - 在 AppServiceProvider 中注册你的「多态对照表」
Relation::morphMap([
    'Post' => App\Post::class,
    'Comment' => App\Comment::class,
]);

// 多态多对多关联 - 涉及数据库表: posts,videos,tags,taggables
// Post::tags()
return $this->morphToMany('App\Tag', 'taggable');
// Video::tags()
return $this->morphToMany('App\Tag', 'taggable');
// Tag::posts()
return $this->morphedByMany('App\Post', 'taggable');
// Tag::videos()
return $this->morphedByMany('App\Video', 'taggable');

// 查找关联
$user->posts()->where('active', 1)->get();
// 获取所有至少有一篇评论的文章...
$posts = App\Post::has('comments')->get();
// 获取所有至少有三篇评论的文章...
$posts = Post::has('comments', '>=', 3)->get();
// 获取所有至少有一篇评论被评分的文章...
$posts = Post::has('comments.votes')->get();
// 获取所有至少有一篇评论相似于 foo% 的文章
$posts = Post::whereHas('comments', function ($query) {
    $query->where('content', 'like', 'foo%');
})->get();

// 预加载
$books = App\Book::with('author')->get();
$books = App\Book::with('author', 'publisher')->get();
$books = App\Book::with('author.contacts')->get();

// 延迟预加载
$books->load('author', 'publisher');

// 写入关联模型
$comment = new App\Comment(['message' => 'A new comment.']);
$post->comments()->save($comment);
// Save 与多对多关联
$post->comments()->saveMany([
    new App\Comment(['message' => 'A new comment.']),
    new App\Comment(['message' => 'Another comment.']),
]);
$post->comments()->create(['message' => 'A new comment.']);

// 更新「从属」关联
$user->account()->associate($account);
$user->save();
$user->account()->dissociate();
$user->save();

// 附加多对多关系
$user->roles()->attach($roleId);
$user->roles()->attach($roleId, ['expires' => $expires]);
// 从用户上移除单一身份...
$user->roles()->detach($roleId);
// 从用户上移除所有身份...
$user->roles()->detach();
$user->roles()->detach([1, 2, 3]);
$user->roles()->attach([1 => ['expires' => $expires], 2, 3]);

// 任何不在给定数组中的 IDs 将会从中介表中被删除。
$user->roles()->sync([1, 2, 3]);
// 你也可以传递中介表上该 IDs 额外的值:
$user->roles()->sync([1 => ['expires' => true], 2, 3]);

五、模型事件

Eloquent 模型可以在模型生命周期中的各个时间点触发相应的事件

注意点:
1、依次是 saving>creating>created>saved。
2、update 同理就不具体贴代码了, saving>updating>updated>saved。
3、而 delete 不涉及 save,因此依次只触发了 deleting 和deleted。 当 restore 软删除记录时触发了 restoring 和 restored 方法。
4、Eloquent模型可以触发事件,允许你在模型生命周期中的多个时间点调用如下这些方法:creating, created, updating, updated, saving, saved,deleting, deleted, restoring, restored。事件允许你在一个指定模型类每次保存或更新的时候执行代码。
5、一个新模型被首次保存的时候,creating和created事件会被触发。如果一个模型已经在数据库中存在并调用save方法,updating/updated事件会被触发,无论是创建还是更新,saving/saved事件都会被调用.
6、deleting和deleted很好理解,在删除模型时触发,deleting在删除操作前执行,deleted在删除完成后执行。
7、当创建模型时,依次执行saving、creating、created和saved,同理在更新模型时依次执行saving、updating、updated和saved。无论是使用批量赋值(create/update)还是直接调用save方法,都会触发对应事件(前提是注册了相应的模型事件)。

// 从数据库中获取已存在模型时会触发该事件 
// 数据库中检索现有模型时会触发 retrieved 事件
Model::retrieved(function($model){});

// 当一个新模型被首次保存的时候,这两个事件会被触发
• creating - 对象已经 ready 但未写入数据库
• created - 对象已经写入数据库

Model::creating(function($model){});
Model::created(function($model){});

// 当一个模型已经在数据库中存在并调用 save 方法,这两个事件会被触发
• updating - 对象已经修改但未写入数据库
• updated - 修改已经写入数据库

Model::updating(function($model){});
Model::updated(function($model){});

// 无论是创建还是更新,这两个事件都会被触发
• saving - 对象创建或者已更新但未写入数据库
• saved - 对象创建或者更新已经写入数据库

Model::saving(function($model){});
Model::saved(function($model){});

// 当一个新模型被删除的时候,这两个事件会被触发
• deleting - 删除前
• deleted - 删除后

Model::deleting(function($model){});
Model::deleted(function($model){});

// 当一个新模型恢复删除的时候,这两个事件会被触发
• restoring - 恢复软删除前
• restored - 恢复软删除后

Model::restoring(function($model){});
Model::restored(function($model){});

// 在某个服务提供者的 boot 方法中注册观察者
Model::observe(new FooObserver);

六、Eloquent 配置信息

// 关闭模型插入或更新操作引发的 「mass assignment」异常
Eloquent::unguard();
// 重新开启「mass assignment」异常抛出功能
Eloquent::reguard();

希望上面的总结能对大家有帮助,如果有问题,欢迎大家留言。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351