laravel笔记-Eloquent ORM(基础)

时间:2022-10-25 08:33:28

laravel笔记-Eloquent ORM(基础)

写在前面

时间可贵,善用目录↑

学习Laravel的笔记,仅仅是作为laravel文档笔记,目的是强化对文档的理解,质量不高。


什么是Eloquent ORM

M,就是其他框架里的模型~,用来和数据库交互,一般来讲每一个表对应一个模型。

这篇主要介绍简单的使用流程,为了跑通概念。


定义一个Eloquent ORM

使用Artisan 命令:

php artisan make:model Flight

运行完后我们会在app目录下看到我们创建的model。

表名

这里注意,我们并没有定义Flight对应的表明,默认规则是模型类名的复数作为与其对应的表名。Flight->flights。

当然,我们可以使用$table指定一个表来关联Eloquent。

主键

Eloquent默认每张表的主键名为id,你可以在模型类中定义一个$primaryKey属性来覆盖该约定。

时间戳

laravel默认自动管理created_at和updated_at(在创建表的时候需要创建列)。框架会自动填充相应列。

如果不想自动管理可以$timestamps = false

当然如果要自行更改该时间戳字段的格式可以$dateFormat.

数据库连接

Eloquent默认使用默认的数据库连接,当然也可以自行传入一个定义好的连接
使用$connection = '你配置的连接名'(config/database.php中配置)。

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model{
// 关联到模型的数据表
protected $table = 'flight';
//设置主键
protected $primaryKey = 'flight_id';
//关闭created_at和updated_at的自动管理
$timestamps = false;
}

如何使用Eloquent ORM

如果我们按之前的步骤创建了一个Eloquent,我们就可以使用它方便的对数据库进行操作。

使用all()或者get()查询时,返回值为Illuminate\Database\Eloquent\Collection的实例,所以我们可以查询Collection的源码来查看如何操作数据~

查询:

use App\Flight;

//比如说查询全部数据all()
$flights = App\Flight::all();
//循环输出结果
foreach ($flights as $flight) {
echo $flight->name;
}

//一个简单的带约束的查询
$flights = App\Flight::where('active', 1)
->orderBy('name', 'desc')
->take(10)
->get();

//比如说遍历操作
$flights = $flights->reject(function ($flight) {
return $flight->cancelled;
});

//也可以分组操作数据
App\Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//
}
});

//当我们需要逐条处理大量数据是可以使用cursor()
//游标,可以大幅度减少内存消耗
foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
//
}

//我们也可以通过主键来获取
$flight = App\Flight::find(1);
//当需要抛出异常时~findOrFail
//会返回一个404响应
$model = App\Flight::findOrFail(1);

//或者获取匹配查询条件的第一个模型...
$flight = App\Flight::where('active', 1)->first();
//当需要抛出异常时~findOrFail
//同样会返回一个404响应
$model = App\Flight::where('legs', '>', 100)->firstOrFail();

插入/更新模型:

插入操作需要创建一个Eloquent的实例。
更新操作类似插入,不过需要获取实例。

//插入
$flight = new Flight;
$flight->name = $request->name;
$flight->save();

//更新
$flight = App\Flight::find(1);
$flight->name = 'New Flight Name';
$flight->save();
//或者
App\Flight::where('active', 1)
->where('destination', 'San Diego')
//注意这里是数组形式。
->update(['delayed' => 1]);

当然也可以通过下面的方法创建模型:

// 通过属性获取航班, 如果不存在则创建...
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);
// 通过属性获取航班, 如果不存在初始化一个新的实例...
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);

批量赋值

当然上面我们可以发现save()操作之前$flight->name = $request->name;这种操作在大量数据的时候很繁琐,所以我们可以对实例进行批量赋值。

进行批量复制前需要在Eloquent中指定可以批量赋值的属性。

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model{
//$fillable,数组内是运行批量赋值的列
protected $fillable = ['name', 'sex', 'age'];
}

之后就可以通过create()方法来进行插入。

//使用create插入
$flight = App\Flight::create(['name' => 'Flight1', 'age' => '20']);

黑名单属性

类似于是上面的 fillable guarded属性和其相反为黑名单,注册的列不允许批量赋值。
当然这两个只能存在一个。
(骚操作:可以将$guarded设为空来允许全部字段可批量赋值)

删除

使用delete方法或者destroy方法。

//delete方法是先获取再删除
$flight = App\Flight::find(1);
$flight->delete();
//查询删除模型
$deletedRows = App\Flight::where('active', 0)->delete();

