Symfony Doctrine - сохранение внешнего ключа

fixxxer

К.О.
Партнер клуба
Ну, смотри

1) инструмент и документация к нему навязывают стиль мышления. Использовать DDD+CQRS-подход намного проще, если инструмент помогает, а не мешает.
2) поменьше бойлерплейта и стандартные документированные конструкции - программист существо ленивое и предпочитает идти по проторенной дорожке.

UPD: в целом жду того, что инструмент будет устроен так, что сделать правильно - проще и быстрее, чем неправильно
 
Последнее редактирование:

A1x

Новичок
Ты говоришь, что тебе не нравится $server->getCustomer()->getId(). Ты не уточнил почему, но, возможно, в силу избыточности/неэффективности.
ну да, это же дополнительный запрос или джойн

А где тогда находится твоя бизнес-логика?
бизнес-логику я выношу в сервисы, а в саму сущность которая для персистенции стараюсь не перегружать бизнес-логикой

$customer мне в общем то нужен в виде объекта в классе Server - всякие там гриды и тп, да Doctrine и не требует вносить все связи в сущность, кажется это называется bidirectional и unidirectional associations. Надо еще разобраться как это все работает при EntityManager#flush()

насчет разных персистентных сущностей для разных контекстов мысль интересная, но так и не пойму что мешает мне иметь приватные свойства $customer и $customerId в одной сущности, без отдельного сеттера для $customerId

как-то так
PHP:
/**
  * @param Customer $customer
  * @return Server
  */
  public function setCustomer($customer)
  {
  $this->customer = $customer;
  $this->customerId = $customer ? $customer->getId() : NULL;
  return $this;
  }
 

A1x

Новичок
UPD: в целом жду того, что инструмент будет устроен так, что сделать правильно - проще и быстрее, чем неправильно
а если сделал неправильно чтоб фигачило электрошоком, иначе все равно будут

точно так же как никто не будет обращать внимание на Notice пока не начать бросать вместо них исключения
 

Вурдалак

Продвинутый новичок
Ну, смотри

1) инструмент и документация к нему навязывают стиль мышления. Использовать DDD+CQRS-подход намного проще, если инструмент помогает, а не мешает.
2) поменьше бойлерплейта и стандартные документированные конструкции - программист существо ленивое и предпочитает идти по проторенной дорожке.

UPD: в целом жду того, что инструмент будет устроен так, что сделать правильно - проще и быстрее, чем неправильно
Я не зря спросил про то, что ты ждёшь от инструмента, потому что слабо себе представляю что именно тут вписывается в роль «инструмента».

Ты про command bus? Про маппинг сущности из БД? Про маппинг сущности на БД? Про диспетчеризацию событий? Про расположение папок в пакете? Про получение read model из БД?
Мы решали каждый вопрос индивидуально.

И следующий важный момент: разработчики привыкают делать по аналогии, даже если им что-то не очень нравится. Когда ты напишешь свой бойлерплейт (микрофреймворк со своими интерфейсами, правилами, соглашениями) и сделаешь свои модели, то другие не будут ломать эти модели, а будут пилить по аналогии. К примеру, если ты сделал сущность в стиле event sourcing, то для добавления нового метода сущности так или иначе придётся подумать над именем нового события, иначе просто не получится сохранить модель (нет события — нет изменений). В этом смысле ты начинаешь навязывать свой подход, придётся начинать думать командами и событиями.

При этом, ни один инструмент не заставит разработчика писать грамотные бизнес-события в стиле ServerWasTransferred вместо CRUD-like ServerWasUpdated. Это нужно просто понять.
 

Вурдалак

Продвинутый новичок
$customer мне в общем то нужен в виде объекта в классе Server - всякие там гриды и тп,
Иными словами — для отображения в отдельно взятых случаях. ServerExtendedReadModel {"id": 42, "customer": { ... }, ...} (грязноватое имя, но суть в том, что моделей на чтение может несколько).

насчет разных персистентных сущностей для разных контекстов мысль интересная
«Персистентная» — это та, которую сохраняют. Read models никуда не сохраняют по определению: они нужны только для чтения.
 

fixxxer

К.О.
Партнер клуба
В данный момент про
маппинг сущности из БД, маппинг сущности на БД, получение read model из БД
С остальным хорошие примеры есть и так.

