20 个 Laravel的orm技巧和窍门(转)

2022年7月1日09:44:11

原文: https://laravel-news.com/eloquent-tips-tricks

Eloquent ORM 似乎是一个简单的机制,但在底层,有很多半隐藏的功能和鲜为人知的方法来实现更多功能。在本文中,我将向您展示一些技巧。

1. 递增和递减
而不是这个:

$article = Article::find($article_id);
$article->read_count++;
$article->save();
你可以这样做:

$article = Article::find($article_id);
$article->increment('read_count');
这些也将起作用:

Article::find($article_id)->increment('read_count');
Article::find($article_id)->increment('read_count', 10); // +10
Product::find($produce_id)->decrement('stock'); // -1
2.异或法
Eloquent 有很多功能结合了两种方法,比如“请做 X,否则做 Y”。

示例 1 – findOrFail():

代替:

$user = User::find($id);
if (!$user) { abort (404); }
做这个:

$user = User::findOrFail($id);
示例 2 – firstOrCreate():

代替:

$user = User::where('email', $email)->first();
if (!$user) {
  User::create([
    'email' => $email
  ]);
}
这样做:

$user = User::firstOrCreate(['email' => $email]);
3.模型boot()方法
在 Eloquent 模型中有一个神奇的地方boot()可以覆盖默认行为:

class User extends Model
{
    public static function boot()
    {
        parent::boot();
        static::updating(function($model)
        {
            // do some logging
            // override some property like $model->something = transform($something);
        });
    }
}
可能最流行的示例之一是在创建模型对象时设置一些字段值。假设您想在那一刻生成UUID 字段。

public static function boot()
{
  parent::boot();
  self::creating(function ($model) {
    $model->uuid = (string)Uuid::generate();
  });
}
4. 与条件和顺序的关系
这是定义关系的典型方式:

public function users() {
    return $this->hasMany('App\User');
}
但是您是否知道此时我们已经可以添加whereor orderBy?
例如,如果您想要某种类型的用户的特定关系,也通过电子邮件订购,您可以这样做:

public function approvedUsers() {
    return $this->hasMany('App\User')->where('approved', 1)->orderBy('email');
}
5. 模型属性:时间戳、追加等。
Eloquent 模型有一些“参数”,以该类的属性的形式。最受欢迎的可能是这些:

class User extends Model {
    protected $table = 'users';
    protected $fillable = ['email', 'password']; // which fields can be filled with User::create()
    protected $dates = ['created_at', 'deleted_at']; // which fields will be Carbon-ized
    protected $appends = ['field1', 'field2']; // additional values returned in JSON
}
但是等等,还有更多:

protected $primaryKey = 'uuid'; // it doesn't have to be "id"
public $incrementing = false; // and it doesn't even have to be auto-incrementing!
protected $perPage = 25; // Yes, you can override pagination count PER MODEL (default 15)
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at'; // Yes, even those names can be overridden
public $timestamps = false; // or even not used at all
还有更多,我列出了最有趣的,更多请查看默认抽象模型类的代码并查看所有使用的特征。

6.查找多个条目
方法大家都知道find()吧?

$user = User::find(1);
我很惊讶很少有人知道它可以接受多个 ID 作为数组:

$users = User::find([1,2,3]);
7. 哪里X
有一种优雅的方式来改变它:

$users = User::where('approved', 1)->get();
进入这个:

$users = User::whereApproved(1)->get();
是的,您可以更改任何字段的名称并将其作为后缀附加到“where”,它会神奇地起作用。

此外,Eloquent 中有一些与日期/时间相关的预定义方法:

User::whereDate('created_at', date('Y-m-d'));
User::whereDay('created_at', date('d'));
User::whereMonth('created_at', date('m'));
User::whereYear('created_at', date('Y'));
8. 按关系排序
一个更复杂的“技巧”。如果您有论坛主题但想通过最新帖子订购它们怎么办?论坛中很常见的要求,最后更新的主题在顶部,对吧?

首先,为有关该主题的最新帖子描述一个单独的关系:

public function latestPost()
{
    return $this->hasOne(\App\Post::class)->latest();
}
然后,在我们的控制器中,我们可以做这个“魔术”:

