laravel一些小技巧

2022年7月4日09:59:33

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]);

21.数据库定义连接
在您的数据库配置文件中 - 可能app/config/database.php- 您可以定义多个任何类型的数据库连接。
事实上,您可以定义任意数量的连接。例如,如果您的应用程序必须从 2 个 MySQL 数据库中提取数据,您可以分别定义它们:
<?php
return array(
    'default' => 'mysql',
    'connections' => array(
        # Our primary database connection
        'mysql' => array(
            'driver'    => 'mysql',
            'host'      => 'host1',
            'database'  => 'database1',
            'username'  => 'user1',
            'password'  => 'pass1'
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
        ),

        # Our secondary database connection
        'mysql2' => array(
            'driver'    => 'mysql',
            'host'      => 'host2',
            'database'  => 'database2',
            'username'  => 'user2',
            'password'  => 'pass2'
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
        ),
    ),
);

我们的默认连接仍设置为mysql。这意味着,除非我们另外指定,否则应用程序将使用该mysql连接。

指定连接
现在我们有了第二个数据库连接设置——我们如何在代码中使用它?
原来有几种方法!

架构
在 Schema Builder 中,您可以将Schema 外观与任何连接一起使用。要指定要使用的连接,只需运行以下connection()方法:
Schema::connection('mysql2')->create('some_table', function($table)
{
    $table->increments('id'):
});

询问与 Schema Builder 类似,您可以在 Query Builder 上定义连接:
$users = DB::connection('mysql2')->select(...);

orm
您还可以定义在 Eloquent 模型中使用的连接!
一种方法是在模型中设置$connection变量:
<?php
class SomeModel extends Eloquent {

    protected $connection = 'mysql2';
}

您还可以通过setConnection方法在运行时定义连接。

<?php

class SomeController extends BaseController {

    public function someMethod()
    {
        $someModel = new SomeModel;

        $someModel->setConnection('mysql2');

        $something = $someModel->find(1);

        return $something;
    }

}
尝试与跨数据库的表建立关系时要小心!可以这样做,但它可能带有一些警告,并且取决于您拥有的数据库和/或数据库设置。

22,海量数据应用上的某些限制 1390 Prepared statement contains too many placeholders 
 /*
         * 只跑没有邀请关系的表的用户
         * 1390 Prepared statement contains too many placeholders
         * 联表查询的时候,注意这里因为占位符太多导致的,分批量查询
         */
        $u = UUser::with('UUserInvitRelationship')->select(['id', 'phone', 'invite_people_id'])->get()->toArray();
        if (!empty($u)) {
            foreach ($u as $k => $v) {
                if (!empty($v['u_user_invit_relationship'])) {
                    continue;
                }
                $p = new PullNewRelationshipService();
                $p->runUserInvitRelationship($v);
            }
        }
这里注意 Prepared statement 最多支持65535个占位变量,所以在大量数据的时候,如果需要做数据批量执行,就分块,或者分批,或者更换检索条件

23,laravrl  常用扩展包
扩展包	一句话描述	本项目应用场景
Intervention/image	图片处理功能库	用于图片裁切
guzzlehttp/guzzle	HTTP 请求套件	请求百度翻译 API
predis/predis	Redis 官方首推的 PHP 客户端开发包	缓存驱动 Redis 基础扩展包
barryvdh/laravel-debugbar	页面调试工具栏 (对 phpdebugbar 的封装)	开发环境中的 DEBUG
spatie/laravel-permission	角色权限管理	角色和权限控制
mewebstudio/Purifier	用户提交的 Html 白名单过滤	帖子内容的 Html 安全过滤,防止 XSS 攻击
hieu-le/active	选中状态	顶部导航栏选中状态
summerblue/administrator	管理后台	模型管理后台、配置信息管理后台
viacreative/sudo-su	用户切换	开发环境中快速切换登录账号
laravel/horizon	队列监控	队列监控命令与页面控制台 /horizon

24,laravel中 name方法的使用
laravel提供了很多magic方法来,先来讲讲命名路由的使用,有两种方法非常便捷。

命名路由让你可以更方便的为特定路由生成 URL 或进行重定向。你可以使用 as 数组键指定名称到路由上

1、第一种:通过route路由中的as关键字来实现
Route::get('api/user',['as'='web.user'],'messageController@userInformation');

2、第二种:通过Route的magic方法name来实现命名路由
Route::get('api/user','messageController@userInformation')->name('web.user');


