Laravel 中的 Eloquent Casting

Laravel

Eloquent Castable 属性是 Laravel 更强大的特性之一; 有些人喜欢使用它们,而另一些人则倾向于回避它们。 在本教程中,我将介绍几个使用它们的不同示例,最重要的是,为什么应该使用它。

你可以为本教程创建一个新的 Laravel 项目或使用现有的项目。 跟随我来写,或者如果你只是想阅读并记录那也很好。

让我们从第一个示例开始,类似于 Laravel 文档中的示例。 Laravel 文档显示了获取用户地址但也使用模型。 现在想象一下,如果这是一个关于用户的 JSON 列,或者该事实的任何模型。我们可以获取和设置一些数据并添加额外的数据以使这项工作如我们所愿。 我们需要通过 Jess Archer 安装一个名为 Laravel Castable Data Transfer Object 的包。你可以使用以下 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,
}

我们有一个扩展 CastableDataTransferObject 的 PHP 类,它允许我们标记属性及其类型,然后在后台处理所有获取和设置选项。 这个包在底层使用了一个名为 Data Transfer Object 的 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 进行邮政编码验证——但这可能会破坏本教程的重点。

让我们转向另一个例子:金钱。 我们都懂钱; 我们知道我们可以使用最小形式的硬币和更大形式的纸币。 因此,假设我们有一个电子商务商店,用于存储我们的产品(一个简单的商店),并且我们有一个名为「价格」的列,我们将其存储在最小分母中。 我在英国,所以我会称这个便士。 然而,这在美国是美分。 一种在我们的数据库中存储货币值的常用方法,因为它避免了浮点数学问题。 因此,让我们为此价格列设计一个演员表,这次使用 phpmoneyphp/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 money 库经过了很好的测试和可靠。

让我们来看一个新的例子。 我们有一个 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,
    ) {}
}

首先,这很不错; 我们只想存储企业是否每天营业,以及是否有一些列被视为假期的日子。接下来,我们可以设计我们的 cast

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);

// business 周一是否开启
$business->open->monday; // true|false

// business 周三是否开启
$business->open->tuesday; // true|false

// 假期是否开启
$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。然而,我们也需要检查节假日。如果当前日期字符串不在假日的数组中,则它是打开的;否则,它是关闭的。

因此,我们可以使用Business表中的 today 方法检查业务是否开放:

1$business = Business:query()->find(1);2 3// 今天营业吗?4$business->open->today();

正如您可能想象的那样,您可以为此添加许多方法,允许您检查多个事物,甚至添加很好地显示此信息的方法。

你在你的应用程序中使用属性转换吗?你还有其他很酷的例子可以分享吗?请在推文中向我们发送您的示例,并分享知识的力量。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://laravel-news.com/eloquent-attrib...

译文地址:https://learnku.com/laravel/t/69452

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!