$users = Topic::with('latestPost')->get()->sortByDesc('latestPost.created_at');
9. Eloquent::when()——不再有 if-else
我们中的许多人使用“if-else”编写条件查询,如下所示:

if (request('filter_by') == 'likes') {
    $query->where('likes', '>', request('likes_amount', 0));
}
if (request('filter_by') == 'date') {
    $query->orderBy('created_at', request('ordering_rule', 'desc'));
}
但是有一个更好的方法——使用when():

$query = Author::query();
$query->when(request('filter_by') == 'likes', function ($q) {
    return $q->where('likes', '>', request('likes_amount', 0));
});
$query->when(request('filter_by') == 'date', function ($q) {
    return $q->orderBy('created_at', request('ordering_rule', 'desc'));
});
它可能不会感觉更短或更优雅,但最强大的是传递参数:

$query = User::query();
$query->when(request('role', false), function ($q, $role) {
    return $q->where('role_id', $role);
});
$authors = $query->get();
10. 属于默认模型
假设您有属于 Author 的 Post 和 Blade 代码:

{{ $post->author->name }}
但是如果作者被删除,或者由于某种原因没有设置怎么办?你会得到一个错误,比如“非对象的属性”。

当然,您可以像这样阻止它:

{{ $post->author->name ?? '' }}
但是你可以在 Eloquent 关系级别上做到这一点:

public function author()
{
    return $this->belongsTo('App\Author')->withDefault();
}
在此示例中,如果没有作者附加到帖子,则author()关系将返回一个空模型。App\Author

此外,我们可以将默认属性值分配给该默认模型。

public function author()
{
    return $this->belongsTo('App\Author')->withDefault([
        'name' => 'Guest Author'
    ]);
}
11. Mutator 排序
想象一下你有这个:

function getFullNameAttribute()
{
  return $this->attributes['first_name'] . ' ' . $this->attributes['last_name'];
}
现在,您想按那个顺序订购full_name吗?这不起作用:

$clients = Client::orderBy('full_name')->get(); // doesn't work
解决方案非常简单。我们需要在得到结果后对结果进行排序。

$clients = Client::get()->sortBy('full_name'); // works!
请注意,函数名称不同——它不是orderBy,而是sortBy。

12. 全局范围内的默认排序
如果您想User::all()始终按name字段排序怎么办?您可以分配一个全局范围。让我们回到boot()上面已经提到的方法。

protected static function boot()
{
    parent::boot();
    // Order by name ASC
    static::addGlobalScope('order', function (Builder $builder) {
        $builder->orderBy('name', 'asc');
    });
}
在此处阅读有关查询范围的更多信息。

13.原始查询方法
有时我们需要在 Eloquent 语句中添加原始查询。幸运的是,有一些功能。

// whereRaw
$orders = DB::table('orders')
    ->whereRaw('price > IF(state = "TX", ?, 100)', [200])
    ->get();
// havingRaw
Product::groupBy('category_id')->havingRaw('COUNT(*) > 1')->get();
// orderByRaw
User::where('created_at', '>', '2016-01-01')
  ->orderByRaw('(updated_at - created_at) desc')
  ->get();
14. 复制:复制一行
短一个。没有深入的解释,这是制作数据库条目副本的最佳方法:

$task = Tasks::find(1);
$newTask = $task->replicate();
$newTask->save();
15. 大表的 Chunk() 方法
与 Eloquent 不完全相关,它更多的是关于 Collection,但仍然很强大——要处理更大的数据集,你可以将它们分成几块。

代替:

