laravel框架中使用Eloquent写入数据库流程步骤
Laravel Eloquent 是当今现代框架中最强大、最令人惊叹的功能之一。
从将数据转换为值对象和类,使用可填充字段、事务、范围、全局范围和关系来保护数据库。
Eloquent 使您能够成功地完成您需要对数据库执行的任何操作。
开始使用 Eloquent 有时会让人感到害怕,因为它可以做很多事情,你永远不知道从哪里开始。
在本教程中,我将专注于我认为任何应用程序的基本方面之一——写入数据库。
您可以在任何应用程序区域写入数据库:
控制器、作业、中间件、工匠命令。处理数据库写入的最佳方法是什么?
让我们从一个没有关系的简单 Eloquent 模型开始。
final class Post extends Model
{
protected $fillable = [
'title',
'slug',
'content',
'published',
];
protected $casts = [
'published' => 'boolean',
];
}
我们有一个Post代表博客文章的模型;
它有一个标题、标签、内容和一个布尔标志来表示它是否已发布。
在这个例子中,我们假设发布的属性默认true在数据库中。
现在,首先,我们告诉 Eloquent 我们希望能够填写title、slug、content和published属性或列。
因此,如果我们传递未在fillable数组中注册的任何内容,则会引发异常 - 保护我们的应用程序免受潜在问题的影响。
现在我们知道可以填充哪些字段,我们可以看看将数据写入数据库,无论是创建、更新还是删除。
如果你的模型继承了这个SoftDeletes特征,那么删除一条记录就是一个写操作——但对于这个例子,我会保持简单;删除就是删除。
您最有可能看到的内容(尤其是在文档中)类似于以下内容:
Post::create($request->only('title', 'slug', 'content'));
这就是我可以标准的 Eloquent,你有一个模型,然后你调用静态方法来创建一个新实例——从请求中传入一个特定的数组。
这种方法有好处;它干净简单,每个人都理解它。有时我可能是一个非常固执己见的开发人员。
但是,我仍然会使用这种方法,特别是如果我处于原型设计模式,它更多的是测试一个想法而不是长期构建一些东西。
在要求创建新实例之前,我们可以通过在模型上启动一个新的 Eloquent 查询构建器实例来更进一步。
这将如下所示:
Post::query()->create($request->only('title', 'slug', 'content'));
如您所见,它仍然非常简单,并且正在成为在 Laravel 中启动查询的一种更加标准化的方式。
这种方法最显着的好处之一是之后的一切都query遵循最近引入的查询构建器契约。
由于 Laravel 在后台的工作方式,您的 IDE 无法很好地理解静态调用 - 因为它是使用__callStatic实际静态方法的方法的静态代理。
幸运的是,方法不是这种情况query,它是您正在扩展的 Eloquent 模型上的静态方法。
有一种构建模型以保存到数据库的“旧”方法。但是,我很少看到它经常被使用。
不过,为了清楚起见,我会提到它:
$post = new Post();
$post->title = $request->get('title');
$post->slug = $request->get('slug');
$post->content = $request->get('content');
$post->save();
这是我们以编程方式构建模型的地方,将值分配给属性,然后将其保存到数据库中。
这有点啰嗦,而且总觉得要付出太多努力才能实现。
但是,如果您喜欢这样做,这仍然是创建新模型的可接受方式。
到目前为止,我们已经研究了在数据库中创建新数据的三种不同方法。
我们可以使用类似的方法来更新数据库中的数据,静态调用update或使用查询构建合约
query()->where('column', 'value')->update(),
或者最终以编程方式设置属性,然后save. 我不会在这里重复自己,因为它与上面的大致相同。
如果我们不确定记录是否已经存在,我们该怎么办?例如,我们要创建或更新现有帖子。
我们将有一列是我们想要检查的唯一性 - 然后我们传递一个我们想要创建或更新的值数组,具体取决于它是否存在。
Post::query()->updateOrCreate(
attributes: ['slug' => $request->get('slug'),
values: [
'title' => $request->get('title'),
'content' => $request->get('content'),
],
);
如果您不确定记录是否存在,这会带来一些巨大的好处,而且我最近自己实现了这一点,当时我想“确保”一条记录无论如何都在数据库中。
例如,对于 OAuth 2.0 社交登录,您可以接受来自提供商的信息并在验证用户身份之前更新或创建新记录。
我们能更进一步吗?会有什么好处?
您可以使用像存储库模式这样的模式来基本上“代理”您将通过不同的类发送给 eloquent 的调用。
这样做有一些好处,或者至少在 Eloquent 成为今天的样子之前曾经有过。
让我们看一个例子:
class PostRepository
{
private Model $model;
public function __construct()
{
$this->model = Post::query();
}
public function create(array $attributes): Model
{
return $this->model->create(
attributes: $attributes,
);
}
}
如果我们使用 DB Facade 或普通 PDO,那么存储库模式可能会给我们带来很多保持一致性的好处。
让我们继续。
在某些时候,人们认为从 Repository 类迁移到 Service 类是一个好主意。但是,这是同一件事......我们不要进入那个。
所以,我们想要一种处理与 Eloquent 交互的方法,它不是那么“内联”或程序化的。
几年前,我采用了一种现在被称为“行动”的方法。它类似于存储库模式。
但是,与 Eloquent 的每次交互都是它自己的类,而不是一个类中的方法。
让我们看一下这个例子,其中我们为每个交互都有一个专用的类,称为“动作”:
final class CreateNewPostAction implements CreateNewPostContract
{
public function handle(array $attributes): Model|Post
{
return Post::query()
->create(
attributes: $attributes,
);
}
}
我们的类实现了一个契约,将它很好地绑定到容器,允许我们将它注入到构造函数中,并在需要时用我们的数据调用 handle 方法。
这变得越来越流行,许多人(以及包)已经开始采用这种方法,因为您创建的实用程序类可以很好地完成一件事 - 并且可以轻松地为它们创建测试替身。
另一个好处是我们使用了一个接口;
如果我们决定离开 Eloquent(不知道你为什么要这样做),我们可以快速更改我们的代码以反映这一点,而无需寻找任何东西。
同样,这是一种非常好的方法 - 原则上没有真正的缺点。我提到我是一个非常挑剔的开发人员,对吧?
出色地 ...
在使用了这么长时间之后,我对“操作”的最大问题是我们将所有的写入、更新和删除集成都放在一个引擎盖下。
对我来说,行动并没有把事情分解得足够多。如果我想一想,我们想要实现两件不同的事情——我们想要写作,我们想要阅读。
这部分反映了另一种称为 CQRS(命令查询责任分离)的设计模式,这是我稍微借鉴的东西。
在 CQRS 中,通常会使用命令总线和查询总线来读取和写入数据,通常会发出要使用事件源存储的事件。
但是,有时这比您需要的要多得多。不要误会我的意思,这种方法肯定有时间和地点,但你应该只在需要时才去接触它——否则,
所以我把我的写操作分成“命令”,把我的读操作分成“查询”,这样我的交互就分开了并且集中了。我们来看一个命令:
final class CreateNewPost implements CreateNewPostContract
{
public function handle(array $attributes): Model|Post
{
return Post::query()
->create(
attributes: $attributes,
);
}
}
你看一下,除了类命名之外,它和一个动作是一样的。这是设计使然。
操作是写入数据库的绝佳方式。我发现他们往往会很快变得拥挤。
我们还有什么其他方法可以改进这一点?
引入域传输对象将是一个很好的起点,因为它提供了类型安全、上下文和一致性。
final class CreateNewPost implements CreateNewPostContract
{
public function handle(CreatePostRequest $post): Model|Post
{
return Post::query()
->create(
attributes: $post->toArray(),
);
}
}
所以我们现在在数组中引入类型安全,我们以前依赖数组并希望事情进展顺利。
是的,我们可以随心所欲地验证——但对象具有更好的一致性。
有什么办法可以改进吗?总有改进的余地,但我们需要吗?当前的方法可靠、类型安全且易于记忆。
但是,如果数据库表在我们可以写入之前锁定,或者如果我们的网络连接出现问题,我们该怎么办,可能 Cloudflare 在错误的时间出现故障。
数据库事务将在这里节省我们的精力。
它们没有得到应有的使用,但它们是您应该考虑尽快采用的强大工具。
final class CreateNewPost implements CreateNewPostContract
{
public function handle(CreatePostRequest $post): Model|Post
{
return DB::transaction(
fn() => Post::query()->create(
attributes: $post->toArray(),
)
);
}
}
我们最终到达了那里!如果我在 PR 或我必须做的代码审查中看到这样的代码,我会高兴得跳起来。
但是,不要觉得您必须以这种方式编写代码。create请记住,如果它可以为您完成工作,那么内联静态是完全可以的!
重要的是做你觉得舒服的事,让你有效率的事——而不是别人说你应该在社区里做的事。
采用我们刚刚看到的方法,我们可以以相同的方式从数据库中读取数据。
分解问题,确定步骤以及可以改进的地方,但总是质疑你是否走得太远了。如果感觉自然,这可能是一个好兆头。
您如何处理写入数据库?你会走多远,什么时候太远?在 Twitter 上让我们知道您的想法!
转:
https://laravel-news.com/writing-to-the-database-with-eloquent
相关文章