(转) Laravel Eloquent 提示和技巧

原文:https://learnku.com/articles/19876#1face4
Eloquent ORM 看起来像一个简单的机制,但在幕后,有很多半隐藏的功能和不太知名的方法来实现更多。在本文中,我将向您展示一些技巧。

1. Increments and Decrements

文章阅读量增加 1:

$article = Article::find($articleid);
$article->readcount++;
$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. XorY methods

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

  1. findOrFail() :

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

    等价于:

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

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

    等价于:

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

3. 模型 boot() 方法

在Eloquent模型中有一个名为boot()的方法,您可以在其中覆盖默认行为:

 class User extends Model
 {
     public static function boot()
     {
         parent::boot();
         static::updating(function ($model) 
         {
             // do some logging
         });
     }
 }

可能最常见的例子之一是在创建模型对象时设置一些字段值。假设你想在那一刻生成UUID字段。

 public static function boot()
 {
     parent::boot();
     static::creating(function ($model) 
     {
         $model->uuid = (string)Uuid::generate();
     });
 }

4. Relationship with conditions and ordering

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

 public function users()
 {
    retrun $this->hasMany('App\User');
 }

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

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

5. 模型属性:timestamps, appends等。

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

image.png

更多请查看默认abstract Model class 的代码,并查看所有使用的特征。

6. find()

大家都知道 **find() **方法可以这样用:

$user = User::find(1);

其实 find() 还可以传递一个数组作为参数:

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

7. whereX

有一种优雅的方式可以解决这个问题:

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

等价于:

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

8. Order by relationship

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

首先,描述关于该主题的最新帖子的单独关系:

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

然后,在我们的控制器中,我们可以这样做:

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

9. Eloquent::when() – no more if-else’s

我们中的许多人用 “ 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) use ($role) { 
    return $q->where('role_id', $role);
});

$authors = $query->get();

10. BelongsTo Default Models

假设你有Post属于Author,然后是Blade代码:

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

但是如果作者被删除,或者由于某种原因没有设置呢?您将收到错误,例如“property of non-object”。
当然,您可以像这样阻止它:

{{ $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. 赋值函数排序

假设有这么一段代码:

public function getFullNameAttribute()
{
    return $this->attributes['first_name'].' '.$this->attributes['last_name'];
}

如果你想按照 full_name 进行排序,下面这句代码将不起作用:

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

解决办法很简单,我们只需要在获取集合之后利用 sortBy 对集合进行排序即可:

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

12. 全局范围内默认排序

如果你希望所有用户总是按照 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', '>', '2018-11-11')
        ->orderByRaw('(updated_at - created_at) desc')
        ->get();

14. Replicate: 制作一行的副本

制作数据库条目副本的最佳方法:

  $task = Task::find(1);
  $newTask = $task->replicate();
  $newTask->save();

15. chunk() 方法批量处理大数据量

不完全与Eloquent相关,它更多关于Collection,但仍然很强大 - 处理更大的数据集,你可以将它们分成几块。
一般情况下数据量不太大的情况下会像下面这样遍历

  $users = User::all();
  foreach($users as $user) {
      // ...
  }

数据太大就能显示 chunk() 的神威了

 User::chunk(100, function ($users) {
     foreach($users as $user) {
         //...
     }
 });

16. 命令行创建模型的同时,创建迁移文件和控制器

laravel创建模型的命令大家都很熟悉:

php artisan make:model Company

不过你应该了解另外几个很常用的参数:

php artisan make:model Company -m
php artisan make:model Company -mc
php artisan make:model Company -mcr
php artisan make:model Company -mcrf

-m 表示创建模型对应的迁移文件
-c 表示创建模型对应的控制器
-r 表示创建的控制器属于资源控制器
-f 表示创建模型对应的工厂文件

实际上上述几个情况,也可以通过 -a 来实现

php artisan make:model Company -a

17. 保存数据的同时 覆盖 updated_at 的默认更新时间

其实 ->save() 方法是可以接受额外参数的,因此,我们可以告诉它“忽略”updated_at默认功能以填充当前时间戳。

$product = Product::find(1);
$product->updated_at = '2018-11-11 11:11:11';
$product->save(['timestamps' => false]);

可以看到,我们用我们预先定义的版本覆盖默认的updated_at。

18. update() 方法的执行结果

你有没有想过这段代码究竟返回了什么?

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

更新是在数据库中执行的,但$ result会包含什么?
答案是受影响的行。因此,如果您需要检查受影响的行数,则无需再调用任何其他内容 - update()方法将为您返回此数字。

19. 将and 或者 or转换为Eloquent查询

在你的查询中肯定会遇到 and 或者 or 的情况,就像这样:

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

那么怎么转换成Eloquent查询呢?先来看一个错误的例子:

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

正确的方法有点复杂,使用闭包函数作为子查询:

$q->where(function ($query) {
    $query->where('gender', 'Male')->where('age', '>', 18);
})->orWhere(function ($query) {
    $query->where('gender', 'Female')->orWhere('age', '>=', 65);
})

20. orWhere() 有多个参数的情况

通常情况下遇到这种查询:

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

这种情况下可以传递一个数组作为 orWhere() 的参数:

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

推荐阅读更多精彩内容