25、如何使用
在代码中可以这样使用
this->visit(route('web.user'))
在模板中这样使用
<a href="{{route('web.user')}}" rel="external nofollow" >user</a>
laravel配置数据里有特殊字符的时候
.env文件里密码设置加上双引号变可解决
laravel常用细节总结

26. 在 find 方法中指定属性
User::find(1, ['name', 'email']);
User::findOrFail(1, ['name', 'email']);

27. Clone 一个 Model
用 replicate 方法可以克隆一个 Model

$user = User::find(1);
$newUser = $user->replicate();
$newUser->save();

28. 判断两个 Model 是否相同
检查两个 Model 的ID是否相同用 is 方法
$user = User::find(1);
$sameUser = User::find(1);
$diffUser = User::find(2);
$user->is($sameUser); // true
$user->is($diffUser); // false;

29. 重新加载一个 Mode
$user = User::find(1);
$user->name; // 'Peter'
// 如果 name 更新过,比如由 peter 更新为 John
$user->refresh();
$user->name; // John

30. 加载新的 Model
$user = User::find(1);
$user->name; // 'Peter'
// 如果 name 更新过,比如由 peter 更新为 John
$user->refresh();
$user->name; // John

31. 更新带关联的 Model

在更新关联的时候,使用 push 方法可以更新所有 Model

class User extends Model
{
 public function phone()
 {
 return $this->hasOne('App\Phone');
 }
}

$user = User::first();
$user->name = "Peter";
$user->phone->number = '1234567890';
$user->save(); // 只更新 User Model
$user->push(); // 更新 User 和 Phone Model

32. 自定义软删除字段
Laravel 默认使用 deleted_at 作为软删除字段,我们通过以下方式将 deleted_at 改成 is_deleted

class User extends Model
{
 use SoftDeletes;
 * deleted_at 字段.
 *
 * @var string
 */
 const DELETED_AT = 'is_deleted';

}

或者使用访问器
class User extends Model
{
 use SoftDeletes;
 public function getDeletedAtColumn(){
 	return 'is_deleted';
 }
}

33. 查询 Model 更改的属性
$user = User::first();
$user->name; // John
$user->name = 'Peter';
$user->save();

dd($user->getChanges());

// 输出:
[
 'name' => 'John',
 'updated_at' => '...'
]

34. 查询 Model 是否已更改
$user = User::first();
$user->name;  // John
$user->isDirty(); // false 
$user->name = 'Peter'; 
$user->isDirty(); // true
$user->getDirty(); // ['name' => 'Peter']
$user->save();
$user->isDirty(); // false
getChanges() 与 getDirty() 的区别
getChanges() 方法用在 save() 方法之后输出结果集
getDirty() 方法用在 save() 方法之前输出结果集

35. 查询修改前的 Model 信息
$user = App\User::first();
$user->name;   //John
$user->name = "Peter";  //Peter
$user->getOriginal('name'); //John
$user->getOriginal();  //Original $user record

36,大批量假数据填充的正确方法
$users = factory(\App\Models\User::class)->times(1000)->make();
\App\Models\User::insert($users->toArray());

37,laravel框架中实现代码追踪(PHPstorm IDE)
composer require barryvdh/laravel-ide-helper

38,laravel debug包
composer require barryvdh/laravel-debugbar
5.5以上就不要任何配置,还有这个是只能在项目本身的页面有,纯API接口是没有的
composer require barryvdh/laravel-ide-helper

php artisan ide-helper:generate - 为 Facades 生成注释
php artisan ide-helper:models - 为数据模型生成注释
php artisan ide-helper:meta - 生成 PhpStorm Meta file

------------------------------------------------------------------------
https://learnku.com/docs/laravel-tips/8.x/eloquent-model/11336

复用或克隆 query ()
通常,我们需要从过滤后的查询中进行多次查询。所以,大多数时候我们使用 query() 方法,

让我们编写一个查询来获取今天创建的已激活和未激活的产品

$query = Product::query();

$today = request()->q_date ?? today();

if($today){
    $query->where('created_at', $today);
}

// 让我们获取已激活和未激活的产品

$active_products = $query->where('status', 1)->get(); // 这一行修改了 $query 对象变量

$inactive_products = $query->where('status', 0)->get(); // 所以这里我们不会找到任何未激活的产品
但是,在获得 $active products 后,$query 会被修改。$inactive_products 不会从 $query 中获取任何未激活产品,并且每次都返回空集合。因为,它尝试从 $active_products 中查找未激活产品($query 仅返回激活产品)。