//直接通过主键删除
App\Flight::destroy(1);
App\Flight::destroy([1, 2, 3]);
App\Flight::destroy(1, 2, 3);

软删除

感觉这才是常用的,
就是不真正的将记录从表中删除,而是通过设置一个deleted_at属性,当deleted_at不为空时,表示记录已经被软删除。

但是需要先在Eloquent中启用

namespace App;

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

class Flight extends Model{
//使用trait
use SoftDeletes;
//设置软删除代表字段
protected $dates = ['deleted_at'];
}

这样,再使用delete方法或者destroy方法时就是软删除。

判断是否软删除:$flight->trashed()
查询时排除软删除的记录withTrashed()
只查询被软删除的记录onlyTrashed()
恢复被软删除的记录restore()
强制删除记录forceDelete()


骚操作

查询作用域

为给定模型的所有查询添加条件约束。

可以提供一种方便的、简单的方式来确保给定模型的每个查询都有特定的条件约束。

编写全局作用域

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

注:Laravel应用默认并没有为作用域预定义文件夹,所以你可以按照自己的喜好在app目录下创建Scopes目录。

比如说规定查询都要age>200:

namespace App\Scopes;

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

class AgeScope implements Scope{
/**
* Apply the scope to a given Eloquent query builder.
*
* @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);
}
}

全局作用域

要将全局作用域分配给模型,需要重写给定模型的 boot 方法并使用 addGlobalScope 方法:

namespace App;

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

class Flight extends Model{
/**
* The "booting" method of the model.
*
* @return void
*/

protected static function boot()
{

parent::boot();

static::addGlobalScope(new AgeScope);

//当然有些简单的作用域可以直接闭包函数定义
static::addGlobalScope('age', function(Builder $builder) {
$builder->where('age', '>', 200);
});
}
}

这样,每次Flight查询都会固定现在范围。

有时,我们需要全局作用域暂时失效:

//移除固定字段的作用域
Flight::withoutGlobalScope('age')->get();
//全局作用域
//移除全部
Flight::withoutGlobalScope()->get();
//单个
Flight::withoutGlobalScope(AgeScope::class)->get();
//多个
Flight::withoutGlobalScopes([FirstScope::class, SecondScope::class])->get();

本地作用域

本地作用域允许我们定义通用的约束集合以便在应用中复用。例如,你可能经常需要获取最受欢迎的用户,要定义这样的一个作用域,只需简单在对应Eloquent模型方法前加上一个scope前缀。

其实就是预定义一些条件方便之后调用(复用)

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

使用本地作用域:去掉前缀

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

动态作用域

我不是很懂它存在的意义
感觉就是单纯代替where
这种奇技淫巧估计只有用到的时候才会明白。


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事件会被触发。如果一个模型已经在数据库中存在并调用save方法,updating/updated事件会被触发,无论是创建还是更新,saving/saved事件都会被调用。

举个例子,我们在服务提供者中定义一个Eloquent事件监听器,在事件监听器中,我们会调用给定模型的isValid方法,如果模型无效会返回false。如果从Eloquent事件监听器中返回false则取消save/update操作:

namespace App\Providers;

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

class AppServiceProvider extends ServiceProvider{
/**
* 启动所有应用服务
*
* @return void
*/

public function boot()
{

User::creating(function ($user) {
return $user->isValid();
});
}

/**
* 注册服务提供者.
*
* @return void
*/

public function register()
{

//
}
}

观察者

如果你在给定模型中监听多个事件,可以使用观察者来对所有监听器进行分组,观察者类拥有反射你想要监听的Eloquent事件对应的方法名,每个方法接收模型作为唯一参数。

其实就得大量使用事件监听器的时候代码会比较乱,所以单独建立一个类来存储监听操作,方便维护和调用。

<?php

namespace App\Observers;

use App\User;

class UserObserver
{

/**
* Listen to the User created event.
*
* @param User $user
* @return void
*/

public function created(User $user)
{

//
}

/**
* Listen to the User deleting event.
*
* @param User $user
* @return void
*/

public function deleting(User $user)
{

//
}
}

在服务提供者中注册时只需要observe方法来注册,方法会通过反射实现。

namespace App\Providers;

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

class AppServiceProvider extends ServiceProvider
{

/**
* Bootstrap any application services.
*
* @return void
*/

public function boot()
{

//使用
User::observe(UserObserver::class);
}

/**
* Register the service provider.
*
* @return void
*/
public function register()
{

//
}
}