$users = User::all();
foreach ($users as $user) {
    // ...
你可以做:

User::chunk(100, function ($users) {
    foreach ($users as $user) {
        // ...
    }
});
16. 创建模型时创建额外的东西
我们都知道这个 Artisan 命令:

php artisan make:model Company
但是您知道生成模型相关文件的三个有用标志吗?

php artisan make:model Company -mcr
-m 将创建一个迁移文件
-c 将创建一个控制器
-r 表示控制器应该是足智多谋的
17. 保存时覆盖updated_at
你知道->save()方法可以接受参数吗?因此,我们可以告诉它“忽略”updated_at要填充当前时间戳的默认功能。看到这个:

$product = Product::find($id);
$product->updated_at = '2019-01-01 10:00:00';
$product->save(['timestamps' => false]);
updated_at在这里,我们用我们预定义的覆盖默认值。

18. update() 的结果是什么?
你有没有想过这段代码实际上返回了什么?

$result = $products->whereNull('category_id')->update(['category_id' => 2]);
我的意思是,更新是在数据库中执行的,但它$result包含什么?

答案是受影响的行。因此,如果您需要检查有多少行受到影响,则无需调用任何其他方法 -update()方法将为您返回此数字。

19. 将括号转换为 Eloquent 查询
如果您在 SQL 查询中有和/或混合,会怎样,如下所示:

... WHERE (gender = 'Male' and age >= 18) or (gender = 'Female' and age >= 65)
如何将其翻译成 Eloquent?这是错误的方法:

$q->where('gender', 'Male');
$q->orWhere('age', '>=', 18);
$q->where('gender', 'Female');
$q->orWhere('age', '>=', 65);
顺序将不正确。正确的方法稍微复杂一些,使用闭包函数作为子查询:

$q->where(function ($query) {
    $query->where('gender', 'Male')
        ->where('age', '>=', 18);
})->orWhere(function($query) {
    $query->where('gender', 'Female')
        ->where('age', '>=', 65);
})
20. orWhere 有多个参数
最后,您可以将一组参数传递给orWhere().
“常规”方式:

$q->where('a', 1);
$q->orWhere('b', 2);
$q->orWhere('c', 3);
你可以这样做:

$q->where('a', 1);
$q->orWhere(['b' => 2, 'c' => 3]);
如

Eloquent ORM 似乎是一个简单的机制,但在底层,有很多半隐藏的功能和鲜为人知的方法来实现更多功能。在本文中,我将向您展示一些技巧。

1. 递增和递减

而不是这个:

1 $article = Article::find($article_id);
2 $article->read_count++;
3 $article->save();

你可以这样做:

1 $article = Article::find($article_id);
2 $article->increment('read_count');

这些也将起作用:

1 Article::find($article_id)->increment('read_count');
2 Article::find($article_id)->increment('read_count', 10); // +10
3 Product::find($produce_id)->decrement('stock'); // -1

2.异或法

Eloquent 有很多功能结合了两种方法,比如“请做 X,否则做 Y”。

示例 1 – findOrFail()

代替:

1 $user = User::find($id);
2 if (!$user) { abort (404); }

做这个:

1 $user = User::findOrFail($id);

示例 2 – firstOrCreate()

代替:

1 $user = User::where('email', $email)->first();
2 if (!$user) {
3 User::create([
4 'email' => $email
5 ]);
6 }

这样做:

1 $user = User::firstOrCreate(['email' => $email]);

3.模型boot()方法

在 Eloquent 模型中有一个神奇的地方boot()可以覆盖默认行为:

1 class User extends Model
2 {
3 public static function boot()
4 {
5 parent::boot();
6 static::updating(function($model)
7 {
8 // do some logging
9 // override some property like $model->something = transform($something);
10 });
11 }
12 }

可能最流行的示例之一是在创建模型对象时设置一些字段值。假设您想在那一刻生成UUID 字段

1 public static function boot()
2 {
3 parent::boot();
4 self::creating(function ($model) {
5 $model->uuid = (string)Uuid::generate();
6 });
7 }

4. 与条件和顺序的关系

这是定义关系的典型方式:

1 public function users() {
2 return $this->hasMany('App\User');
3 }

但是您是否知道此时我们已经可以添加whereor orderBy?例如,如果您想要某种类型的用户的特定关系,也通过电子邮件订购,您可以这样做:

1 public function approvedUsers() {
2 return $this->hasMany('App\User')->where('approved', 1)->orderBy('email');
3 }

5. 模型属性:时间戳、追加等。

Eloquent 模型有一些“参数”,以该类的属性的形式。最受欢迎的可能是这些:

1 class User extends Model {
2 protected $table = 'users';
3 protected $fillable = ['email', 'password']; // which fields can be filled with User::create()
4 protected $dates = ['created_at', 'deleted_at']; // which fields will be Carbon-ized
5 protected $appends = ['field1', 'field2']; // additional values returned in JSON
6 }

但是等等,还有更多:

1 protected $primaryKey = 'uuid'; // it doesn't have to be "id"
2 public $incrementing = false; // and it doesn't even have to be auto-incrementing!
3 protected $perPage = 25; // Yes, you can override pagination count PER MODEL (default 15)
4 const CREATED_AT = 'created_at';
5 const UPDATED_AT = 'updated_at'; // Yes, even those names can be overridden
6 public $timestamps = false; // or even not used at all

还有更多,我列出了最有趣的,更多请查看默认抽象模型类的代码并查看所有使用的特征。


6.查找多个条目

方法大家都知道find()吧?

1 $user = User::find(1);

我很惊讶很少有人知道它可以接受多个 ID 作为数组:

1 $users = User::find([1,2,3]);

7. 哪里X

有一种优雅的方式来改变它:

1 $users = User::where('approved', 1)->get();

进入这个:

1 $users = User::whereApproved(1)->get();

是的,您可以更改任何字段的名称并将其作为后缀附加到“where”,它会神奇地起作用。

此外,Eloquent 中有一些与日期/时间相关的预定义方法:

1 User::whereDate('created_at', date('Y-m-d'));
2 User::whereDay('created_at', date('d'));
3 User::whereMonth('created_at', date('m'));
4 User::whereYear('created_at', date('Y'));

8. 按关系排序

一个更复杂的“技巧”。如果您有论坛主题但想通过最新帖子订购它们怎么办?论坛中很常见的要求,最后更新的主题在顶部,对吧?

首先,为有关该主题的最新帖子描述一个单独的关系:

1 public function latestPost()
2 {
3 return $this->hasOne(\App\Post::class)->latest();
4 }

然后,在我们的控制器中,我们可以做这个“魔术”:

1 $users = Topic::with('latestPost')->get()->sortByDesc('latestPost.created_at');

9. Eloquent::when()——不再有 if-else

我们中的许多人使用“if-else”编写条件查询,如下所示:

1 if (request('filter_by') == 'likes') {
2 $query->where('likes', '>', request('likes_amount', 0));
3 }
4 if (request('filter_by') == 'date') {
5 $query->orderBy('created_at', request('ordering_rule', 'desc'));
6 }

但是有一个更好的方法——使用when()

1 $query = Author::query();
2 $query->when(request('filter_by') == 'likes', function ($q) {
3 return $q->where('likes', '>', request('likes_amount', 0));
4 });
5 $query->when(request('filter_by') == 'date', function ($q) {
6 return $q->orderBy('created_at', request('ordering_rule', 'desc'));
7 });

它可能不会感觉更短或更优雅,但最强大的是传递参数:

1 $query = User::query();
2 $query->when(request('role', false), function ($q, $role) {
3 return $q->where('role_id', $role);
4 });
5 $authors = $query->get();

10. 属于默认模型

假设您有属于 Author 的 Post 和 Blade 代码:

1 {{ $post->author->name }}

但是如果作者被删除,或者由于某种原因没有设置怎么办?你会得到一个错误,比如“非对象的属性”。

当然,您可以像这样阻止它:

1 {{ $post->author->name ?? '' }}

但是你可以在 Eloquent 关系级别上做到这一点:

1 public function author()
2 {
3 return $this->belongsTo('App\Author')->withDefault();
4 }

在此示例中,如果没有作者附加到帖子,则author()关系将返回一个空模型。App\Author

此外,我们可以将默认属性值分配给该默认模型。

1 public function author()
2 {
3 return $this->belongsTo('App\Author')->withDefault([
4 'name' => 'Guest Author'
5 ]);
6 }

11. Mutator 排序

想象一下你有这个:

1 function getFullNameAttribute()
2 {
3 return $this->attributes['first_name'] . ' ' . $this->attributes['last_name'];
4 }

现在,您想按那个顺序订购full_name吗?这不起作用:

1 $clients = Client::orderBy('full_name')->get(); // doesn't work

解决方案非常简单。我们需要在得到结果后对结果进行排序。

1 $clients = Client::get()->sortBy('full_name'); // works!

请注意,函数名称不同——它不是orderBy,而是sortBy。


12. 全局范围内的默认排序

如果您想User::all()始终按name字段排序怎么办?您可以分配一个全局范围。让我们回到boot()上面已经提到的方法。

1 protected static function boot()
2 {
3 parent::boot();
4
5 // Order by name ASC
6 static::addGlobalScope('order', function (Builder $builder) {
7 $builder->orderBy('name', 'asc');
8 });
9 }

在此处阅读有关查询范围的更多信息。


13.原始查询方法

有时我们需要在 Eloquent 语句中添加原始查询。幸运的是,有一些功能。

1 // whereRaw
2 $orders = DB::table('orders')
3 ->whereRaw('price > IF(state = "TX", ?, 100)', [200])
4 ->get();
5
6 // havingRaw
7 Product::groupBy('category_id')->havingRaw('COUNT(*) > 1')->get();
8
9 // orderByRaw
10 User::where('created_at', '>', '2016-01-01')
11 ->orderByRaw('(updated_at - created_at) desc')
12 ->get();

14. 复制:复制一行

短一个。没有深入的解释,这是制作数据库条目副本的最佳方法:

1 $task = Tasks::find(1);
2 $newTask = $task->replicate();
3 $newTask->save();

15. 大表的 Chunk() 方法

与 Eloquent 不完全相关,它更多的是关于 Collection,但仍然很强大——要处理更大的数据集,你可以将它们分成几块。

代替:

1 $users = User::all();
2 foreach ($users as $user) {
3 // ...

你可以做:

1 User::chunk(100, function ($users) {
2 foreach ($users as $user) {
3 // ...
4 }
5 });

16. 创建模型时创建额外的东西

我们都知道这个 Artisan 命令:

1 php artisan make:model Company

但是您知道生成模型相关文件的三个有用标志吗?

1 php artisan make:model Company -mcr
  • -m 将创建一个迁移文件
  • -c 将创建一个控制器
  • -r 表示控制器应该是足智多谋的

17. 保存时覆盖updated_at

你知道->save()方法可以接受参数吗?因此,我们可以告诉它“忽略”updated_at要填充当前时间戳的默认功能。看到这个:

1 $product = Product::find($id);
2 $product->updated_at = '2019-01-01 10:00:00';
3 $product->save(['timestamps' => false]);

updated_at在这里,我们用我们预定义的覆盖默认值。


18. update() 的结果是什么?

你有没有想过这段代码实际上返回了什么?

1 $result = $products->whereNull('category_id')->update(['category_id' => 2]);

我的意思是,更新是在数据库中执行的,但它$result包含什么?

答案是受影响的行。因此,如果您需要检查有多少行受到影响,则无需调用任何其他方法 -update()方法将为您返回此数字。


19. 将括号转换为 Eloquent 查询

如果您在 SQL 查询中有和/或混合,会怎样,如下所示:

1 ... WHERE (gender = 'Male' and age >= 18) or (gender = 'Female' and age >= 65)

如何将其翻译成 Eloquent?这是错误的方法:

1 $q->where('gender', 'Male');
2 $q->orWhere('age', '>=', 18);
3 $q->where('gender', 'Female');
4 $q->orWhere('age', '>=', 65);

顺序将不正确。正确的方法稍微复杂一些,使用闭包函数作为子查询:

1 $q->where(function ($query) {
2 $query->where('gender', 'Male')
3 ->where('age', '>=', 18);
4 })->orWhere(function($query) {
5 $query->where('gender', 'Female')
6 ->where('age', '>=', 65);
7 })

20. orWhere 有多个参数

最后,您可以将一组参数传递给orWhere().“常规”方式:

1 $q->where('a', 1);
2 $q->orWhere('b', 2);
3 $q->orWhere('c', 3);

你可以这样做:

1 $q->where('a', 1);
2 $q->orWhere(['b' => 2, 'c' => 3]);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值