为了解决这个问题,我们可以通过复用这个 $query 对象来进行多次查询。

因此,我们需要在执行任何 $query 修改操作之前克隆这个 $query。

$active_products = (clone $query)->where('status', 1)->get(); // 它不会修改 $query

$inactive_products = (clone $query)->where('status', 0)->get(); // 所以我们将从 $query 中获取未激活的产品
Eloquent where 日期方法
在 Eloquent 中,使用 whereDay()、whereMonth()、whereYear()、whereDate() 和 whereTime() 函数检查日期。

$products = Product::whereDate('created_at', '2018-01-31')->get();

$products = Product::whereMonth('created_at', '12')->get();

$products = Product::whereDay('created_at', '31')->get();

$products = Product::whereYear('created_at', date('Y'))->get();

$products = Product::whereTime('created_at', '=', '14:13:58')->get();
增量和减量
如果要增加数据库某个表中的某个列,只需使用 increment() 函数。你不仅可以增加 1,还可以增加一些数字,比如 50。

Post::find($post_id)->increment('view_count');

User::find($user_id)->increment('points', 50);
没有 timestamp 列
如果你的数据库表不包含 timestamp 字段 created_at 和 updated_at,你可以使用 $timestamps = false 属性指定 Eloquent 模型不使用它们。

class Company extends Model
{
    public $timestamps = false;
}
软删除:多行恢复
使用软删除时,您可以在一个句子中恢复多行。

Post::onlyTrashed()->where('author_id', 1)->restore();
模型 all:列
当调用 Eloquent 的 Model::all() 时,你可以指定要返回的列。

$users = User::all(['id', 'name', 'email']);
失败或不失败
除了 findOrFail() 之外,还有 Eloquent 方法 firstOrFail(),如果没有找到查询记录,它将返回 404 页。

$user = User::where('email', 'povilas@laraveldaily.com')->firstOrFail();
列名更改
在 Eloquent Query Builder 中,您可以指定「as」以返回具有不同名称的任何列,就像在普通 SQL 查询中一样。

$users = DB::table('users')->select('name', 'email as user_email')->get();
Map 查询结果
在 Eloquent 查询之后,您可以使用 Collections 中的 map() 函数来修改行。

$users = User::where('role_id', 1)->get()->map(function (User $user) {
    $user->some_column = some_function($user);
    return $user;
});
更改默认时间戳字段
如果您使用的是非 Laravel 数据库并且时间戳列的名称不同怎么办?也许,你有 create_time 和 update_time。 幸运的是,您也可以在模型中指定它们:

class Role extends Model
{
    const CREATED_AT = 'create_time';
    const UPDATED_AT = 'update_time';
}
按 created_at 快速排序
不要使用:

User::orderBy('created_at', 'desc')->get();
你可以做的更快:

User::latest()->get();
默认情况下,latest() 会按 created_at 降序排序。

有一个相反的方法 oldest(),它按 created_at 升序排序:

User::oldest()->get();
此外,您可以指定另一列进行排序。 例如,如果你想使用 updated_at,你可以这样做:

$lastUpdatedUser = User::latest('updated_at')->first();
创建记录时的自动列值
如果您想在创建记录时生成一些 DB 列值,请将其添加到模型的 boot() 方法中。

例如,如果您有一个字段 「position」,并且想要将下一个可用位置分配给新记录(例如 Country::max('position') + 1),请执行以下操作:

class Country extends Model {
    protected static function boot()
    {
        parent::boot();
        Country::creating(function($model) {
            $model->position = Country::max('position') + 1;
        });
    }
}
数据库原始查询计算运行得更快
使用类似 whereRaw() 方法的 SQL 原始查询,直接在查询中进行一些特定于数据库的计算,而不是在 Laravel 中,通常结果会更快。 例如,如果您想获得注册后 30 天以上仍处于活跃状态的用户,请使用以下代码:

User::where('active', 1)
->whereRaw('TIMESTAMPDIFF(DAY, created_at, updated_at) > ?', 30)
->get();
不止一个作用域
您可以在 Eloquent 中组合和链式查询作用域,在查询中使用多个作用域。

模型:

public function scopeActive($query) {
    return $query->where('active', 1);
}