При этом, ни один инструмент не заставит разработчика писать грамотные бизнес-события в стиле ServerWasTransferred вместо CRUD-like ServerWasUpdated. Это нужно просто понять.
Это решается документацией и туториалами.
 

Вурдалак

Продвинутый новичок
Domain model <-> БД псевдокодом:
PHP:
public function findById(int $fooId): Foo
{
    $doctrineEntity = $this->fooDoctrineRepository->find($id);
    return $this->hydrator->hydrate($doctrineEntity->toArray(), new Foo());
}

public function save(Foo $foo)
{
    $doctrineEntity = $this->entityManager->getReference(Foo::class, $id);

    foreach ($foo->events() as $event) {
        $this->{eventToMethodName($event)}($doctrineEntity, $event);
    }

    $this->fooDoctrineRepository->save($doctrineEntity);
}

private function onFooWasRenamed(FooDoctrineEntity $foo, FooWasRenamed $event) {
    $foo->setName($event->getNewName());
}
Ну, а read model можно получать через какой-нибудь $doctrineEntity->toReadModel(). Или из raw sql.
 
Последнее редактирование:

Sufir

Я не волшебник, я только учусь
Иными словами — для отображения в отдельно взятых случаях. ServerExtendedReadModel {"id": 42, "customer": { ... }, ...} (грязноватое имя, но суть в том, что моделей на чтение может несколько).
Кстати. Я в последнем проекте вообще в итоге забил на объекты и тупо массивы возвращал, т.к. получалось очень дофига вариаций. Либо кучу почти одинаковых объектов плодить пришлось бы, либо в один всё свойства добавить и полупустым таскать, или всегда с полной инфой...

Много мест, где одни и те же "сущности" нужно отображать, но с разными наборами данных. Получалось ClientReadModel, ClientWithServicesReadModel, ClientWithLastContactReadModel и т.д., а когда понадобились всякие ClientWithServicesAndWithLastContactReadModel забил и стал массивами пользоваться, в ClientExtendedReadModel всё-всё сразу напихать тоже не вариант (сначала так и делал, были basic, extended и detailed), данных разных много - куча запросов и джойнов вхолостую гонять там где они не нужны.

Есть идеи, как в подобных случаях с ReadModel быть? Массивы всё-таки не так удобны...
 

AnrDaemon

Продвинутый новичок
Lazy load редкоиспользуемых параметров?
Хак конечно, но… Если количество моделей переходит все разумные границы, явно, что-то надо делать.
 

Вурдалак

Продвинутый новичок
Много мест, где одни и те же "сущности" нужно отображать, но с разными наборами данных. Получалось ClientReadModel, ClientWithServicesReadModel, ClientWithLastContactReadModel и т.д., а когда понадобились всякие ClientWithServicesAndWithLastContactReadModel забил и стал массивами пользоваться, в ClientExtendedReadModel всё-всё сразу напихать тоже не вариант (сначала так и делал, были basic, extended и detailed), данных разных много - куча запросов и джойнов вхолостую гонять там где они не нужны.
Если они тупо сериализуются в API, то в итоге те же массивы и получаются. Можно вытащить наружу API наподобие того, как это сделано в GraphQL: клиент сам запрашивает список полей, а не ты пилишь отдельную read model под каждый отдельно взятый запрос.

Возможно также, что ты пытаешься заниматься лишней оптимизацией. Ну, заткни узкое место кешом, чо :)
 

fixxxer

К.О.
Партнер клуба
Массивы всё-таки не так удобны...
Судя по задаче, тебе надо просто взять поля и отдать в шаблон или вообще наружу через json, тут уж какая разница-то?
Если в шаблон и хочется автокомплитов - ну, никто не мешает сделать read model, где куча полей nullable, а вытаскивать только то, что надо. Запрос генерить маппиннгом, вот из чего-то типа как в GraphQL, да.
 

Sufir

Я не волшебник, я только учусь
Судя по задаче, тебе надо просто взять поля и отдать в шаблон или вообще наружу через json, тут уж какая разница-то?
В шаблон. Так-то да, получаемый DTO - тот же ассоциативный массив. В принципе всё верно, но у объекта автокомплит, плюс можно простенький сахарок подкрутить, вроде $client->isSomeStatus() вместо $client['status'] === ClientStaus::SOME_STATUS
 

fixxxer

К.О.
Партнер клуба
@Sufir, ну, пачку nullable-ов и делов. Будет как массив, только немного лучше.
 
Сверху