Laravel - Eloquent


[ 定义模型 ]

使用 make:model 命令来生成一个模型文件, 理论上模型文件是不需要在意位置的,因为你使用的是命名空间

个人习惯会放在 App\Http\Models 中,这个目录需要手动生成

php artisan make:model App\Http\Rider

当你生成一个模型时想要顺便生成一个 数据库迁移,可以使用 --migration-m 选项:

php artisan make:model App\Http\Rider -m

[ 模型约定 ]

<?php

namespace App\Http\Models;

use Illuminate\Database\Eloquent\Model;

class rider extends Model
{
    //本模型关联表
    protected $table = 'riders';
    //关闭自动维护时间戳
    public $timestamps = false;
    //隐式模型绑定时候 想要绑定除 id(主键) 以外的数据库字段,就要修改一下函数
    public function getRouteKeyName()
    {
        //返回你所想要的字段,默认会是主键key name;
        return 'id';
    }
}
数据库连接

默认情况下,所有的 Eloquent 模型会使用应用程序中默认的数据库连接设置。如果你想为模型指定不同的连接,可以使用

$connection 属性:

protected $connection = 'connection-name';
//connection-name? 是不是很熟悉,我们在数据库配置一章的时候讲过了,就是 connections 数组中 链接对应的自定义名字
关联表名

如果我们没有 显示声明关联的表名,会默认绑定到 类名的复数形式名称 的数据表

比如 box => boxs , rider => riders 如此如此。所以我们可以显示声明表名:

protected $table = 'riders';
时间格式

如果你需要自定义自己的时间戳格式,可在模型内设置 $dateFormat 属性。这个属性决定了日期应如何在数据库中存储,以及当模型被序列化成数组或 JSON 格式:

protected $dateFormat = 'U';
时间维护

将时间维护设定成false的话, createdat 和 updated_at 在ORM操作的时候就不会自动更新了

public $timestamps = false;

[ 查询构造器 ]

其实每个模型都是一个 查询构造器, 查询构造器 所能用的方法,每个模型都可以用。

//返回全部骑士
$riders = App\Rider::all()
集合

类似 all 以及 get 之类的可以取回多个结果的 Eloquent 方法,将会返回一个 Illuminate\Database\Eloquent\Collection 实例。Collection 类提供 多种辅助函数 来处理你的 Eloquent 结果。

$flights = $flights->reject(function ($flight) {
    return $flight->cancelled;
});

当然,你也可以简单地像数组一样来遍历集合:

foreach ($flights as $flight) {
    echo $flight->name;
}
分块结果

如果你需要处理数以千计的 Eloquent 查找结果,则可以使用 chunk 命令。

Rider::select('name','kick')->where('kick','>',5)->chunk(2,function($riders){
    foreach ($riders as $d) {
        echo $d->name."<br/>";
    }
});
使用游标

cursor允许你使用游标来遍历数据库数据,一次只执行单个查询。在处理大数据量请求时 cursor方法可以大幅度减少内存的使用:

foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
    //
}

[ 取回单个模型/集合 ]

通过 findfirst 方法来取回单条记录。但这些方法返回的是单个模型的实例,而不是返回模型的集合:

// 通过主键取回一个模型...
$rider = Rider::find(1);

// 取回符合查询限制的第一个模型 ...
$rider = rider::where('name', 'faiz')->first();
允许异常捕获

有时候你可能希望在找不到模型时抛出一个异常,这在路由或是控制器内特别有用。findOrFail 以及 firstOrFail 方法会取回查询的第一个结果。如果没有找到相应结果,则会抛出一个 Illuminate\Database\Eloquent\ModelNotFoundException

$rider = Rider::findOrFail(111);

$rider = Rider::where('kick', '>', 100)->firstOrFail();

如果该异常没有被捕获,则会自动返回 HTTP 404 响应给用户

取回集合

当然,你也可以使用 countsummax,和其它查询构造器提供的聚合函数。这些方法会返回适当的标量值,而不是一个完整的模型实例:

$count = App\Flight::where('active', 1)->count();

$max = App\Flight::where('active', 1)->max('price');

[ 添加和更新模型 ]

添加新数据,和YII的操作无异。

 $rider = new Rider;
 $rider->name = $request->name;
 $rider->create_at = date('Y-m-d H:i:s');
 $rider->update_at = date('Y-m-d H:i:s');
 $rider->save();
基本更新

save 方法也可以用于更新数据库中已经存在的模型。要更新模型,则须先取回模型,再设置任何你希望更新的属性,接着调用save 方法。同样的,updated_at时间戳将会被自动更新,所以我们不需要手动设置它的值(假如你没有关闭时间维护的话):

