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']);
黑名单属性
类似于是上面的
当然这两个只能存在一个。
(骚操作:可以将$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()
{
//
}
}