data-mapper или active-record

Вурдалак

Продвинутый новичок
на уровне middleware (url типа /order/{id}) создается готовый заказ. далее действие /order/{id}/add/product/{id}
те на уровне контролера мы имеем все что записано в руте стало обьектом остальное в request
Я думал мы ведём речь про модель, а не про какие-то детали внешнего API. Ну, и что, в Order не будет id? Как это сохраняется потом в DataMapper, как ты создаёшь событие, не зная id?
 

WMix

герр M:)ller
Партнер клуба
Я думал мы ведём речь про модель, а не про какие-то детали внешнего API
хм, я запутался, что же такое $order если не модель?
Как это сохраняется потом в DataMapper, как ты создаёшь событие, не зная id?
DataMapper знает. грубо так
PHP:
/** @var $entities \SplObjectStorage */
$entities[ $order ] = $identity;
 

WMix

герр M:)ller
Партнер клуба
Так как ты будешь создавать событие
PHP:
function addProduct($order, $product, $quantity ){
  $this->em->trigger(...);
}
Как ты будешь выполнять то же действие из консоли?
чтонить вроде этого
Код:
$ php app.php order:addproduct --order=42 --product=55 --qty=22
команда практически не отличается от контролера, возможно только обьекты придется добыть ручками (middleware не работает, но это можно поправить)

хотя вероятнее там демон и тебя он интересует
 

Вурдалак

Продвинутый новичок
Понятно. Вместо команды AddProduct { orderId: 42, productId: 55 } ты сделал middleware для HTTP-контекста, который будешь немного допиливать для CLI, а ещё будешь использовать MySQL DataMapper для создания событий, о которых он даже понятия не имеет. Это всё упрощает.
 

Вурдалак

Продвинутый новичок
PHP:
$mapper->identity($order);
Каким образом ты понимаешь какие события нужно сделать? Ты отменяешь заказ, потом ещё раз отменяешь. Два раза сделаешь событие OrderWasCancelled? Или ты проверишь состояние if ($order->isCancelled()) { ... } в сервисе?
 

WMix

герр M:)ller
Партнер клуба
сразу и не отвечу, но спонтанно думаю както так
PHP:
class Order{
  public function cancel(){
    if( !in_array($this->stautus, [...]){
      throw new Exception(...);
    }
    $this->status = 'cancel';
  }
}
$order->cancel();
$mapper->store($order);
$em->trigger(new OrderWasCancelled($mapper->identity($order)));
 

Вурдалак

Продвинутый новичок
сразу и не отвечу, но спонтанно думаю както так
PHP:
class Order{
  public function cancel(){
    if( !in_array($this->stautus, [...]){
      throw new Exception(...);
    }
    $this->status = 'cancel';
  }
}
$order->cancel();
$mapper->store($order);
$em->trigger();
OK, другой пример: у тебя есть статья, которая должна быть зааппрувлена корректором ($article->approveByProofreader()) и главным редактором ($article->approveByChiefEditor()). Они могут это сделать в произвольной последовательности. Когда они оба её заппрувят, то статья должна перейти в статус «готовой к публикации», должно возникнуть соответствующее событие ArticleBecameReadyToPublish и по этому событию автору статьи отправят письмо, что он может её опубликовать. Как ты поймёшь, что какой-то из этих двух методов перевёл статью в статус «готовой к публикации»? Если аппрув будет только от одного, то статья ещё не готова к публикации.
 

WMix

герр M:)ller
Партнер клуба
ну те вопрос в какой момент мы на уровне сервиса получим знание что статья готова к публикации?
PHP:
interface{
  function approveByChiefEditor():boolean
  function approveByProofreader():boolean
}
или
PHP:
interface{
  function approveByChiefEditor():void
  function approveByProofreader():void
  function isReady():boolean
}
я тендирую к первому варианту, возможно сокращенному
PHP:
interface{
  function approve(Employee $employee):boolean
}
но хотелось бы услышать твое предложение
 

Вурдалак

Продвинутый новичок
я тендирую к первому варианту, возможно сокращенному
PHP:
interface{
  function approve(Employee $employee):boolean
}
но хотелось бы услышать твое предложение
Если обобщить, то ты просто возвращаешь информацию о том, что произошло. А эта информация равноценна событиям, поэтому их можно записывать внутри сущности:
PHP:
$article->approveByChiefEditor();
$this->articleMapper->save($article);
$this->eventDispatcher->dispatch($article->getEvents());
И id статьи там очень в этом пригодится:
PHP:
public function approveByChiefEditor(): void
{
    if ($this->approvedByChiefEditor) {
        return;
    }

    $this->approvedByChiefEditor = true;
    $this->recordThat(new ArticleWasApprovedByEditor($this->id));

    if ($this->approvedByProofreader) {
        $this->recordThat(new ArticleBecameReadyToPublish($this->id));
    }
}
 

WMix

герр M:)ller
Партнер клуба
@Вурдалак, спасибо, теперь я буду переваривать пару деньков

хочется сказать можно сделать так
PHP:
new ArticleBecameReadyToPublish($this);
те перенести eventManager в entity
но чую придется маппер привязывать к eventManager'у чтоб id добыть
 

WMix

герр M:)ller
Партнер клуба
PHP:
$this->recordThat(new ArticleWasApprovedByEditor($this->id));
$this->eventDispatcher->dispatch($article->getEvents());
сразу представляю себе нечто подобное
PHP:
abstract class Entity{
  /**
   * @var Event[]
   */
  private $events = [];
  function recordThat(Event $event){
    $this->events[] = $event;
  }
  function getEvents(){
    return $this->events[];
  }
}
class Article extends Entity{...}

class ArticleService{
  private $eventDispatcher;
  // ...
}
те на самом деле, сама сущность на прямую не общается с EventDispatcher?
 

Вурдалак

Продвинутый новичок
сразу представляю себе нечто подобное
...
те на самом деле, сама сущность на прямую не общается с EventDispatcher?
Да, всё так. Я правда использую трейт всего abstract class Entity.

Напрямую использовать $eventDispatcher нельзя, потому что в этот момент изменения сущности ещё не закоммичены.
 

Adelf

Administrator
Команда форума
function getEvents(){
return
$this->events[];
}
Я еще видел когда в этотм методе массив чистился. Ибо единственный кто этот метод юзает - это команда, которая посылает все в dispatcher. И на всякий случай лучше вычистить чтобы не послать вторично.
 

Вурдалак

Продвинутый новичок
Я еще видел когда в этотм методе массив чистился. Ибо единственный кто этот метод юзает - это команда, которая посылает все в dispatcher. И на всякий случай лучше вычистить чтобы не послать вторично.
У нас метод выглядит как-то так:
PHP:
public function release() {
    $events = $this->eventHistory;
    $this->eventHistory = [];
    return $events;
}
 

WMix

герр M:)ller
Партнер клуба
@Вурдалак, а не получается ли нарушение tell don't ask
PHP:
$entity->tell();
$events = $entity->get();
а можно же так

PHP:
class Order{
  function cancel(){
    // ...
   return [OrderWasCancelled::class];
  }
}

class Service{
  function cancel($id){
   $order = $this->repo->get($id);
   foreach($order->cancel() as $class){
      $this->eventDispatcher->dispatch(new $class($id));
   }
   $this->store($order);
  }
}
 
Сверху