$flight = App\Flight::find(1);
$flight->name = 'New Flight Name';
$flight->save();
批量更新
App\Flight::where('active', 1)
          ->where('destination', 'San Diego')
          ->update(['delayed' => 1]);

update 方法会期望收到一个含有字段与值对应的数组,而这些字段的内容将会被更新。

批量赋值

fillable 和 guarded, 简单来说就是,控制字段是否能批量赋值的白名单和黑名单

在 model 中使用 fillable 数组中对应的字段,被允许批量赋值

protected $fillable = ['name'];

而在 model 中使用 guarded 数组中对应的字段,不被允许批量赋值

//除了price以外的属性都可以批量赋值
protected $guarded = ['price'];

//guarded为空,代表所有属性都可以批量赋值
protected $guarded = [];

从应用上来说,黑白名单只使用其中一个。

当设定完成 fillable 或者 guarded 之后,就可以使用 create 方法来插入新数据

//App\Http\Models\Rider
protected $fillable  = ['name','create_at','update_at'];
//RiderController
$new_rider = Rider::create(['name'=>'kabuto-Hyper Form', 'create_at'=>date('Y-m-d H:i:s'), 'update_at'=>date('Y-m-d H:i:s')]);
dd($new_rider);

如果没有设定 fillable 或者 guarded 是无法使用 create 方法的。会出现异常

如果你已经有一个 model实例,你可以使用一个数组传递给 fill 方法:

$flight->fill(['name' => 'Flight 22']);
其它创建的方法

firstOrNew / firstOrCreate

firstOrNew 方法类似 firstOrCreate 方法,它会尝试使用指定的属性在数据库中寻找符合的纪录。如果模型未被找到,将会返回一个新的模型实例。请注意 firstOrnew 返回的模型还尚未保存到数据库。你需要通过手动调用 save 方法来保存它:

// 用属性取回航班,当结果不存在时创建它...
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);

// 用属性取回航班,当结果不存在时实例化一个新实例...
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);
$flight->save();

updateOrCreate

你可能会碰到模型已经存在则更新,否则创建新模型的情形, 使用updateOrCreate 方法来一步完成该操作,类似 firstOrCreate 方法, updateOrCreate 方法会持久化模型,所以无需调用 save() :

$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99]
);

第一个参数是查询条件,第二个参数是修改字段和它的值

[ 删除模型 ]

delete 方法

delete方法删除必须返回一个实例模型

$flight = App\Flight::find(1);
$flight->delete();
通过主键删除

destroy方法则不需要实例模型,但需要知道主键id

App\Flight::destroy([1, 2, 3]);
通过查询删除

本质上还是用 delete 方法删除, 只是添加条件而已

$deletedRows = App\Flight::where('active', 0)->delete();

当使用 Eloquent 批量删除语句时,deletingdeleted 模型事件不会在被删除模型实例上触发。因为删除语句执行时,不会检索回模型实例。

就是说不存在 删除中事件/删除后事件

软删除

要使用软删除,需要

1.数据表中有 deleted_at

2.模型必须添加使用 Illuminate\Database\Eloquent\SoftDeletes , trait 并添加 deleted_at 字段到你的 $dates 属性上

<?php

namespace App;

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

class Flight extends Model
{
    use SoftDeletes;

    /**
     * 需要被转换成日期的属性。
     *
     * @var array
     */
    protected $dates = ['deleted_at'];
}

现在,当你在模型上调用 delete 方法时,deleted_at 字段将会被设置成目前的日期和时间。而且,当查询有启用软删除的模型时,被软删除的模型将会自动从所有查询结果中排除。

查询被软删除的模型

如上所述,被软删除的模型将会自动从所有的查询结果中排除。不过,你可以通过在查询中调用 withTrashed 方法来强制查询已被软删除的模型:

$flights = App\Flight::withTrashed()
                ->where('account_id', 1)
                ->get();

withTrashed 方法也可以被用在关联查询:

$flight->history()->withTrashed()->get();

要确认指定的模型实例是否已经被软删除,可以使用 trashed方法:

if ($flight->trashed()) {
    //
}
只取出软删除数据

onlyTrashed 会只取出软删除数据:

$flights = App\Flight::onlyTrashed()
                ->where('airline_id', 1)
                ->get();
恢复被软删除的模型
$flight->restore();

//还可以添加条件查询来回复。
App\Flight::withTrashed()
        ->where('airline_id', 1)
        ->restore();