public function scopeRegisteredWithinDays($query, $days) {
    return $query->where('created_at', '>=', now()->subDays($days));
}
控制器中使用:

$users = User::registeredWithinDays(30)->active()->get();
无需转换 Carbon
如果你使用 whereDate() 查询今日的记录,可以直接使用 Carbon 的 now() 方法,会自动转换为日期进行查询,无需指定 ->toDateString():

// 今日注册的用户
$todayUsers = User::whereDate('created_at', now()->toDateString())->get();
// 无需 toDateString() ,直接 now() 即可
$todayUsers = User::whereDate('created_at', now())->get();
由第一个单词分组
你可以对 Eloquent 结果进行条件分组,下面的示例是由用户名称的第一个单词进行分组:

$users = User::all()->groupBy(function($item) {
    return $item->name[0];
});
永不更新某个字段
如果有一个数据库字段你想只更新一次,可以使用 Eloquent 的修改器来实现:

class User extends Model
{
    public function setEmailAttribute($value)
    {
        if ($this->email) {
            return;
        }

        $this->attributes['email'] = $value;
    }
}
find () 查询多条数据
find() 不止可以查询一条数据,当传入多个 ID 的值会返回这些结果的集合:

// 返回 Eloquent Model
$user = User::find(1);
// 返回 Eloquent Collection
$users = User::find([1,2,3]);
感谢 @tahiriqbalnajam 提供

find () 限制字段
find() 可在查询多条的数据的情况下,指定只返回哪些字段:

// 会返回只包含 first_name 和 email 的 Eloquent 模型
$user = User::find(1, ['first_name', 'email']);
// 会返回只包含 first_name 和 email 两个字段的 Eloquent 集合
$users = User::find([1,2,3], ['first_name', 'email']);
感谢 @tahiriqbalnajam 提供

按键查找
您还可以使用 whereKey() 方法查找多条记录,该方法负责处理哪个字段正是您的主键(id 是默认值,但您可以在 Eloquent 模型中覆盖它):

$users = User::whereKey([1,2,3])->get();
使用 UUID 而不是自动递增
您不想在模型中使用自动递增 ID?

迁移:

Schema::create('users', function (Blueprint $table) {
    // $table->increments('id');
    $table->uuid('id')->unique();
});
模型:

class User extends Model
{
    public $incrementing = false;
    protected $keyType = 'string';

    protected static function boot()
    {
        parent::boot();

        User::creating(function ($model) {
            $model->setId();
        });
    }

    public function setId()
    {
        $this->attributes['id'] = Str::uuid();
    }
}
Laravel 中的子选择
从 Laravel 6 开始,您可以在 Eloquent 语句中使用 addSelect (),并对附加的列进行一些计算。

return Destination::addSelect(['last_flight' => Flight::select('name')
    ->whereColumn('destination_id', 'destinations.id')
    ->orderBy('arrived_at', 'desc')
    ->limit(1)
])->get();
隐藏一些列
在进行 Eloquent 查询时,如果您想在返回中隐藏特定字段,最快捷的方法之一是在集合结果上添加 ->makeHidden()。

$users = User::all()->makeHidden(['email_verified_at', 'deleted_at']);
确切的数据库错误
如果您想捕获 Eloquent Query 异常,请使用特定的 QueryException 代替默认的 Exception 类,您将能够获得错误的确切 SQL 代码。

try {
    // 一些 Eloquent/SQL 声明
} catch (\Illuminate\Database\QueryException $e) {
    if ($e->getCode() === '23000') { // integrity constraint violation
        return back()->withError('Invalid data');
    }
}
使用查询构造器查询软删除
不要忘记,当您使用 Eloquent 时会排除已软删除的条目,但如果您使用查询构造器,则不会起作用。

// 排除软删除条目
$users = User::all();

// 不排除软删除条目
$users = User::withTrashed()->get();

// 不排除软删除条目
$users = DB::table('users')->get();
SQL 声明
如果你需要执行一个简单的 SQL 查询,但没有得到任何结果 —— 比如改变数据库模式中的某些东西,只需执行 DB::statement()。

DB::statement('DROP TABLE users');
DB::statement('ALTER TABLE projects AUTO_INCREMENT=123');
使用数据库事务
如果您执行了两个数据库操作,第二个可能会出错,那么您应该回滚第一个,对吗?

为此,我建议使用 DB Transactions,它在 Laravel 中非常简单:

