Laravel 包含 Eloquent,一个对象关系映射器(ORM),它让数据库交互变得愉快。使用 Eloquent 时,每个数据库表都对应一个“模型”,用于与该表进行交互。除了从数据库表中检索记录之外,Eloquent 模型还允许你向表中插入、更新和删除记录。
[!注意]
在开始之前,请务必在您的应用程序的config/database.php配置文件中配置数据库连接。有关配置数据库的更多信息,请查阅数据库配置文档。
要开始使用,我们来创建一个 Eloquent 模型。模型通常位于 app\Models 目录中,并继承 Illuminate\Database\Eloquent\Model 类。你可以使用 make:model Artisan 命令 来生成一个新的模型:
php artisan make:model Flight如果您想生成一个数据库迁移当您生成模型时,可以使用--migration或-m选项:
php artisan make:model Flight --migration在生成模型时,你可能会生成各种其他类型的类,例如工厂、填充器、策略、控制器和表单请求。此外,这些选项可以组合起来一次性创建多个类:
# Generate a model and a FlightFactory class...
php artisan make:model Flight --factory
php artisan make:model Flight -f
# Generate a model and a FlightSeeder class...
php artisan make:model Flight --seed
php artisan make:model Flight -s
# Generate a model and a FlightController class...
php artisan make:model Flight --controller
php artisan make:model Flight -c
# Generate a model, FlightController resource class, and form request classes...
php artisan make:model Flight --controller --resource --requests
php artisan make:model Flight -crR
# Generate a model and a FlightPolicy class...
php artisan make:model Flight --policy
# Generate a model and a migration, factory, seeder, and controller...
php artisan make:model Flight -mfsc
# Shortcut to generate a model, migration, factory, seeder, policy, controller, and form requests...
php artisan make:model Flight --all
php artisan make:model Flight -a
# Generate a pivot model...
php artisan make:model Member --pivot
php artisan make:model Member -p有时,仅通过浏览其代码可能难以确定模型的所有可用属性和关联。相反,请尝试使用 model:show Artisan 命令,它提供了模型所有属性和关联的便捷概览:
php artisan model:show Flight由 make:model 命令生成的模型将放置在 app/Models 目录中。让我们来看看一个基本的模型类,并讨论 Eloquent 的一些关键约定:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
// ...
}在浏览了上面的示例后,您可能已经注意到我们没有告诉 Eloquent 哪个数据库表对应我们的 Flight 模型。按照惯例,类的“snake case”,复数名称将被用作表名,除非明确指定了其他名称。因此,在这种情况下,Eloquent 将会假定 Flight 模型将记录存储在 flights 表中,而一个 AirTrafficController 模型则会将记录存储在 air_traffic_controllers 表中。
如果您的模型对应的数据库表不符合此约定,您可以通过在模型上定义一个 table 属性来手动指定模型的表名:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'my_flights';
}Eloquent 还会假定每个模型对应的数据库表拥有一个名为 id 的主键列。如有必要,你可以在模型上定义一个受保护的 $primaryKey 属性,以指定一个不同的列作为该模型的主键:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'flight_id';
}此外,Eloquent 假定主键是一个自增的整数值,这意味着 Eloquent 会自动将主键转换为整数。如果你希望使用一个非自增或非数字的主键你必须在模型上定义一个公共的 $incrementing 属性,并将其设置为 false:
<?php
class Flight extends Model
{
/**
* Indicates if the model's ID is auto-incrementing.
*
* @var bool
*/
public $incrementing = false;
}如果你的模型主键不是整数,你应当在模型上定义一个受保护的 $keyType 属性。此属性的值应为 string:
<?php
class Flight extends Model
{
/**
* The data type of the primary key ID.
*
* @var string
*/
protected $keyType = 'string';
}Eloquent 要求每个模型拥有至少一个唯一标识的“ID”作为其主键。“复合”主键不受 Eloquent 模型支持。然而,你可以在表的唯一标识主键之外,自由地向数据库表添加额外的多列唯一索引。
与其使用自增整数作为你的 Eloquent 模型的主键, 你可以选择使用 UUIDs. UUIDs 是全局唯一的字母数字标识符, 长度为 36 个字符.
如果您希望模型使用 UUID 键而不是自增整数键,您可以在模型上使用 Illuminate\Database\Eloquent\Concerns\HasUuids trait。当然,您应该确保模型拥有一个 UUID 等效的主键列:
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use HasUuids;
// ...
}
$article = Article::create(['title' => 'Traveling to Europe']);
$article->id; // "8f8e8478-9035-4d23-b9a7-62f4d2612ce5"默认情况下,HasUuids trait 将为你的模型生成“有序”UUID。这些 UUID 对于索引数据库存储更高效,因为它们可以按字典顺序排序。
你可以通过在模型上定义一个 newUniqueId 方法来覆盖给定模型的 UUID 生成过程。此外,你还可以通过在模型上定义一个 uniqueIds 方法来指定哪些列应该接收 UUID:
use Ramsey\Uuid\Uuid;
/**
* Generate a new UUID for the model.
*/
public function newUniqueId(): string
{
return (string) Uuid::uuid4();
}
/**
* Get the columns that should receive a unique identifier.
*
* @return array<int, string>
*/
public function uniqueIds(): array
{
return ['id', 'discount_code'];
}如果您愿意,您可以选择使用“ULID”而非 UUID。ULID 类似于 UUID;然而,它们仅有 26 个字符长。与有序 UUID 类似,ULID 可以进行字典排序,以实现高效的数据库索引。要使用 ULID,您应该在模型上使用 Illuminate\Database\Eloquent\Concerns\HasUlids trait。您还应该确保该模型具有一个 ULID 等效主键列:
use Illuminate\Database\Eloquent\Concerns\HasUlids;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use HasUlids;
// ...
}
$article = Article::create(['title' => 'Traveling to Asia']);
$article->id; // "01gd4d3tgrrfqeda94gdbtdk5c"默认情况下, Eloquent 期望 created_at 和 updated_at 列存在于您的模型对应的数据库表中. Eloquent 会自动设置这些列的值 当模型被创建或更新时. 如果您不希望这些列由 Eloquent 自动管理, 您应该在您的模型上定义一个值为 false 的 $timestamps 属性:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
}如果你需要自定义模型的时间戳格式,请在你的模型上设置 $dateFormat 属性。这个属性决定了日期属性在数据库中如何存储,以及模型序列化为数组或 JSON 时的格式:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The storage format of the model's date columns.
*
* @var string
*/
protected $dateFormat = 'U';
}如果您需要自定义用于存储时间戳的列名,您可以在模型上定义 CREATED_AT 和 UPDATED_AT 常量:
<?php
class Flight extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';
}如果您想执行模型操作,而不修改模型的 updated_at 时间戳,您可以在给定 withoutTimestamps 方法的闭包中对模型进行操作:
Model::withoutTimestamps(fn () => $post->increment('reads'));默认情况下,所有 Eloquent 模型都将使用为您的应用程序配置的默认数据库连接。如果您想指定与特定模型交互时应使用的不同连接,您应该在模型上定义一个 $connection 属性:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The database connection that should be used by the model.
*
* @var string
*/
protected $connection = 'mysql';
}默认情况下,一个新实例化的模型实例将不包含任何属性值。如果您想为模型的某些属性定义默认值,您可以在模型上定义一个 $attributes 属性。放置在 $attributes 数组中的属性值应采用原始的、“可存储”的格式,就如同它们刚从数据库中读取一样:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The model's default values for attributes.
*
* @var array
*/
protected $attributes = [
'options' => '[]',
'delayed' => false,
];
}Laravel 提供了几种方法,允许您在多种情况下配置 Eloquent 的行为和 "严格性"。
首先,preventLazyLoading 方法接受一个可选的布尔参数,用于指示是否应阻止延迟加载。例如,您可能希望只在非生产环境中禁用延迟加载,以便您的生产环境能继续正常运行,即使生产代码中意外存在延迟加载的关系。通常,此方法应在您的应用程序的 AppServiceProvider 的 boot 方法中调用:
use Illuminate\Database\Eloquent\Model;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Model::preventLazyLoading(! $this->app->isProduction());
}另外,你还可以指示 Laravel 在尝试填充不可填充属性时抛出异常,方法是调用 preventSilentlyDiscardingAttributes 方法。这有助于在本地开发过程中防止意外错误,即当尝试设置一个未添加到模型 fillable 数组中的属性时:
Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction());创建模型和其关联的数据库表后,您就可以开始从数据库中检索数据了。您可以将每个 Eloquent 模型视为一个强大的查询构建器,它允许您流畅地查询与该模型关联的数据库表。模型的 all 方法将检索模型关联数据库表中的所有记录:
use App\Models\Flight;
foreach (Flight::all() as $flight) {
echo $flight->name;
}Eloquent 的 all 方法将返回模型表中的所有结果。然而,由于每个 Eloquent 模型都可作为一个 查询构建器,你可以向查询添加额外约束,然后调用 get 方法来获取结果:
$flights = Flight::where('active', 1)
->orderBy('name')
->limit(10)
->get();[!NOTE]
由于 Eloquent 模型是查询构建器,你应该查阅 Laravel 的 查询构建器 提供的所有方法。在编写 Eloquent 查询时,你可以使用这些方法中的任何一个。
如果你已经有一个从数据库中检索出来的 Eloquent 模型实例,你可以使用 fresh 和 refresh 方法“刷新”该模型。fresh 方法将会从数据库中重新检索模型。现有的模型实例将不会受到影响:
$flight = Flight::where('number', 'FR 900')->first();
$freshFlight = $flight->fresh();该 refresh 方法将使用来自数据库的最新数据重新加载现有模型。此外,所有其已加载关系也将被刷新:
$flight = Flight::where('number', 'FR 900')->first();
$flight->number = 'FR 456';
$flight->refresh();
$flight->number; // "FR 900"正如我们所看到的,Eloquent 方法例如 all 和 get 从数据库中检索多个记录。然而,这些方法不会返回一个普通的 PHP 数组。取而代之的是,一个 Illuminate\Database\Eloquent\Collection 的实例被返回。
Eloquent Collection 类继承了 Laravel 的基础 Illuminate\Support/Collection 类,该类提供 多种有用的方法 用于与数据集合交互。例如,reject 方法可用于基于调用的闭包的结果从集合中移除模型:
$flights = Flight::where('destination', 'Paris')->get();
$flights = $flights->reject(function (Flight $flight) {
return $flight->cancelled;
});除了 Laravel 基础集合类提供的方法之外,Eloquent 集合类还提供了 一些额外的方法,这些方法专门用于与 Eloquent 模型集合进行交互。
由于所有 Laravel 的集合都实现了 PHP 的可迭代接口,您可以像遍历数组一样遍历集合:
foreach ($flights as $flight) {
echo $flight->name;
}您的应用程序在尝试通过 all 或 get 方法加载数万条 Eloquent 记录时可能会耗尽内存。与其使用这些方法,chunk 方法可用于更高效地处理大量模型。
chunk 方法将检索 Eloquent 模型的一个子集,并将它们传递给一个闭包进行处理。由于每次只检索当前批次的 Eloquent 模型,因此当处理大量模型时,chunk 方法将显著减少内存使用:
use App\Models\Flight;
use Illuminate\Database\Eloquent\Collection;
Flight::chunk(200, function (Collection $flights) {
foreach ($flights as $flight) {
// ...
}
});传递给 chunk 方法的第一个参数是您希望每个“块”接收的记录数量。作为第二个参数传递的闭包将为从数据库中检索到的每个块调用。将执行一个数据库查询来检索传递给该闭包的每个记录块。
如果你正在基于一个你也会在遍历结果时更新的列来筛选 chunk 方法的结果,你应该使用 chunkById 方法。在这些场景中使用 chunk 方法可能会导致意想不到和不一致的结果。在内部,chunkById 方法总是会检索那些 id 列大于前一个数据块中最后一个模型的模型:
Flight::where('departed', true)
->chunkById(200, function (Collection $flights) {
$flights->each->update(['departed' => false]);
}, column: 'id');由于 chunkById 和 lazyById 方法会向执行中的查询添加它们自己的 "where" 条件,你通常应该在一个闭包中逻辑分组你自己的条件:
Flight::where(function ($query) {
$query->where('delayed', true)->orWhere('cancelled', true);
})->chunkById(200, function (Collection $flights) {
$flights->each->update([
'departed' => false,
'cancelled' => true
]);
}, column: 'id');lazy 方法的作用类似于 [chunk 方法](#chunking-results),在后台以分块方式执行查询方面。然而,并非直接将每个分块原样传递给回调,lazy 方法返回一个扁平化的 [LazyCollection](/zh-cn/docs/laravel/12.x/collections#lazy-collections) 集合,其中包含 Eloquent 模型,让你能够以单一流的形式与结果进行交互:
use App\Models\Flight;
foreach (Flight::lazy() as $flight) {
// ...
}如果您正在根据您在迭代结果时也将更新的列来过滤 lazy 方法的结果,您应该使用 lazyById 方法。在内部,lazyById 方法将始终检索那些其 id 列大于前一个数据块中最后一个模型的模型:
Flight::where('departed', true)
->lazyById(200, column: 'id')
->each->update(['departed' => false]);您可以根据 id 的降序使用 lazyByIdDesc 方法筛选结果。
类似于 lazy 方法,cursor 方法可用于在迭代处理数万条 Eloquent 模型记录时,显著减少您的应用的内存消耗。
cursor 方法只会执行单个数据库查询;然而,单个 Eloquent 模型直到它们被实际迭代时才会被填充。因此,在任何给定时间,只有一个 Eloquent 模型在迭代游标时被保存在内存中。
[!WARNING]
由于cursor方法一次只在内存中保存一个 Eloquent 模型,它无法预加载关联关系。如果您需要预加载关联关系,请考虑使用lazy方法 代替。
在内部,cursor 方法使用 PHP 生成器来实现此功能:
use App\Models\Flight;
foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) {
// ...
}此 cursor 返回一个 Illuminate\Support\LazyCollection 实例。 延迟集合 允许您使用典型 Laravel 集合上可用的许多集合方法,同时每次只加载单个模型到内存中:
use App\Models\User;
$users = User::cursor()->filter(function (User $user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}尽管 cursor 方法使用的内存量远少于常规查询(因为它一次只在内存中保存一个 Eloquent 模型),它最终仍然会耗尽内存。这是因为 PHP 的 PDO 驱动器会在其缓冲区中内部缓存所有原始查询结果。如果您正在处理大量 Eloquent 记录,请考虑使用lazy 方法代替。
Eloquent 也提供高级子查询支持,它允许你在单个查询中从相关表中获取信息。例如,让我们想象我们有一个航班目的地表和一个到目的地的航班表。该航班表包含一个arrived_at列,它指示航班何时抵达目的地。
使用查询构造器的 select 和 addSelect 方法中可用的子查询功能, 我们可以选择所有的 destinations 以及最近抵达该目的地的航班名称 使用单个查询:
use App\Models\Destination;
use App\Models\Flight;
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
])->get();此外,查询构建器的 orderBy 函数支持子查询。继续使用我们的航班示例,我们可以使用此功能根据上次航班抵达该目的地的时间对所有目的地进行排序。同样,这可以在执行单个数据库查询时完成:
return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
)->get();除了检索所有符合给定查询的记录,你也可以使用 find、first 或 firstWhere 方法检索单条记录。这些方法不会返回模型集合,而是返回单个模型实例:
use App\Models\Flight;
// Retrieve a model by its primary key...
$flight = Flight::find(1);
// Retrieve the first model matching the query constraints...
$flight = Flight::where('active', 1)->first();
// Alternative to retrieving the first model matching the query constraints...
$flight = Flight::firstWhere('active', 1);有时,您可能希望在未找到结果时执行其他操作。findOr 和 firstOr 方法将返回单个模型实例,或者在未找到结果时执行给定的闭包。闭包返回的值将被视为该方法的结果:
$flight = Flight::findOr(1, function () {
// ...
});
$flight = Flight::where('legs', `>`, 3)->firstOr(function () {
// ...
});有时,如果模型未找到,您可能希望抛出一个异常。 这在路由或控制器中特别有用。 这些 findOrFail 和 firstOrFail 方法将检索查询的第一个结果; 但是,如果没有找到结果, 一个 Illuminate\Database\Eloquent\ModelNotFoundException 将被抛出:
$flight = Flight::findOrFail(1);
$flight = Flight::where('legs', `>`, 3)->firstOrFail();如果 ModelNotFoundException 未被捕获,则会自动向客户端发送 404 HTTP 响应:
use App\Models\Flight;
Route::get('/api/flights/{id}', function (string $id) {
return Flight::findOrFail($id);
});firstOrCreate 方法将尝试使用给定的列/值对查找数据库记录。如果数据库中找不到该模型,将插入一条记录,其属性来自合并第一个数组参数与可选的第二个数组参数。
firstOrNew 方法,与 firstOrCreate 类似,将尝试在数据库中查找与给定属性匹配的记录。但是,如果未找到模型,将返回一个新的模型实例。请注意,firstOrNew 返回的模型尚未持久化到数据库。你需要手动调用 save 方法来持久化它:
use App\Models\Flight;
// Retrieve flight by name or create it if it doesn't exist...
$flight = Flight::firstOrCreate([
'name' => 'London to Paris'
]);
// Retrieve flight by name or create it with the name, delayed, and arrival_time attributes...
$flight = Flight::firstOrCreate(
['name' => 'London to Paris'],
['delayed' => 1, 'arrival_time' => '11:30']
);
// Retrieve flight by name or instantiate a new Flight instance...
$flight = Flight::firstOrNew([
'name' => 'London to Paris'
]);
// Retrieve flight by name or instantiate with the name, delayed, and arrival_time attributes...
$flight = Flight::firstOrNew(
['name' => 'Tokyo to Sydney'],
['delayed' => 1, 'arrival_time' => '11:30']
);与 Eloquent 模型交互时,你也可以使用由 Laravel 查询构建器 提供的 count、sum、max 以及其他 聚合方法。正如你所预料的,这些方法返回一个标量值而不是一个 Eloquent 模型实例:
$count = Flight::where('active', 1)->count();
$max = Flight::where('active', 1)->max('price');当然,在使用 Eloquent 时,我们不仅需要从数据库中检索模型。我们还需要插入新记录。值得庆幸的是,Eloquent 使这变得简单。要向数据库插入新记录,您应该实例化一个新的模型实例并设置模型的属性。然后,在该模型实例上调用 save 方法:
<?php
namespace App\Http\Controllers;
use App\Models\Flight;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class FlightController extends Controller
{
/**
* Store a new flight in the database.
*/
public function store(Request $request): RedirectResponse
{
// Validate the request...
$flight = new Flight;
$flight->name = $request->name;
$flight->save();
return redirect('/flights');
}
}在此示例中, 我们将传入的HTTP请求中的 name 字段赋值给 App\Models\Flight 模型实例的 name 属性. 当我们调用 save 方法时, 一条记录将被插入到数据库中. 该模型的 created_at 和 updated_at 时间戳将在 save 方法被调用时自动设置, 因此无需手动设置它们.
或者,您可以使用 create 方法来“保存”一个新模型,只需一条 PHP 语句。插入的模型实例将由 create 方法返回给您:
use App\Models\Flight;
$flight = Flight::create([
'name' => 'London to Paris',
]);然而,在使用 create 方法之前,你需要在你的模型类上指定一个 fillable 或 guarded 属性。这些属性是必需的因为所有 Eloquent 模型默认都受到大规模赋值漏洞的保护。要了解更多关于大规模赋值的信息,请查阅大规模赋值文档。
save 方法也可用于更新已存在于数据库中的模型。要更新模型,应先检索它并设置要更新的任何属性。然后,您应调用模型的 save 方法。同样,updated_at 时间戳将自动更新,因此无需手动设置其值:
use App\Models\Flight;
$flight = Flight::find(1);
$flight->name = 'Paris to London';
$flight->save();偶尔,你可能需要更新一个现有模型,或者在没有匹配模型时创建一个新模型。类似于 firstOrCreate 方法,updateOrCreate 方法会持久化模型,因此无需手动调用 save 方法。
在下面的例子中,如果存在一个航班,其 departure 地点为 Oakland 且 destination 地点为 San Diego,那么它的 price 和 discounted 列将被更新。如果不存在这样的航班,将创建一个新航班,该航班将具有的属性是将第一个参数数组与第二个参数数组合并后得到的:
$flight = Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);当使用 firstOrCreate 或 updateOrCreate 等方法时,您可能不清楚是创建了一个新模型,还是更新了一个现有模型。wasRecentlyCreated 属性指示模型是否在其当前生命周期中被创建:
$flight = Flight::updateOrCreate(
// ...
);
if ($flight->wasRecentlyCreated) {
// New flight record was inserted...
}更新也可以针对符合给定查询条件的模型执行。在此示例中,所有处于 active 状态且 destination 为 San Diego 的航班都将被标记为延误:
Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]);该 update 方法期望一个由列和值对组成的数组,这些对代表应该被更新的列。该 update 方法返回受影响的行数。
[!WARNING]
通过 Eloquent 进行批量更新时,saving、saved、updating和updated模型事件不会针对已更新的模型触发。这是因为在进行批量更新时,模型从未实际检索。
Eloquent 提供了 isDirty、isClean 和 wasChanged 方法,用于检查模型的内部状态,并确定其属性相较于模型最初被检索时是如何变化的。
该 isDirty 方法用于判断模型自检索以来,其任何属性是否已发生更改。你可以将特定的属性名或属性数组传递给 isDirty 方法,以判断这些属性中是否有任何属性是“脏”的。该 isClean 方法将判断自模型检索以来,某个属性是否未发生更改。此方法也接受一个可选的属性参数:
use App\Models\User;
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->isDirty(); // true
$user->isDirty('title'); // true
$user->isDirty('first_name'); // false
$user->isDirty(['first_name', 'title']); // true
$user->isClean(); // false
$user->isClean('title'); // false
$user->isClean('first_name'); // true
$user->isClean(['first_name', 'title']); // false
$user->save();
$user->isDirty(); // false
$user->isClean(); // truewasChanged 方法用于判断模型在当前请求周期内上次保存时是否有任何属性被更改。如果需要,您可以传递一个属性名称来查看某个特定属性是否被更改:
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->save();
$user->wasChanged(); // true
$user->wasChanged('title'); // true
$user->wasChanged(['title', 'slug']); // true
$user->wasChanged('first_name'); // false
$user->wasChanged(['first_name', 'title']); // truegetOriginal 方法返回一个数组,其中包含模型的原始属性,无论模型自检索后是否发生任何更改。如果需要,你可以传递一个特定的属性名称来获取特定属性的原始值:
$user = User::find(1);
$user->name; // John
$user->email; // john@example.com
$user->name = 'Jack';
$user->name; // Jack
$user->getOriginal('name'); // John
$user->getOriginal(); // Array of original attributes...getChanges 方法返回一个数组,其中包含模型上次保存时发生变化的属性,而 getPrevious 方法返回一个数组,其中包含模型上次保存之前的原始属性值:
$user = User::find(1);
$user->name; // John
$user->email; // john@example.com
$user->update([
'name' => 'Jack',
'email' => 'jack@example.com',
]);
$user->getChanges();
/*
[
'name' => 'Jack',
'email' => 'jack@example.com',
]
*/
$user->getPrevious();
/*
[
'name' => 'John',
'email' => 'john@example.com',
]
*/您可以使用 create 方法通过单个 PHP 语句来“保存”一个新的模型。该方法将返回插入的模型实例给您:
use App\Models\Flight;
$flight = Flight::create([
'name' => 'London to Paris',
]);然而,在使用 create 方法之前,你需要在你的模型类上指定 fillable 或 guarded 属性。这些属性是必需的,因为所有 Eloquent 模型默认都受保护,以防止批量赋值漏洞。
批量赋值漏洞发生在用户传递了一个意料之外的 HTTP 请求字段,并且该字段更改了您的数据库中您未曾预料到的列时。例如,恶意用户可能会通过 HTTP 请求发送一个 is_admin 参数,该参数随后被传递到您模型的 create 方法,从而允许用户将自己提升为管理员。
因此,要开始使用,您应该定义哪些模型属性可以进行批量赋值。您可以使用模型上的 $fillable 属性来完成此操作。例如,让我们将我们 Flight 模型的 name 属性设置为可批量赋值:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = ['name'];
}一旦您指定了哪些属性可以进行批量赋值,您就可以使用 create 方法在数据库中插入一条新记录。create 方法会返回新创建的模型实例:
$flight = Flight::create(['name' => 'London to Paris']);如果你已经有一个模型实例,你可以使用 fill 方法用一个属性数组填充它:
$flight->fill(['name' => 'Amsterdam to Frankfurt']);在分配 JSON 列时,每列的可批量赋值键必须在模型的$fillable 数组中指定。出于安全考虑,当使用guarded 属性时,Laravel 不支持更新嵌套的 JSON 属性:
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'options->enabled',
];如果您想让您的所有属性都可批量赋值, 您可以将模型的 $guarded 属性定义为空数组. 如果您选择取消模型的防护, 您应该特别注意始终手动构建传递给 Eloquent 的 fill, create, and update 方法的数组:
/**
* The attributes that aren't mass assignable.
*
* @var array<string>|bool
*/
protected $guarded = [];默认情况下,未包含在$fillable数组中的属性在执行批量赋值操作时会被静默丢弃。在生产环境中,这是预期行为;然而,在本地开发过程中,这可能导致困惑,不知道为什么模型更改未生效。
如果您愿意,您可以指示 Laravel 在尝试填充不可填充属性时抛出一个异常,方法是调用 preventSilentlyDiscardingAttributes 方法。通常,这个方法应该在您的应用程序的 AppServiceProvider 类的 boot 方法中被调用:
use Illuminate\Database\Eloquent\Model;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Model::preventSilentlyDiscardingAttributes($this->app->isLocal());
}Eloquent 的 upsert 方法可用于在一个单一的、原子性的操作中更新或创建记录。该方法的第一个参数包含要插入或更新的值,而第二个参数列出了在关联表中唯一标识记录的列。该方法的第三个也是最后一个参数是一个列数组,如果数据库中已存在匹配的记录,则这些列应该被更新。upsert 方法如果模型上启用了时间戳,将自动设置 created_at 和 updated_at 时间戳:
Flight::upsert([
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], uniqueBy: ['departure', 'destination'], update: ['price']);[!WARNING]
除 SQL Server 外的所有数据库都要求upsert方法的第二个参数中的列具有“主”或“唯一”索引。此外,MariaDB 和 MySQL 数据库驱动程序会忽略upsert方法的第二个参数,并始终使用表的“主”和“唯一”索引来检测现有记录。
要删除模型,您可以在模型实例上调用 delete 方法:
use App\Models\Flight;
$flight = Flight::find(1);
$flight->delete();在上面的例子中,我们在调用 delete 方法之前从数据库中检索模型。然而,如果你知道模型的主键,你可以通过调用 destroy 方法,无需显式地检索即可删除模型。除了接受单个主键外,destroy 方法还将接受多个主键、一个主键数组或一个主键集合:
Flight::destroy(1);
Flight::destroy(1, 2, 3);
Flight::destroy([1, 2, 3]);
Flight::destroy(collect([1, 2, 3]));如果你正在使用 软删除模型,你可以通过 forceDestroy 方法永久删除模型:
Flight::forceDestroy(1);[!WARNING]
destroy方法单独加载每个模型并调用delete方法,以便deleting和deleted事件能够为每个模型正确分发。
当然,你可以构建一个 Eloquent 查询来删除所有符合你的查询条件的模型。在此示例中,我们将删除所有标记为不活跃的航班。和批量更新一样,批量删除不会为被删除的模型分发模型事件:
$deleted = Flight::where('active', 0)->delete();要删除表中的所有模型,您应该执行一个查询而不添加任何条件:
$deleted = Flight::query()->delete();[!WARNING]
当通过 Eloquent 执行批量删除语句时,deleting和deleted模型事件将不会被分发给被删除的模型。这是因为在执行删除语句时,模型从未被实际检索。
除了实际从数据库中删除记录,Eloquent 还可以“软删除”模型。当模型被软删除时,它们并不会实际从数据库中删除。相反,模型上会设置一个 deleted_at 属性,指示模型被“删除”的日期和时间。要为一个模型启用软删除,请将 Illuminate\Database\Eloquent\SoftDeletes trait 添加到模型中:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Flight extends Model
{
use SoftDeletes;
}[!注意]
SoftDeletes特性将自动为您把deleted_at属性转换为DateTime/Carbon实例。
您还应该将 deleted_at 列添加到您的数据库表。Laravel 架构构建器 包含一个用于创建此列的辅助方法:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('flights', function (Blueprint $table) {
$table->softDeletes();
});
Schema::table('flights', function (Blueprint $table) {
$table->dropSoftDeletes();
});现在,当你调用 delete 模型上的方法时,deleted_at 列将被设置为当前日期和时间。然而,该模型的数据库记录将保留在表中。当查询使用软删除的模型时,被软删除的模型将自动从所有查询结果中排除。
为了确定给定的模型实例是否已被软删除,您可以使用 trashed 方法:
if ($flight->trashed()) {
// ...
}有时你可能希望“取消删除”一个软删除的模型。要恢复一个软删除的模型,你可以在模型实例上调用 restore 方法。 restore 方法会将模型的 deleted_at 列设置为 null:
$flight->restore();你也可以在查询中使用 restore 方法来恢复多个模型。同样,像其他“批量”操作一样,这不会为被恢复的模型调度任何模型事件:
Flight::withTrashed()
->where('airline_id', 1)
->restore();在构建 关联关系 查询时,restore 方法也可使用:
$flight->history()->restore();有时你可能需要真正地从数据库中删除一个模型。你可以使用 forceDelete 方法从数据库表中永久删除一个软删除的模型:
$flight->forceDelete();您也可以在构建 Eloquent 关联查询时使用 forceDelete 方法:
$flight->history()->forceDelete();如上所述,软删除模型将自动从查询结果中排除。然而,你可以通过在查询上调用 withTrashed 方法来强制将软删除模型包含在查询结果中:
use App\Models\Flight;
$flights = Flight::withTrashed()
->where('account_id', 1)
->get();该 withTrashed 方法在构建 关系 查询时也可被调用:
$flight->history()->withTrashed()->get();The onlyTrashed method will retrieve only soft deleted models:
$flights = Flight::onlyTrashed()
->where('airline_id', 1)
->get();有时您可能希望定期删除不再需要的模型。为此,您可以将 Illuminate\Database\Eloquent\Prunable 或 Illuminate\Database\Eloquent\MassPrunable trait 添加到您希望定期修剪的模型中。将其中一个 trait 添加到模型后,实现一个 prunable 方法,该方法返回一个 Eloquent 查询构建器,用于解析不再需要的模型:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Prunable;
class Flight extends Model
{
use Prunable;
/**
* Get the prunable model query.
*/
public function prunable(): Builder
{
return static::where('created_at', `<=`, now()->subMonth());
}
}当将模型标记为Prunable时,您还可以在模型上定义一个pruning方法。此方法将在模型被删除之前调用。此方法对于删除与模型相关的任何额外资源(例如存储的文件)非常有用,在模型从数据库中永久删除之前:
/**
* Prepare the model for pruning.
*/
protected function pruning(): void
{
// ...
}配置完你的可修剪模型后,你应该在应用的 routes/console.php 文件中调度 model:prune Artisan 命令。你可以自由选择该命令应运行的适当间隔:
use Illuminate\Support\Facades\Schedule;
Schedule::command('model:prune')->daily();在幕后,model:prune 命令将自动检测应用程序 app/Models 目录中的“可剪枝”模型。如果您的模型位于其他位置,您可以使用 --model 选项来指定模型类名:
Schedule::command('model:prune', [
'--model' => [Address::class, Flight::class],
])->daily();如果您希望在对所有其他检测到的模型进行剪枝的同时,将某些模型排除在外,可以使用 --except 选项:
Schedule::command('model:prune', [
'--except' => [Address::class, Flight::class],
])->daily();您可以通过执行带 --pretend 选项的 model:prune 命令来测试您的 prunable 查询。在模拟运行时,model:prune 命令只会报告如果命令实际运行的话将有多少条记录会被剪枝:
php artisan model:prune --pretend[!WARNING]
软删除模型将被永久删除(forceDelete),如果它们与可修剪查询匹配。
当模型被标记为 Illuminate\Database\Eloquent\MassPrunable trait 时,模型会使用批量删除查询从数据库中删除。因此,将不会调用 pruning 方法,也不会触发 deleting 和 deleted 模型事件。这是因为模型在删除前从未实际检索,从而使修剪过程效率更高:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\MassPrunable;
class Flight extends Model
{
use MassPrunable;
/**
* Get the prunable model query.
*/
public function prunable(): Builder
{
return static::where('created_at', `<=`, now()->subMonth());
}
}您可以使用 replicate 方法创建一个现有模型实例的未保存副本。 当您拥有的模型实例共享许多相同的属性时,此方法特别有用:
use App\Models\Address;
$shipping = Address::create([
'type' => 'shipping',
'line_1' => '123 Example Street',
'city' => 'Victorville',
'state' => 'CA',
'postcode' => '90001',
]);
$billing = $shipping->replicate()->fill([
'type' => 'billing'
]);
$billing->save();为了将一个或多个属性排除在复制到新模型之外,你可以向 replicate 方法传递一个数组:
$flight = Flight::create([
'destination' => 'LAX',
'origin' => 'LHR',
'last_flown' => '2020-03-04 11:00:00',
'last_pilot_id' => 747,
]);
$flight = $flight->replicate([
'last_flown',
'last_pilot_id'
]);全局作用域允许你为给定模型的所有查询添加约束。Laravel 自带的 软删除 功能利用全局作用域,仅从数据库中检索“未删除”的模型。编写你自己的全局作用域可以提供一种方便、简单的方式,以确保针对给定模型的每个查询都接收特定的约束。
`
php artisan make:scope AncientScope编写全局作用域很简单。首先,使用 make:scope 命令生成一个实现 Illuminate\Database\Eloquent\Scope 接口的类。Scope 接口要求你实现一个方法:apply。apply 方法可以根据需要向查询添加 where 约束或其他类型的子句:
<?php
namespace App\Models\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class AncientScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*/
public function apply(Builder $builder, Model $model): void
{
$builder->where('created_at', `<`, now()->subYears(2000));
}
}[!NOTE]
如果你的全局作用域正在向查询的 select 子句添加列,你应该使用addSelect方法而不是select。这将防止查询现有的 select 子句被意外替换。
要为模型分配全局作用域,您只需将 ScopedBy 特性放置在模型上:
<?php
namespace App\Models;
use App\Models\Scopes\AncientScope;
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
#[ScopedBy([AncientScope::class])]
class User extends Model
{
//
}或者,你可以手动注册全局作用域,通过覆盖模型的 booted 方法并调用模型的 addGlobalScope 方法。addGlobalScope 方法接受你的作用域实例作为其唯一参数:
<?php
namespace App\Models;
use App\Models\Scopes\AncientScope;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*/
protected static function booted(): void
{
static::addGlobalScope(new AncientScope);
}
}在上述示例中将作用域添加到 App\Models\User 模型后,调用 User::all() 方法将执行以下 SQL 查询:
select * from `users` where `created_at` < 0021-02-18 00:00:00Eloquent 也允许你使用闭包定义全局作用域,这对于不值得拥有独立类的简单作用域特别有用。当使用闭包定义全局作用域时,你应该将自行选择的作用域名称作为 addGlobalScope 方法的第一个参数提供:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*/
protected static function booted(): void
{
static::addGlobalScope('ancient', function (Builder $builder) {
$builder->where('created_at', `<`, now()->subYears(2000));
});
}
}如果您想为给定查询移除一个全局作用域,您可以使用 withoutGlobalScope 方法。此方法接受全局作用域的类名作为其唯一参数:
User::withoutGlobalScope(AncientScope::class)->get();或者,如果你使用闭包定义了全局作用域,你应该传递你分配给全局作用域的字符串名称:
User::withoutGlobalScope('ancient')->get();如果您想移除查询的几个甚至全部全局作用域,您可以使用 withoutGlobalScopes 和 withoutGlobalScopesExcept 方法:
// Remove all of the global scopes...
User::withoutGlobalScopes()->get();
// Remove some of the global scopes...
User::withoutGlobalScopes([
FirstScope::class, SecondScope::class
])->get();
// Remove all global scopes except the given ones...
User::withoutGlobalScopesExcept([
SecondScope::class,
])->get();本地作用域允许你定义一组通用的查询约束,你可以在整个应用程序中轻松复用它们。例如,你可能需要频繁地检索所有被认为是“热门”的用户。要定义一个作用域,请将 Scope 属性添加到 Eloquent 方法中。
作用域应始终返回相同的查询构建器实例或 void:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Scope a query to only include popular users.
*/
#[Scope]
protected function popular(Builder $query): void
{
$query->where('votes', `>`, 100);
}
/**
* Scope a query to only include active users.
*/
#[Scope]
protected function active(Builder $query): void
{
$query->where('active', 1);
}
}一旦作用域被定义,你就可以在查询模型时调用这些作用域方法。你甚至可以链式调用多个作用域:
use App\Models\User;
$users = User::popular()->active()->orderBy('created_at')->get();通过 or 查询操作符组合多个 Eloquent 模型作用域可能需要使用闭包才能实现正确的逻辑分组:
$users = User::popular()->orWhere(function (Builder $query) {
$query->active();
})->get();然而,由于这可能很繁琐,Laravel 提供了一个“高阶”orWhere 方法,允许您流畅地将作用域链式连接起来而无需使用闭包:
$users = User::popular()->orWhere->active()->get();有时你可能希望定义一个接受参数的作用域。首先,只需将你额外的参数添加到你的作用域方法的签名中。作用域参数应该在 $query 参数之后定义:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Scope a query to only include users of a given type.
*/
#[Scope]
protected function ofType(Builder $query, string $type): void
{
$query->where('type', $type);
}
}一旦预期的参数已添加到你的作用域方法的签名中,你可以在调用该作用域时传递这些参数:
$users = User::ofType('admin')->get();如果您想使用作用域来创建具有与用于约束该作用域的属性相同属性的模型,您可以在构建作用域查询时使用 withAttributes 方法:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
/**
* Scope the query to only include drafts.
*/
#[Scope]
protected function draft(Builder $query): void
{
$query->withAttributes([
'hidden' => true,
]);
}
}该 withAttributes 方法将使用给定的属性向查询中添加 where 条件,并且它还将把给定的属性添加到通过该作用域创建的任何模型中:
$draft = Post::draft()->create(['title' => 'In Progress']);
$draft->hidden; // true为了指示 withAttributes 方法不要向查询添加 where 条件,你可以将 asConditions 参数设置为 false:
$query->withAttributes([
'hidden' => true,
], asConditions: false);有时您可能需要确定两个模型是否“相同”。is 和 isNot 方法可用于快速验证两个模型是否具有相同的主键、表和数据库连接:
if ($post->is($anotherPost)) {
// ...
}
if ($post->isNot($anotherPost)) {
// ...
}is 和 isNot 方法在使用 belongsTo、hasOne、morphTo 和 morphOne 关系时也可用。当您希望比较一个关联模型而无需发出查询来检索该模型时,此方法特别有用:
if ($post->author()->is($user)) {
// ...
}[!NOTE]
想要将你的 Eloquent 事件直接广播到你的客户端应用吗?了解一下 Laravel 的模型事件广播。
Eloquent 模型会分派多个事件, 允许你介入模型生命周期中的以下时刻: retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, trashed, forceDeleting, forceDeleted, restoring, restored, 和 replicating.
当现有模型从数据库中检索出来时,retrieved 事件将被调度。当新模型首次保存时,creating 和 created 事件将被调度。当现有模型被修改并调用了 save 方法时,updating / updated 事件将被调度。当模型被创建或更新时,saving / saved 事件将被调度——即使模型的属性没有改变。以 -ing 结尾的事件名称在对模型进行任何更改持久化之前被调度,而以 -ed 结尾的事件在对模型的更改持久化之后被调度。
要开始监听模型事件,在你的 Eloquent 模型上定义一个 $dispatchesEvents 属性。此属性将 Eloquent 模型生命周期中的各个点映射到你自己的 事件类。每个模型事件类都应期望通过其构造函数接收受影响模型的实例:
<?php
namespace App\Models;
use App\Events\UserDeleted;
use App\Events\UserSaved;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
/**
* The event map for the model.
*
* @var array<string, string>
*/
protected $dispatchesEvents = [
'saved' => UserSaved::class,
'deleted' => UserDeleted::class,
];
}在定义和映射您的 Eloquent 事件之后, 您可以使用 事件监听器 来处理这些事件.
[!警告]
当通过 Eloquent 执行批量更新或删除查询时,已保存、已更新、正在删除和已删除模型事件将不会针对受影响的模型触发。这是因为在执行批量更新或删除时,模型从未实际被获取。
而不是使用自定义事件类,您可以注册在各种模型事件被分派时执行的闭包。通常,您应该在您的模型的booted方法中注册这些闭包:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*/
protected static function booted(): void
{
static::created(function (User $user) {
// ...
});
}
}如有需要,您可以在注册模型事件时使用 可队列的匿名事件监听器。 这将指示 Laravel 使用您的应用程序的 队列 在后台执行模型事件监听器:
use function Illuminate\Events\queueable;
static::created(queueable(function (User $user) {
// ...
}));如果你在一个给定模型上监听许多事件,你可以使用观察者(observers)将所有监听器分组到一个类中。观察者类拥有方法名,这些方法名反映了你希望监听的 Eloquent 事件。每个方法都接收受影响的模型作为其唯一参数。make:observer Artisan 命令是创建新观察者类最简单的方式:
php artisan make:observer UserObserver --model=User此命令会将新的观察者放置在您的 app/Observers 目录中。如果此目录不存在,Artisan 将为您创建它。您新鲜的观察者将如下所示:
<?php
namespace App\Observers;
use App\Models\User;
class UserObserver
{
/**
* Handle the User "created" event.
*/
public function created(User $user): void
{
// ...
}
/**
* Handle the User "updated" event.
*/
public function updated(User $user): void
{
// ...
}
/**
* Handle the User "deleted" event.
*/
public function deleted(User $user): void
{
// ...
}
/**
* Handle the User "restored" event.
*/
public function restored(User $user): void
{
// ...
}
/**
* Handle the User "forceDeleted" event.
*/
public function forceDeleted(User $user): void
{
// ...
}
}要注册一个观察者,您可以将 ObservedBy 属性放置在对应的模型上:
use App\Observers\UserObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
#[ObservedBy([UserObserver::class])]
class User extends Authenticatable
{
//
}或者, 您可以通过在您希望观察的模型上调用 observe 方法来手动注册观察者。 您可以在应用程序的 AppServiceProvider 类的 boot 方法中注册观察者:
use App\Models\User;
use App\Observers\UserObserver;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
User::observe(UserObserver::class);
}[!NOTE]
观察者可以监听额外的事件,例如saving和retrieved。这些事件在 事件 文档中进行了描述。
当模型在数据库事务中创建时,您可能希望指示观察者仅在数据库事务提交后执行其事件处理程序。您可以通过在观察者上实现 ShouldHandleEventsAfterCommit 接口来达到此目的。如果没有数据库事务正在进行,事件处理程序将立即执行:
<?php
namespace App\Observers;
use App\Models\User;
use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit;
class UserObserver implements ShouldHandleEventsAfterCommit
{
/**
* Handle the User "created" event.
*/
public function created(User $user): void
{
// ...
}
}你可能偶尔需要暂时“静默”模型触发的所有事件。你可以使用 withoutEvents 方法实现此目的。withoutEvents 方法接受一个闭包作为其唯一参数。在此闭包内执行的任何代码都不会分派模型事件,并且闭包返回的任何值都将由 withoutEvents 方法返回:
use App\Models\User;
$user = User::withoutEvents(function () {
User::findOrFail(1)->delete();
return User::find(2);
});有时您可能希望 "保存" 给定的模型而不触发任何事件。您可以使用 saveQuietly 方法来完成此操作:
$user = User::findOrFail(1);
$user->name = 'Victoria Faith';
$user->saveQuietly();您还可以“更新”、“删除”、“软删除”、“恢复”和“复制”给定的模型,而无需分派任何事件:
$user->deleteQuietly();
$user->forceDeleteQuietly();
$user->restoreQuietly();