永久删除 软删除的数据
// 强制删除单个模型实例...
$flight->forceDelete();

// 强制删除所有相关模型...
$flight->history()->forceDelete();

[ 全局作用域 ]

全局作用域允许我们为给定模型的所有查询添加条件约束。

编写全局作用域

自定义全局作用域很简单,首先定义一个实现 Illuminate\Database\Eloquent\Scope 接口的类,该接口要求你实现一个方法:apply。需要的话可以在 apply 方法中添加 where 条件到查询:

<?php

namespace App\Scopes;

use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class AgeScope implements Scope
{
    /**
     * 应用作用域
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        return $builder->where('age', '>', 200);
    }
}
应用全局作用域
<?php

namespace App;

use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 数据模型的启动方法
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope(new AgeScope);
    }
}

添加作用域后,如果使用 User::all() 查询则会生成如下SQL语句:

select * from `users` where `age` > 200
匿名的全局作用域

Eloquent 还允许我们使用闭包定义全局作用域,这在实现简单作用域的时候特别有用,这样的话,我们就没必要定义一个单独的类了:

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

    static::addGlobalScope('age', function(Builder $builder) {
        $builder->where('age', '>', 200);
    });
}
移除全局作用域

使用 withoutGlobalScope 方法来移除全局作用域名。

User::withoutGlobalScope('age')->get();

如果是多个作用域, 就使用 withoutGlobalScopes ,复数形式。

User::withoutGlobalScopes([FirstScope::class, SecondScope::class])->get();
本地作用域

本地作用域允许我们定义通用的约束集合以便在应用中复用。定义一个本地作用域,只需简单在对应 Eloquent 模型方法前加上一个 scope 前缀,作用域总是返回查询构建器:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 限制查询只包括受欢迎的用户。
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }

    /**
     * 限制查询只包括活跃的用户。
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeActive($query)
    {
        return $query->where('active', 1);
    }
}
利用查询范围

在进行方法调用时不需要加上 scope 前缀。你甚至可以链式调用不同的范围,如:

$users = App\User::popular()->active()->orderBy('created_at')->get();
动态范围

有时候,你可能希望定义一个可接受参数的范围。这时只需给你的范围加上额外的参数即可。范围参数应该被定义在$query

参数之后

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 限制查询只包括指定类型的用户。
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeOfType($query, $type)
    {
        return $query->where('type', $type);
    }
}

调用

$users = App\User::ofType('admin')->get();

[ 事件 ]

Eloquent 模型会触发许多事件,让你在模型的生命周期的多个时间点进行监控

creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored.

事件让你每当有特定的模型类在数据库保存或更新时,执行代码。

当一个新模型被初次保存将会触发 creating 以及 created 事件。如果一个模型已经存在于数据库且调用了 save 方法,将会触发 updatingupdated 事件。在这两种情况下都会触发 savingsaved 事件。

在你的 Eloquent 模型上定义一个 $events 属性,将 Eloquent 模型的生命周期的多个点映射到你的 服务提供者

<?php

namespace App;

use App\Events\UserSaved;
use App\Events\UserDeleted;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * 模型的时间映射。
     *
     * @var array
     */
    protected $events = [
        'saved' => UserSaved::class,
        'deleted' => UserDeleted::class,
    ];
}

暂时不明白。服务提供者 怎么处理

反而观察者模式比较明白?

观察者

如果你在一个给定的模型中监听许多事件,您可以使用观察者将所有监听器变成一个类。观察者类里的方法名应该反映Eloquent想监听的事件。 每种方法接收 model 作为其唯一的参数。 Laravel不包括观察者默认目录,所以你可以创建任何你喜欢你的目录来存放:

<?php

namespace App\Observers;

use App\User;

class UserObserver
{
    /**
     * 监听用户创建的事件。
     *
     * @param  User  $user
     * @return void
     */
    public function created(User $user)
    {
        //
    }

    /**
     * 监听用户删除事件。
     *
     * @param  User  $user
     * @return void
     */
    public function deleting(User $user)
    {
        //
    }
}
注册观察者

要注册一个观察者,需要用模型中的observe 方法去观察。你可以在你的 服务提供商之一的boot方法 中注册观察者。在这个例子中,我们将在AppServiceProvider 注册观察者:

<?php

namespace App\Providers;

use App\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * 运行所有应用.
     *
     * @return void
     */
    public function boot()
    {
        User::observe(UserObserver::class);
    }

    /**
     * 注册服务提供.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

results matching ""

    No results matching ""