DB::transaction(function () {
    DB::table('users')->update(['votes' => 1]);

    DB::table('posts')->delete();
});
更新或创建
如果你需要检查记录是否存在,然后更新它,或者创建一个新记录,你可以用一句话来完成 - 使用 Eloquent updateOrCreate() 方法:

// 不要这样做
$flight = Flight::where('departure', 'Oakland')
    ->where('destination', 'San Diego')
    ->first();
if ($flight) {
    $flight->update(['price' => 99, 'discounted' => 1]);
} else {
    $flight = Flight::create([
        'departure' => 'Oakland',
        'destination' => 'San Diego',
        'price' => 99,
        'discounted' => 1
    ]);
}
// 一句话完成
$flight = Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99, 'discounted' => 1]
);
保存时移除缓存
感谢 @pratiksh404 提供

如果您有提供集合 posts 这样的缓存键,想在新增或更新时移除缓存键,可以在您的模型上调用静态的 saved 函数:

class Post extends Model
{
    // 存储或更新时移除缓存
    public static function boot()
    {
        parent::boot();
        static::saved(function () {
           Cache::forget('posts');
        });
    }
}
改变 Created_at 和 Updated_at 的时间格式
感谢 @syofyanzuhad 提供

想要改变 created_at 的格式,您可以在模型中添加一个方法,如下所示:

public function getCreatedAtFormattedAttribute()
{
   return $this->created_at->format('H:i d, M Y');
}
你可以在需要改变时间格式时使用 $entry->created_at_formatted ,它会返回 created_at 的属性如同 04:19 23, Aug 2020。

你也可以用同样的方法更改 updated_at:

public function getUpdatedAtFormattedAttribute()
{
   return $this->updated_at->format('H:i d, M Y');
}
在有需要的时候使用 $entry->updated_at_formatted。它会返回 updated_at 的属性如同: 04:19 23, Aug 2020 。

数组类型存储到 JSON 中
感谢 @pratiksh404 提供

如果你的输入字段有一个数组需要存储为 JSON 格式,你可以在模型中使用 $casts 属性。 这里的 images 是 JSON 属性。

protected $casts = [
    'images' => 'array',
];
这样你可以以 JSON 格式存储它,但当你从 DB 中读取时,它会以数组方式使用。

制作模型的副本
如果你有两个非常相似的模型(比如送货地址和账单地址),而且你想要复制其中一个作为另一个,你可以使用 replicate() 方法并更改一部分属性。

官方文档 的示例:

$shipping = Address::create([
    'type' => 'shipping',
    'line_1' => '123 Example Street',
    'city' => 'Victorville',
    'state' => 'CA',
    'postcode' => '90001',
]);

$billing = $shipping->replicate()->fill([
    'type' => 'billing'
]);

$billing->save();
减少内存
有时我们需要将大量的数据加载到内存中,比如:

$orders = Order::all();
但如果我们有非常庞大的数据库,这可能会很慢,因为 Laravel 会准备好模型类的对象。在这种情况下,Laravel 有一个很方便的函数 toBase()。

$orders = Order::toBase()->get();
//$orders 将包含 `Illuminate\Support\Collection` 与对象 `StdClass`
通过调用这个方法,它将从数据库中获取数据,但它不会准备模型类。同时,向 get() 方法传递一个字段数组通常是个好主意,这样可以防止从数据库中获取所有字段。

忽略 $fillable/$guarded 并强制查询
如果你创建了一个 Laravel 模板作为其他开发者的「启动器」, 并且你不能控制他们以后会在模型的 $fillable/$guarded 中填写什么,你可以使用 forceFill()

$team->update(['name' => $request->name])
如果 name 不在团队模型的 $fillable 中,怎么办?或者如果根本就没有 $fillable/$guarded, 怎么办?

$team->forceFill(['name' => $request->name])
这将忽略该查询的 $fillable 并强制执行。

3 层父子级结构
如果你有一个 3 层的父子结构,比如电子商店中的分类,你想显示第三层的产品数量,你可以使用 with('yyy.yyy'),然后添加 withCount() 作为条件

class HomeController extend Controller
{
    public function index()
    {
        $categories = Category::query()
            ->whereNull('category_id')
            ->with(['subcategories.subcategories' => function($query) {
                $query->withCount('products');
            }])->get();
    }
}
class Category extends Model
{
    public function subcategories()
    {
        return $this->hasMany(Category::class);
    }

