Eloquent 属性转换

发表于 作者

Eloquent Attribute Casting image

Eloquent 可转换属性是 Laravel 最强大的功能之一。有些人经常使用它们,而有些人则倾向于避开它们。在本教程中,我将介绍几个使用它们的示例,最重要的是,为什么你应该使用它们。

你可以创建一个新的 Laravel 项目或使用现有的项目来完成本教程。请随意跟着我一起做,或者如果你想阅读并记住它们 - 也可以。从本教程中要带走的最重要的一点是 Eloquent 转换有多好。

让我们从第一个例子开始,类似于 Laravel 文档中的例子。Laravel 文档展示了如何获取用户的地址,但使用了 Model。现在想象一下,如果这是用户模型上的 JSON 列,或者实际上是任何模型上的 JSON 列。我们可以获取和设置一些数据,并添加一些额外的功能来让它按我们想要的方式工作。我们需要安装一个由 Jess Archer 开发的软件包,名为 Laravel Castable 数据传输对象。你可以使用以下 Composer 命令安装它

composer require jessarcher/laravel-castable-data-transfer-object

现在我们已经安装了它,让我们设计我们的 Castable 类

namespace App\Casts;
 
use JessArcher\CastableDataTransferObject\CastableDataTransferObject;
 
class Address implements CastableDataTransferObject
{
public string $nameOrNumber,
public string $streetName,
public string $localityName,
public string $town,
public string $county,
public string $postalCode,
public string $country,
}

我们有一个 PHP 类,它扩展了 CastableDataTransferObject,它允许我们标记属性及其类型,然后处理后台的所有获取和设置选项。这个软件包在幕后使用了 Spatie 开发的名为 数据传输对象 的软件包,它在社区中非常受欢迎。所以现在,如果我们想扩展它,我们可以

namespace App\Casts;
 
use JessArcher\CastableDataTransferObject\CastableDataTransferObject;
 
class Address implements CastableDataTransferObject
{
public string $nameOrNumber,
public string $streetName,
public string $localityName,
public string $town,
public string $county,
public string $postalCode,
public string $country,
 
public function formatString(): string
{
return implode(', ', [
"$this->nameOrNumber $this->streetName",
$this->localityName,
$this->townName,
$this->county,
$this->postalCode,
$this->country,
]);
}
}

我们有一个 Address 类,它扩展了来自软件包的 CastableDataTransferObject 类,它处理我们对数据库的所有数据获取和设置。然后,我们拥有我们想要存储的所有属性 - 这是我居住地的英国格式地址。最后,我们添加了一个方法,帮助我们将这个地址格式化为字符串 - 如果我们想在用户界面中显示它。我们可以使用正则表达式或 API 来进一步进行邮政编码验证,但这可能背离了本教程的目的。

让我们继续另一个例子:货币。我们都理解货币;我们知道我们可以使用最小的硬币形式和更大的钞票形式的货币。所以想象一下,我们有一个电子商务商店,我们在其中存储我们的产品(一个简单的商店,所以我们不必担心变体等),并且我们有一个名为 price 的列,我们在其中存储我们货币的最小单位。我在英国,所以我会称之为便士。然而,在美国是美分。在我们的数据库中存储货币值的常用方法,因为它避免了浮点数学问题。所以让我们为这个 price 列设计一个转换,这次使用 PHP moneyphp/money 软件包

namespace App\Casts;
 
use Money\Currency;
use Money\Money;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
 
class Money implements CastsAttributes
{
public function __construct(
protected int $amount,
) {}
 
public function get($model, string $key, $value, array $attributes)
{
return new Money(
$attributes[$this->amount],
new Currency('GBP'),
);
}
 
public function set($model, string $key, $value, array $attributes)
{
return [
$this->amount => (int) $value->getAmount(),
$this->curreny => (string) $value->getCurrency(),
];
}
}

所以这个类不使用 DTO 软件包,而是返回一个具有自己的方法的新实例。在我们的构造函数中,我们传入一个金额。当我们获取和设置金额时,我们要么转换为数组以便存储在数据库中,要么解析数组以返回一个新的货币对象。当然,我们可以将其改为数据传输对象,并更好地控制如何处理货币。但是,PHP 货币库经过了充分的测试并且可靠,说实话 - 我不会做太多改变。

让我们来看一个新的例子。我们有一个 CRM 应用程序,我们想存储企业的营业时间或营业日。每个企业都需要能够标记他们营业的日期,但只能用简单的真或假的方式。所以我们可以创建一个新的转换,但首先,我们将创建我们要转换到的类,就像上面的 PHP 货币类一样。

namespace App\DataObjects;
 
class Open
{
public function __construct(
public readonly bool $monday,
public readonly bool $tuesday,
public readonly bool $wednesday,
public readonly bool $thursday,
public readonly bool $friday,
public readonly bool $saturday,
public readonly bool $sunday,
public readonly array $holidays,
) {}
}

首先,这很好;我们只想存储企业是否在每一天营业,以及他们计算为假日的日期数组。接下来,我们可以设计我们的转换

namespace App\Casts;
 
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
 
class OpenDaysCast implements CastsAttributes
{
public function __construct(
public readonly array $dates,
) {}
 
public function set($model, string $key, $value, array $attributes)
{
return $this->dates;
}
 
public function get($model, string $key, $value, array $attributes)
{
return Open::fromArray(
dates: $this->dates,
);
}
}

我们的构造函数将接受一个日期数组来简化保存(但是,你需要确保在这里正确地验证输入)。然后,当我们想从数据库中获取数据时,我们创建一个新的 Open 对象,并将日期传入。但是,这里我们调用了一个 fromArray 方法,我们还没有创建,所以让我们现在设计它

