数据模型是任何Laravel应用程序中最重要的部分之一。许多系统都是围绕这个数据模型设计的, 所以它通常是我们在开发过程中最先接触的东西之一.我们中的一些人已经做了很多年,对如何处理这个问题有一个很好的想法 - 而其他人可能还不习惯。在我知道有框架这种东西之前,我就学会了数据建模,用CREATE TABLE 语句设计我的数据模型。
在本教程中, 我将介绍如何在你的Laravel应用程序中进行数据建模 - 以及一些我认为有用的提示.
本教程是一个正在进行的系列教程的第一部分, 在这个系列中, 我们应该从头到尾一起建立一个完整的可生产的系统.换句话说, 从IDE到服务器.
我们要建立什么?我很高兴你这么问。我可以在这里做一些简单的事情,比如一个ToDo应用程序或一个博客--但你不会从中学到任何有用的东西。相反,我们将建立一些独特的、令人兴奋的、有许多活动部件的东西,到最后,这应该是一些有价值的东西。最近我去寻找一个会议室预订系统,说实话,我很难找到一个。所以我们将在Laravel中建立一个开源的系统。这里的目的是以我们在生产环境中期望的方式建立一些东西,同时提供一些免费的东西。
这个会议室管理平台将包括什么?这不会是一个SaaS风格的应用程序,任何人都可以注册并使用它,这将是你自己下载并运行的东西。在这个阶段,考虑这些决定是很关键的,因为它为我们的数据模型提供了很多信息。
我们希望我们的应用程序的工作方式是,一开始,一个配置过程会发生,重要的设置指示可以通过。一个整体的系统管理员可以被邀请到平台上,让他们邀请用户并按要求设置系统。
让我们先来看看我们的用户模型,它和典型的Laravel用户模型不太一样。最终, 我们将重构我们的用户模型到一个为我们控制认证的包 - 但现在, 我们将保持简单。
我们的用户模型将需要一个额外的列,叫做type,这将是一个Enum。我们将保留电子邮件验证列,以便在基于API的环境中更有效地验证用户。
现在的用户迁移应该如下所示。
public function up(): void
{
Schema::create('users', function (Blueprint $table): void {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->string('type')->default(Type::ADMIN->value);
$table->timestamps();
});
}
正如你所看到的, 对于Laravel应用程序来说, 除了我们要增加的那一列之外, 它是相对标准的.从这里, 我们可以开始关注我们需要为用户创建的模型工厂.
final class UserFactory extends Factory
{
protected $model = User::class;
public function definition(): array
{
return [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => Hash::make(
value: 'password',
),
'type' => Type::ADMIN,
];
}
public function unverified(): UserFactory
{
return $this->state(
state: fn (array $attributes) => ['email_verified_at' => null],
);
}
public function type(Type $type): UserFactory
{
return $this->state(
state: fn (array $attributes) => ['type' => $type],
);
}
}
我们添加了一个默认的类型,我们希望适用于任何创建的用户。然而,我们也创建了一个辅助方法,这样我们就可以定制我们想要创建的用户类型。
这将我们引向模型和我们想应用于它的变化。对Eloquent模型的改动很小,只是增加了一个可填充的列,并确保我们可以将类型属性投给我们的Enum。
final class User extends Authenticatable implements MustVerifyEmail
{
use HasApiTokens;
use HasFactory;
use Notifiable;
protected $fillable = [
'name',
'email',
'password',
'type',
];
protected $hidden = [
'password',
];
protected $casts = [
'type' => Type::class,
'email_verified_at' => 'datetime',
];
}
在我们讨论更多细节之前,你可能想知道Enum本身和它的可用选项。
enum Type: string
{
case ADMIN = 'admin';
case OFFICE_MANAGER = 'office manager';
case STAFF = 'staff';
}
我们的应用程序将由以下人员组成。
- 管理员;是负责配置系统和管理系统的人。
- 办公室经理;是有权在系统上覆盖预订的人。
- 工作人员;是系统中有权预订会议室的人。
这方面的工作流程是,管理员将邀请办公室经理,然后他们可以开始让工作人员进入该平台。
除了对数据的数据库表示进行建模外,我们还需要一种方法来理解应用程序中的数据。我们可以通过使用域传输对象(简称DTO)来实现这一点。
我们首先通过数据库包含的内容来了解这个数据模型,然后弄清楚整个应用程序需要什么。然而,我们有时也需要在数据库中存在这些对象之前,就能创建这些对象。
final class User implements DataObjectContract
{
public function __construct(
private readonly string $name,
private readonly string $email,
private readonly Type $type,
) {}
public function toArray(): array
{
return [
'name' => $this->name,
'email' => $this->email,
'type' => $this->type,
];
}
}
对于我们来说,要创建或邀请一个潜在的用户,或了解关于一个用户的任何情况,我们需要访问他们的名字、电子邮件和类型。这涵盖了大多数用例,因为资源大多是通过路由模型绑定来识别的。
我们现在有了一种通过数据库和应用程序来理解数据的方法。这是我在构建任何Laravel应用时,都会按照这个顺序重复的一个过程。它给了我一个抽象的模型来传递,而不是一个简单的数组,同时强迫属性的类型安全水平。有时我需要直接访问这些属性, 但不是很频繁, 当我需要时, 我会创建一个访问器, 而不是改变属性的可见性.