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,
}
我们有一个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 类,它处理我们对数据库数据的所有获取和设置。然后我们有所有我们想要存储的属性--这是英国格式的地址,因为这是我居住的地方。最后,我们添加了一个方法,帮助我们将这个地址格式化为一个字符串--如果我们想在用户界面的任何形式下显示这个地址的话。我们可以更进一步,使用regex或API进行邮编验证--但这可能有悖于本教程的目的。
让我们继续看另一个例子:钱。我们都了解钱;我们知道我们可以使用最小形式的硬币和较大形式的纸币。所以想象一下,我们有一个电子商务商店,在那里我们存储我们的产品(一个简单的商店,所以我们不必担心变体等等),我们有一个叫做price 的列,我们以最小的货币分母存储。我在英国,所以我将其称为便士。然而,在美国这就是美分。在我们的数据库中存储货币价值的常见方法,因为它避免了浮点数学问题。因此,让我们为这个价格列设计一个铸模,这次使用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货币库已经经过了很好的测试,而且很可靠,如果我说实话--我没有什么不同的做法。
让我们来看看一个新的例子。我们有一个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,
);
}
所以这个方法可能会做一个小小的假设,即我们在日期字符串中存储假期,但逻辑是这样的:获取一周中的一天,用英语说,因为这是我们的属性名称;如果这一天是假的,那么返回假的。然而,我们还需要检查假期。如果当前日期字符串不在假期的数组中,那么它是开放的;否则,它是关闭的。
所以我们就可以用我们的cast上的today方法来检查该企业是否开放。
$business = Business:query()->find(1);
// Is the business open today?
$business->open->today();
你可能可以想象,你可以在此基础上添加许多方法,允许你检查多种事物,甚至添加显示这些信息的方法,非常好。
你在你的应用程序中使用属性铸造吗?你有其他很酷的例子可以分享吗?请给我们发一条推特,告诉我们你的例子,分享知识的力量。