public static function fromArray(array $dates): static
{
return new static(
monday: (bool) data_get($dates, 'monday'),
tuesday: (bool) data_get($dates, 'tuesday'),
wednesday: (bool) data_get($dates, 'wednesday'),
thursday: (bool) data_get($dates, 'thursday'),
friday: (bool) data_get($dates, 'friday'),
saturday: (bool) data_get($dates, 'saturday'),
sunday: (bool) data_get($dates, 'sunday'),
holidays: (array) data_get($dates, 'holidays'),
);
}

所以我们使用 Laravel 助手 data_get 手动构建我们的 Open 对象,这非常方便,确保我们转换为正确的类型。现在,当我们查询时,我们有权访问

$business = Business:query()->find(1);
 
// Is this business open on mondays?
$business->open->monday; // true|false
 
// Is the business open on tuesdays?
$business->open->tuesday; // true|false
 
// What are the busines holiday dates?
$business->open->holidays; // array

如你所见,我们可以使它变得非常可读,以便开发人员体验是逻辑且易于理解的。然后我们可以扩展它来添加其他方法,例如今天是否营业?

public function today(): bool
{
$date = now();
 
$day = strtolower($date->englishDayOfWeek());
 
if (! $this->$day) {
return false;
}
 
return ! in_array(
$date->toDateString(),
$this->holidays,
);
}

所以这个方法可能假设我们正在以日期字符串的形式存储假日,但逻辑如下:获取星期几,以英语表示,因为这是我们的属性名称;如果该天是 false,则返回 false。但是,我们还需要检查假日。如果当前日期字符串不在假日数组中,则营业;否则,则关闭。

所以我们可以然后检查企业是否营业,使用我们转换上的 today 方法

$business = Business:query()->find(1);
 
// Is the business open today?
$business->open->today();

如你所想,你可以向其中添加许多方法,使你能够检查多种事物,甚至添加一些方法来很好地显示这些信息。

你在你的应用程序中使用属性转换吗?你还有其他一些很酷的例子可以分享吗?请在推特上与我们分享你的例子,并分享知识的力量。

Steve McDougall photo

Laravel 新闻 的技术作家,Treblle 的开发者倡导者。API 专家,经验丰富的 PHP/Laravel 工程师。 YouTube 直播主

Cube

Laravel 时事通讯

加入 40,000 多名其他开发者,绝不错过新的技巧、教程等等。

Laravel Forge logo

Laravel Forge

轻松创建和管理你的服务器,并在几秒钟内部署你的 Laravel 应用程序。

Laravel Forge
Tinkerwell logo

Tinkerwell

Laravel 开发者必备的代码运行器。使用 AI、自动完成和对本地和生产环境的即时反馈进行调试。

Tinkerwell
No Compromises logo

没有妥协

Joel 和 Aaron,来自 No Compromises 播客的两名经验丰富的开发者,现在可以为你的 Laravel 项目聘用。 ⬧ 每月固定费用 7500 美元。 ⬧ 没有冗长的销售流程。 ⬧ 没有合同。 ⬧ 100% 退款保证。

没有妥协
Kirschbaum logo

Kirschbaum

提供创新和稳定性,以确保你的 Web 应用程序成功。

Kirschbaum
Shift logo

Shift

正在运行旧版本的 Laravel?即时、自动化的 Laravel 升级和代码现代化,使你的应用程序保持新鲜。

Shift
Bacancy logo

Bacancy

仅需每月 2500 美元,即可为你的项目配备经验丰富的 Laravel 开发者,拥有 4-6 年的经验。获得 160 小时的专业服务,以及 15 天的无风险试用。立即安排通话!

Bacancy
Lucky Media logo

Lucky Media

立即获得幸运 - Laravel 开发的理想选择,拥有超过十年的经验!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel 电子商务

Laravel 电子商务。一个开源软件包,将现代无头电子商务功能的强大功能带入 Laravel。

Lunar: Laravel 电子商务
LaraJobs logo

LaraJobs

官方 Laravel 职位发布平台

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit:Laravel SaaS 启动工具包

SaaSykit 是一个 Laravel SaaS 启动工具包,包含运行现代 SaaS 所需的所有功能。支付、精美结账、管理面板、用户仪表板、身份验证、现成组件、统计信息、博客、文档等等。

SaaSykit:Laravel SaaS 启动工具包
Rector logo

Rector

您无缝升级 Laravel、降低成本和加速创新以帮助企业成功的合作伙伴

Rector
MongoDB logo

MongoDB

通过 MongoDB 和 Laravel 的强大集成来增强您的 PHP 应用程序,使开发人员能够轻松高效地构建应用程序。支持事务、搜索、分析和移动用例,同时使用熟悉的 Eloquent API。探索灵活、现代的 MongoDB 数据库如何改变您的 Laravel 应用程序。

MongoDB
Maska is a Simple Zero-dependency Input Mask Library image

Maska 是一个简单、无依赖的输入掩码库

阅读文章
Add Swagger UI to Your Laravel Application image

在 Laravel 应用程序中添加 Swagger UI

阅读文章
Assert the Exact JSON Structure of a Response in Laravel 11.19 image

在 Laravel 11.19 中断言响应的精确 JSON 结构

阅读文章
Build SSH Apps with PHP and Laravel Prompts image

使用 PHP 和 Laravel Prompts 构建 SSH 应用程序

阅读文章
Building fast, fuzzy site search with Laravel and Typesense image

使用 Laravel 和 Typesense 构建快速、模糊的网站搜索

阅读文章
Add Comments to your Laravel Application with the Commenter Package image

使用 Commenter 包向 Laravel 应用程序添加评论

阅读文章