ORM实用技巧
# 1. 递增和递减
代替以下实现:
$article = Article::find($article_id);
$article->read_count++;
$article->save();
2
3
优化:
$article = Article::find($article_id);
$article->increment('read_count');
2
扩展:
Article::find($article_id)->increment('read_count');
Article::find($article_id)->increment('read_count',10); // +10
Article::find($article_id)->decrement('stock'); // -1
2
3
# 2. 先执行X方法,X方法执行不成功则执行Y方法
Eloquent 有相当一部分函数可以把两个方法结合在一起使用。
实例1 — findOrFail():
代替以下实现:
$user = User::find($id);
if(!$user){abort(404);}
2
优化:
$user = User::findOrFail($id);
实例2 — firstOrCreate():
代替以下实现:
$user = User::where('email', $email)->first();
if(!user){
User::create([
'email' => $email
]);
}
2
3
4
5
6
优化:
$user = User::firstOrCreate([''email' => $email]);
# 3. 模型的boot()方法
在一个Elequent模型中,有个神奇的地方,叫boot()
, 你可以在模型里覆盖默认的行为:
class User extends Model
{
public function boot()
{
parent::boot();
static::updating(function(){
// 可以写日志
// 覆盖一些属性, 类似$model->someting = transform($something);
});
}
}
2
3
4
5
6
7
8
9
10
11
在创建模型对象时,生成UUID字段:
public function boot()
{
parent::boot();
self::creating(function($model){
$model->uuid = (string) Uuid::generate();
});
}
2
3
4
5
6
7
# 4. 带条件与排序的关联关系
定义关联关系的一般方式:
public function users()
{
return $this->hasMany('App\User');
}
2
3
4
进一步,也可以在上面的基础上增加where
或者 orderBy
.
// 如果你想关联某些类型的用户,同时使用email字段排序,可以这样实现
public function approvedUsers()
{
return $this->hasMany('App\User')->where('approved', 1)->orderBy('email');
}
2
3
4
5
# 5. 模型特性:时间、追加等
最常用的Elequent模型参数、属性形式:
class User extends Model
{
protected $table = 'users'; // 绑定 数据表
protected $fillable = ['email', 'password']; //允许批量赋值的字段,User::create()
protected $dates = ['created_at', 'updated_at']; //装换为Carbon()实例
protected $appends = ['field1', 'field2']; // 返回实例时,附加的字段
protected $casts = [
'is_admin' => 'boolean', // 将is_admin 装换成boolean类型数据
'options' => 'array' // JSON 与数组之间类型 转换
];
}
2
3
4
5
6
7
8
9
10
11
还有更多的:
protected $primaryKey = 'uuid'; // 更换主键
public $incrementing = false; // 设置不增长
protected $perPage = 25; // 定义分页每页显示数量,(默认15条)
const CREATED_AT = 'created_at'; // 重写时间字段名
const UPDATED_AR = 'updated_at'; // 重写时间字段名
public $timestamps = false; // 设置不维护时间字段
2
3
4
5
6
# 6. 通过ID 查询多条记录
// find()方法
$user =User::find(1);
// 优化:可以接受多个ID作为数组传递参数
$user = User::find([1, 2, 3]);
2
3
4
5
# 7. WhereX
//正常的条件查询
$users = User::where('approved',1)->get();
//优化:使用字段名作为后缀添加到 where 后面,它就通过魔术方法运行了。
$users = User::whereApproved(1)->get();
//在Elequent里也有和时间相关的预定义方法
User::whereData('created_at', date('Y-m-d'));
User::whereData('created_at', date('d'));
User::whereData('created_at', date('m'));
User::whereData('created_at', date('Y'));
2
3
4
5
6
7
8
9
10
11
12
# 8. 通过关系排序
一个复杂一点的「技巧」:对论坛话题按照最新发布的帖子来排序
// 1. 定义一个单独的关联
public function lastetPost()
{
return $this->hasOne(\App\Post::class)->latest();
}
// 2. 在控制器中实现这个「魔法」
$users = Topic::with('latestPost')->get()->sortByDesc('latestPost.created_at');
2
3
4
5
6
7
8
# 9. Elequent::when() — 不再使用 if - else
很多人喜欢用 if else 来写查询条件:
// 很多人喜欢用 if else 来写查询条件
if(request('fliter_by') == 'likes'){
$query->where('likes', '>', request('likes_amount', 0));
}
if(request('fliter_by') == 'date'){
$query->orderBy('created_at', request('order_rule', 'desc'));
}
2
3
4
5
6
7
有更好的一种方法:使用 when()
// 有更好的一种方法:使用 when()
$query = Author::query();
$query->when(request('fliter_by') == 'likes', function($q){
return $q->where('likes', '>', request('likes_amount', 0));
});
$query->when(request('fliter_by') == 'date', function($q){
return $q->where('created_at', request('ordering_rule', 'desc'));
});
2
3
4
5
6
7
8
虽然看上去不是很优雅,但强大的功能是传递参数:
// 虽然看上去不是很优雅,但强大的功能是传递参数
$query = User::query();
$query->when(request('role', false), function($q, $role){
return $q->where('role_id', $role);
});
$authors = $query->get();
2
3
4
5
6
# 10. BelongsTo 默认模型
假如有一个 Post 模型属于 Author 模型,在 Blade 模板里可以这样写:
{{ $post->author->name }}
但是如果作者被删除了,或者因为某些原因未设置?你就会得到一个错误消息,类似「不存在的对象属性」。
那么,在 Blade 里可以这么避免:
{{ $post->author->name ?? '' }}
但是可以在 Elequent 关系模型级别做到这种效果:
public function author()
{
// 如果帖子没有作者的话,author() 关系方法会返回一个空的 App\User 模型对象。
return $this->belongsTo('App\Author')->withDefault();
}
// 也可以给默认模型分配一个默认的属性值
public function author()
{
return $this->belongsTo('App\Author')->withDefault([
'name' => 'guest author'
]);
}
2
3
4
5
6
7
8
9
10
11
12
13
# 11. 通过赋值函数进行排序
有如下代码:
function getFullNameAttribute()
{
return $this->attributes['first_name'] . ' ' . $this->attributes['last_name'];
}
2
3
4
现在,通过full_name 进行排序是没有效果的:
$clients = Client::orderBy('full_name')->get(); // 没有效果
解决办法:我们需要在获取结果后对结果进行再排序 sortBy()
$clients = Client::get()->sortBy('full_name'); // ok!
注意: 排序的方法名称是不相同的, 不是
orderBy()
而是sortBy()
# 12. 全局作用域下的默认排序
场景: 如果要
User::all()
总是按照name
字段来排序,可以给它分配一个全局作用域。在
boot()
方法里声明:
protected static function boot()
{
parent::boot();
// 按照 name 正序排序
static::addGlobalScope('order', function(Builder $builder){
$builder->orderBy('name', 'asc');
});
}
2
3
4
5
6
7
8
9
# 13. 原生查询方法
// whereRaw
$orders = DB::table('orders')->whereRaw('price'>IF(state="TX",?,100),[200])->get();
//havingRaw
Product::groupBy('catagory_id')->havingRaw('COUNT(*) > 1')->get();
//orderByRaw
User::where('created_at', '>', '2016-01-01')
->orderByRaw('(updated_at-created_at) desc')
->get();
2
3
4
5
6
7
8
9
10
# 14. 复制:复制一行的副本
下面是复制数据库实体(一条数据)的最佳方法:
$task = Task::find(1);
$newTask = $task->replicate();
$newTask->save();
2
3
# 15. Chunk() 方法之大块数据
可以使用chunk()
方法将这些大数据分割成小数据块,这对于处理大的数据集合,很有用处,节省内存。
// 修改前
$users = User::all();
foreach($users as $user){
// ...
}
// 使用 chunk() 后
User::chunk(100, function($users){
foreach($users as $user){
// ...
}
});
2
3
4
5
6
7
8
9
10
11
12
# 16. 创建模型的时候增加额外操作
创建 model 的 Artisan 命令:
php artisan make:model Company
可以在创建 model 文件时带上三个十分有用的标识符用于生成模型相关文件
php artisan make:model Company -mcr
- -m 创建迁移文件
- -c 创建控制器文件
- -r 为控制器添加资源操作方法
# 17 调用 save 方法的时候指定 updated_at
->save()
方法可以接受参数,我们可以通过传入参数阻止默认行为:更新updated_at
字段为当前时间戳
$product = Product::find($id);
$product->updated_at = '2019-01-01 00:00:00';
// 阻止时间字段 默认设置为当前时间戳,成功插入 updated_at 为我们自定义的值
$product->save(['timestamps'=>false]);
2
3
4
5
# 18. update() 的结果是什么?
$result = $product->whereNull('catagory_id')->update(['category_id'=>2]);
上面这段代码的意思是把
category_id
为null
的 product 的`category_id更新为2.但是
$result
会包含什么?答案是受影响的行。
因此,想检查多少行受影响,不需要再额外调用其他任何内容—
update()
方法会返回此数字。
# 19. 把括号转换成 Elequent 查询
and
和 or
混合 的 SQL 查询:
... where (gender = 'male' and age>=18) or (gender = 'female' and age>= 65)
使用Elequent 来翻译它,正确的方式有点复杂,使用闭包作为子查询:
$q->where(function($query){
$query->where('gender', 'male')->where('age', '>=', 18);
})->orWhere(function($query{
$query->where('gender', 'female')->where('age', '>=', 65);
}))
2
3
4
5
# 20. 复数参数的orWhere
$q->where('a', 1);
$q->orWhere('b', 2);
$q->orWhere('c', 3);
// 复数形式 传参到 orWhere
$q->where('a', 1);
$q->orWhere(['b'=>2, 'c'=> 3]);
2
3
4
5
6
7