    public function products()
    {
    return $this->hasMany(Product::class);
    }
}
<ul>
    @foreach($categories as $category)
        <li>
            {{ $category->name }}
            @if ($category->subcategories)
                <ul>
                @foreach($category->subcategories as $subcategory)
                    <li>
                        {{ $subcategory->name }}
                        @if ($subcategory->subcategories)
                            <ul>
                                @foreach ($subcategory->subcategories as $subcategory)
                                    <li>{{ $subcategory->name }} ({{ $subcategory->product_count }})</li>
                                @endforeach
                            </ul>
                        @endif
                    </li>
                @endforeach
                </ul>
            @endif
        </li>
    @endforeach           
</ul>
使用 find() 来搜索更多的记录
你不仅可以用 find() 来搜索单条记录,还可以用 IDs 的集合来搜索更多的记录,方法如下:

return Product::whereIn('id', $this->productIDs)->get();
你可以这样做:

return Product::find($this->productIDs)
失败时执行任何操作
当查询一条记录时,如果没有找到,你可能想执行一些操作。除了用 ->firstOrFail() 会抛出 404 之外,你可以在失败时执行任何操作,只需要使用 ->firstOr(function() { ... })

$model = Flight::where('legs', '>', 3)->firstOr(function () {
    // ...
})
检查记录是否存在或者显示 404
不要使用 find() ,然后再检查记录是否存在,使用 findOrFail() 。

$product = Product::find($id);
if (!$product) {
    abort(404);
}
$product->update($productDataArray);
更简单的方法:

$product = Product::findOrFail($id); // 查询不到时显示 404
$product->update($productDataArray);
条件语句为否时中止
可以使用 abort_if() 作为判断条件和抛出错误页面的快捷方式。

$product = Product::findOrFail($id);
if($product->user_id != auth()->user()->id){
    abort(403);
}
更简单的方法:

/* abort_if(CONDITION, ERROR_CODE) */
$product = Product::findOrFail($id);
abort_if ($product->user_id != auth()->user()->id, 403)
在删除模型之前执行任何额外的操作
感谢 @back2Lobby 提供

我们可以使用 Model::delete() 执行额外的操作来覆盖原本的删除方法

// App\Models\User.php

public function delete(){

    //执行你想要的额外操作

    //然后进行正常的删除
    Model::delete();
}
当你需要在保存数据到数据库时自动填充一个字段
当你需要在保存数据到数据库时自动填充一个字段 (例如: slug),使用模型观察者来代替重复编写代码

use Illuminate\Support\Str;

class Article extends Model
{
    ...
    protected static function boot()
    {
        parent:boot();

        static::saving(function ($model) {
            $model->slug = Str::slug($model->title);
        });
    }
}
感谢 @sky_0xs 提供

获取查询语句的额外信息
你可以使用 explain() 方法来获取查询语句的额外信息

Book::where('name', 'Ruskin Bond')->explain()->dd();
Illuminate\Support\Collection {#5344
    all: [
        {#15407
            +"id": 1,
            +"select_type": "SIMPLE",
            +"table": "books",
            +"partitions": null,
            +"type": "ALL",
            +"possible_keys": null,
            +"key": null,
            +"key_len": null,
            +"ref": null,
            +"rows": 9,
            +"filtered": 11.11111164093,
            +"Extra": "Using where",
        },
    ],
}
感谢 @amit_merchant 提供

在 Laravel 中使用 doesntExist () 方法
// 一个例子
if ( 0 === $model->where('status', 'pending')->count() ) {
}

// 我不关心它有多少数据只要它是0
// Laravel 的 exists() 方法会很清晰:
if ( ! $model->where('status', 'pending')->exists() ) {
}

// 但我发现上面这条语句中的!很容易被忽略。
// 那么 doesntExist() 方法会让这个例子更加清晰
if ( $model->where('status', 'pending')->doesntExist() ) {
}
感谢 @ShawnHooper 提供

想要在一些模型的 boot () 方法中自动调用一个特性
如果你有一个特性,你想把它添加到几个模型中,自动调用它们的 boot() 方法,你可以把特质的方法作为 boot (特性名称)来调用

