Дисклэймер: это не тру DDD и всё такое, и без тэстов. Речь о реальной бизнес задаче, где нужно соблюсти баланс и бюджет не резиновый.
По сути тестовый VO, т.к. раньше я ими не пользовался. Хочу попробовать на основе Адреса.
В модели есть поля для отображения адреса, системный адрес(который может даже отличаться от пользовательского) и координаты.
Второй момент, что VO, CastsAttributes должен быть по сути универсальный, а поля моделей могут отличаться.
Получилось что-то типо такого (только наверное еще координаты туда надо запихнуть в виде объекта)
Третий момент.
По сути VO это практически DTO с логикой? Как минимум нормально имплиментиться от
Spatie\DataTransferObject и использовать его функционал?
namespace App\Casts;
use App\Exceptions\BusinessLogicException;
use App\Models\Property;
use App\ValueObjects\Address;
use Grimzy\LaravelMysqlSpatial\Eloquent\SpatialExpression;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class AddressCast implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
if ($model instanceof Property) {
return new Address([
'street_number' => $model->address_street_number,
'street' => $model->address_street,
'city' => $model->address_city,
'neighborhood' => $model->address_neighborhood,
'province' => $model->address_province,
'google_address_components' => $model->address_raw_google_details,
'location' => $model->location,
]);
}
throw new BusinessLogicException(self::class . ' model not specified');
}
public function set($model, $key, $value, $attributes)
{
if ($model instanceof Property) {
return [
'address_street_number' => $value->street_number,
'address_street' => $value->street,
'address_city' => $value->city,
'address_neighborhood' => $value->neighborhood,
'address_province' => $value->province,
'address_raw_google_details' => $value->google_address_components,
'location' => \Arr::get($attributes, 'location') instanceof SpatialExpression ? new SpatialExpression($value->location) : $value->location,
];
}
throw new BusinessLogicException(self::class . ' model not specified');
}
}
namespace App\ValueObjects;
use App\Http\Requests\Site\PropertyCreateRequest;
use Grimzy\LaravelMysqlSpatial\Types\Point;
use Spatie\DataTransferObject\DataTransferObject;
class Address extends DataTransferObject
{
public string $street_number = '';
public string $street = '';
public string $city = '';
public string $neighborhood = '';
public string $province = '';
public ?\Illuminate\Support\Collection $google_address_components = null;
public \Grimzy\LaravelMysqlSpatial\Types\Point $location;
public static function fromRequest(PropertyCreateRequest $request): self
{
$self = new self([
'google_address_components' => collect(json_decode($request->input('raw_google_address_components'), true)),
'location' => new Point(
$request->input('latitude'),
$request->input('longitude')
),
]);
$self->updateFromGoogleAddressComponent();
return $self;
}
public function updateFromGoogleAddressComponent()
{
if (!empty($this->google_address_components)) {
$componentByType = $this->google_address_components->keyBy(function ($item) {
return \Arr::get($item, 'types.0', '');
});
$this->street_number = \Arr::get($componentByType, 'street_number.long_name', '');
$this->street = \Arr::get($componentByType, 'route.short_name', '');
$this->city = \Arr::get($componentByType, 'locality.long_name', '');
$this->neighborhood = \Arr::get($componentByType, 'neighborhood.short_name', '');
$this->province = \Arr::get($componentByType, 'administrative_area_level_1.short_name', '');
}
}
public function fullAddress()
{
$fullAddress = '';
if ($this->street) {
$fullAddress = $this->street;
if ($this->street_number) {
$fullAddress = $this->street_number . ' ' . $fullAddress;
}
}
if ($this->city) {
$fullAddress .= ', ' . $this->city;
}
if ($this->neighborhood) {
$fullAddress .= ', ' . $this->neighborhood;
}
if ($this->province) {
$fullAddress .= ', ' . $this->province;
}
return $fullAddress;
}
public function __toString()
{
return $this->fullAddress();
}
}
Update
Вобщем более менее адекватный VO получается и ни разу не DTO. Вроде всё норм.