Elokwentne atrybuty rzucane są jedną z potężniejszych cech Laravela; niektórzy ludzie używają ich religijnie, podczas gdy inni mają tendencję do unikania ich. W tym samouczku omówię kilka różnych przykładów ich używania i, co najważniejsze, dlaczego powinieneś ich używać.
Możesz utworzyć nowy projekt Laravel lub użyć istniejącego w tym samouczku.Zapraszam do śledzenia razem ze mną lub jeśli chcesz przeczytać i zapamiętać - to też jest w porządku. Najważniejszą rzeczą, którą należy od tego odjąć, jest to, jak dobre mogą być elokwentne rzuty.
Zacznijmy od naszego pierwszego przykładu, podobnego do tego w dokumentacji Laravela. Dokumentacja Laravela pokazuje pobranie adresu użytkownika, ale także użycie Modelu.Teraz wyobraź sobie, że zamiast tego była to kolumna JSON użytkownika lub dowolny model tego faktu. Możemy pobierać i ustawiać niektóre dane oraz dodawać dodatkowe, aby działały tak, jak chcemy. Musimy zainstalować pakiet przez Jess Archer o nazwie Laravel Castable Data Transfer Object.Możesz zainstalować to za pomocą następującego polecenia kompozytora:
composer require jessarcher/laravel-castable-data-transfer-object
Teraz mamy to zainstalowane, zaprojektujmy naszą klasę 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,
}
Mamy klasę PHP, która rozszerza CastableDataTransferObject
, która pozwala nam oznaczyć właściwości i ich typy, a następnie obsługuje wszystkie opcje pobierania i ustawiania za kulisami.Ten pakiet używa pakietu Spatie o nazwie Data Transfer Object pod maską, co było dość popularne w społeczności.Więc teraz, gdybyśmy chcieli to w ogóle rozszerzyć, moglibyśmy:
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,
]);
}
}
Mamy klasę Address
, która rozszerza klasę CastableDataTransferObject
z pakietu, która obsługuje wszystkie nasze pobieranie i ustawianie danych w bazie danych. Mamy wtedy wszystkie nieruchomości, które chcemy przechowywać - jest to adres w formacie brytyjskim, ponieważ tam mieszkam.Wreszcie mamy dodaną metodę, która pomaga nam sformatować ten adres jako ciąg - jeśli chcemy wyświetlić go w dowolnej formie interfejsu użytkownika. Moglibyśmy pójść o krok dalej dzięki walidacji kodu pocztowego za pomocą wyrażenia regularnego lub interfejsu API – ale może to przekreślić sens samouczka.
Przejdźmy do innego przykładu: pieniędzy.Wszyscy rozumiemy pieniądze; wiemy, że możemy używać pieniędzy w jego najmniejszej formie monet i większej formie banknotów. Wyobraź sobie więc, że mamy sklep e-commerce, w którym przechowujemy nasze produkty (prosty sklep, więc nie musimy się martwić o warianty itp.) i mamy kolumnę o nazwie cena
, którą przechowujemy w najmniejszym mianowniku naszej waluty. Jestem w Wielkiej Brytanii, więc zadzwonię do tego pensa.Jednak w USA to centy. Powszechne podejście do przechowywania wartości pieniężnych w naszej bazie danych, ponieważ pozwala uniknąć problemów z matematyką zmiennoprzecinkową. Zaprojektujmy więc rzut dla tej kolumny ceny, tym razem używając pakietu 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(),
];
}
}
Więc ta klasa nie używa pakietu DTO, ale zamiast tego zwraca nową instancję z własnymi metodami. W naszym konstruktorze przekazujemy w ilości.Kiedy otrzymujemy i ustawiamy kwotę, wykonujemy rzutowanie na tablicę w celu przechowywania w bazie danych lub analizujemy tablicę, aby zwrócić nowy obiekt pieniędzy. Oczywiście zamiast tego moglibyśmy uczynić z tego obiekt przesyłania danych i trochę bardziej kontrolować sposób, w jaki obchodzimy się z pieniędzmi. Jednak biblioteka php money jest całkiem nieźle przetestowana i niezawodna, a jeśli mam być szczery - niewiele zrobiłbym inaczej.
Przejdźmy do nowego przykładu. Mamy aplikację CRM i chcemy przechowywać godziny lub dni otwarcia firmy. Każda firma musi być w stanie oznaczyć dni, w których są otwarte, ale tylko w prosty, prawdziwy lub fałszywy sposób. Możemy więc utworzyć nowy rzut, ale najpierw stworzymy klasę, na którą chcemy rzutować, podobnie jak powyższa klasa PHP money.
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,
) {}
}
Na początek wszystko jest w porządku; chcemy tylko przechowywać, jeśli firma jest otwarta każdego dnia i mieć szereg dni, które liczą się jako święta. Następnie możemy zaprojektować naszą obsadę:
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,
);
}
}
Nasz konstruktor zaakceptuje tablicę dat, aby uprościć zapisywanie (musisz jednak upewnić się, że poprawnie zweryfikowałeś dane wejściowe).Następnie, gdy chcemy odzyskać dane z bazy danych, tworzymy nowy obiekt Open
, przekazując daty.Jednak tutaj wywołujemy metodę fromArray
, której jeszcze nie stworzyliśmy, więc zaprojektujmy ją teraz:
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'),
);
}
Więc ręcznie tworzymy nasz obiekt Open za pomocą pomocnika Laravel data_get
, co jest niezwykle przydatne, upewniając się, że rzutujemy na właściwy typ.Teraz, gdy pytamy, mamy dostęp:
$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
Jak widać, możemy sprawić, że będzie to niezwykle czytelne, aby środowisko programisty było logiczne i łatwe do naśladowania. Czy możemy to rozszerzyć, aby dodać dodatkowe metody, takie jak jest to otwarte dzisiaj?
public function today(): bool
{
$date = now();
$day = strtolower($date->englishDayOfWeek());
if (! $this->$day) {
return false;
}
return ! in_array(
$date->toDateString(),
$this->holidays,
);
}
$business = Business:query()->find(1);
// Is the business open today?
$business->open->today();