class Transaction extends  Model
{
    use MultiTenantModelTrait;
}
class Task extends  Model
{
    use MultiTenantModelTrait;
}
trait MultiTenantModelTrait
{
    // 这个方法名是 boot[特性名称]
    // 它将作为事务/任务的 boot() 被自动调用。
    public static function bootMultiTenantModelTrait()
    {
        static::creating(function ($model) {
            if (!$isAdmin) {
                $isAdmin->created_by_id = auth()->id();
            }
        })
    }
}
Laravel 的 find () 方法,比只传一个 ID 更多的选择
// 在 find($id) 方法中第二个参数可以是返回字段
Studdents::find(1, ['name', 'father_name']);
// 这样我们可以查询 ID 为 '1' 并返回 name , father_name 字段

// 我们可以用数组的方式传递更多的 ID
Studdents::find([1,2,3], ['name', 'father_name']);
// 输出: ID 为 1,2,3 并返回他们的 name , father_name 字段
在 Laravel 中,有两种常见的方法来确定一个表是否为空表
在 Laravel 中,有两种常见的方法来确定一个表是否为空表。 直接在模型上使用 exists() 或者 count()!
一个返回严格的布尔值,另一个返回一个整数,你都可以在条件语句中使用。

public function index()
{
    if (\App\Models\User::exists()) {
        // 如果表有任何保存的数据,则返回布尔值 true 或 false。
    }

    if (\App\Models\User::count()) {
        // 返回表中数据的数量
    }
}
感谢 @aschmelyun 提供

如何防止 property of non-object 错误
// 设定默认模型
// 假设你有一篇 Post (帖子) 属于一个 Author (作者),代码如下:
$post->author->name;

// 当然你可以像这样阻止错误:
$post->author->name ?? ''
// 或者
@$post->auhtor->name

// 但你可以在Eloquent关系层面上做到这一点。
// 如果没有作者关联帖子,这种关系将返回一个空的App/Author模型。
public function author() {
    return $this->belongsTo('App\Author')->withDefaults();
}
// 或者
public function author() {
    return $this->belongsTo('App\Author')->withDefault([
        'name' => 'Guest Author'
    ]);
}
感谢 @coderahuljat 提交

Eloquent 数据改变后获取原始数据
Eloquent 模型数据改变后,你可以使用 getOriginal () 方法来获取原始数据

$user = App\User::first();
$user->name; // John
$user->name = "Peter"; // Peter
$user->getOriginal('name'); // John
$user->getOriginal(); // 原始的 $user 记录
感谢 @devThaer 提交

一种更简单创建数据库的方法
Laravel 还可以使用 .sql 文件来更简单的创建数据库

DB::unprepared(
    file_get_contents(__DIR__ . './dump.sql')
);


为了补充已经很强大的 Collection 类, LazyCollection 类利用了 PHP 的 generators 来允许你在保持低内存使用率的同时使用非常大的数据集。

例如,假设你的应用需要处理一个千兆字节的日志文件,同时利用 Laravel 的集合方法来解析这个日志文件。与其一次将整个文件读入内存,不如使用懒集合在给定时间仅将文件的一小部分保留在内存中:

use App\Models\LogEntry;
use Illuminate\Support\LazyCollection;

LazyCollection::make(function () {
    $handle = fopen('log.txt', 'r');

    while (($line = fgets($handle)) !== false) {
        yield $line;
    }
})->chunk(4)->map(function ($lines) {
    return LogEntry::fromLines($lines);
})->each(function (LogEntry $logEntry) {
    // Process the log entry...
});
或者,假设你需要遍历 10000 个 Eloquent 模型。当使用传统的 Laravel 集合时,全部 10000 个 Eloquent 模型必须同时加载到内存中:

$users = App\Models\User::all()->filter(function ($user) {
    return $user->id > 500;
});
然而,查询构建器的 cursor 方法返回一个 LazyCollection 实例。这允许你对数据库只进行一次查询, 而且一次只能在内存中加载一个 Eloquent 模型。在此例中, filter 回调只有在我们实际逐个迭代每个用户之后才会执行,这样可以大幅度减少内存使用:

$users = App\Models\User::cursor()->filter(function ($user) {
    return $user->id > 500;
});

foreach ($users as $user) {
    echo $user->id;
}

创建懒集合
要创建一个懒集合实例,你应该将 PHP 生成器函数传递给懒集合的 make 方法:

use Illuminate\Support\LazyCollection;

LazyCollection::make(function () {
    $handle = fopen('log.txt', 'r');

    while (($line = fgets($handle)) !== false) {
        yield $line;
    }
});


posted on 2021-04-29 16:13  zh7314  阅读(264)  评论(0编辑  收藏  举报