0
votes

Problem: ¿How can I fetch all applications with a given status?

Hypothesis:

  • The application's status is it's last event.
  • Events have incremental IDs.

Events:

<?php

namespace DnD\RaHApiBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints\DateTime;

/**
 * Event
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Event
{

  public function __construct($name = null, $type = null) {
    if (isset($name)) $this->name = $name;
    if (isset($type)) $this->type = $type;
    $this->eventDate = date_create(date("Y-m-d\TH:i:sO"));
  }

  /**
   * @var integer
   *
   * @ORM\Column(name="id", type="integer")
   * @ORM\Id
   * @ORM\GeneratedValue(strategy="AUTO")
   */
  private $id;

  /**
   * @var string
   *
   * @ORM\Column(name="type", type="string", length=255, nullable=true)
   * @Groups({"application_info"})
   */
  private $type;

  /**
   * @var string
   *
   * @ORM\Column(name="name", type="string", length=255)
   * @Groups({"application_info"})
   */
  private $name;


  /**
   * @var date of event.
   *
   * @ORM\Column(name="event_date", type="datetime")
   * @Groups({"application_info"})
   */
  private $eventDate;

  ...
}

Applications:

<?php

namespace DnD\RaHApiBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints\DateTime;

/**
 * Solicitud de registro.
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="DnD\RaHApiBundle\Repository\ApplicationRepository")
 */
class Application
{

  public function __construct()
  {
    $pending = new Event("PENDING", "APPLICATION");

    $this->status = new ArrayCollection($pending);
  }

  /**
   * @var integer
   *
   * @ORM\Column(name="id", type="integer")
   * @ORM\Id
   * @ORM\GeneratedValue(strategy="AUTO")
   * @Groups({"application_info"})
   */
  private $id;

  /**
   * @var string
   *
   * @ORM\Column(name="name", type="string", length=255)
   * @Groups({"application_info"})
   */
  private $name;

  /**
   * @var string
   *
   * @ORM\Column(name="dni", type="string", length=255)
   * @Groups({"application_info"})
   */
  private $dni;

  /**
   * @var string
   *
   * @ORM\Column(name="email", type="string", length=255)
   * @Groups({"application_info"})
   */
  private $email;

  /**
   * @var string
   *
   * @ORM\ManyToMany(targetEntity="DnD\RaHApiBundle\Entity\Event", cascade={"persist"})
   * @ORM\JoinTable(
   *      name="applications_events",
   *      joinColumns={@ORM\JoinColumn(name="application_id", referencedColumnName="id")},
   *      inverseJoinColumns={@ORM\JoinColumn(name="event_id", referencedColumnName="id", unique=true)}
   * )
   * @Groups({"application_info"})
   */
  private $status;
}

¿Is there a way to get an Application with a given "last status"?

2

2 Answers

0
votes

You can make a custom query in your Application repository.

With a doctrine 2 QueryBuilder your query could look something like this:

public function getApplicationsByStatusId($id)
{
    $applications = $this->createQueryBuilder('a')
        ->innerJoin('a.status', 's')
        ->where('s = :status_id')
        ->setParameter('status_id', $id)
        ->getQuery()
        ->getResult();

    return $applications;
}

Since it is a ManyToMany association your result can be plural.

0
votes

For event sourcing implementation you can use package rela589n/doctrine-event-sourcing:


class Message implements AggregateRoot
{
    private Uuid $uuid;

    private MessageContent $content;

    private MessageStatus $status;

    private Carbon $createdAt;

    private Carbon $updatedAt;

    private User $user;

    private Chat $chat;

    /** @var Collection<MessageEvent> */
    private Collection $recordedEvents;

    /** @var MessageEvent[] */
    private array $newlyRecordedEvents = [];

    private function __construct(Uuid $uuid)
    {
        $this->uuid = $uuid;
        $this->status = MessageStatus::NEW();
        $this->recordedEvents = new ArrayCollection();
    }

    public static function write(Uuid $uuid, User $user, Chat $chat, MessageContent $content): self
    {
        $message = new self($uuid);
        $message->recordThat(
            MessageWritten::withData($message, $user, $chat, $content),
        );

        return $message;
    }

    public function edit(MessageContent $newContent): void
    {
        $this->recordThat(
            MessageWasEdited::with($this, $this->content, $newContent),
        );
    }

    private function recordThat(MessageEvent $event): void
    {
        switch (true) {
            case $event instanceof MessageWritten:
                $this->createdAt = \Carbon\Carbon::instance($event->getTimestamp());
                $this->user = $event->getUser();
                $this->chat = $event->getChat();
                $this->content = $event->getContent();
                $this->chat->addMessage($this);
                break;
            case $event instanceof MessageWasEdited:
                $this->content = $event->getNewContent();
                $this->status = $this->status->changingInto(MessageStatus::EDITED());
                break;
            default:
                throw new UnexpectedAggregateChangeEvent($event);
        }

        $this->updatedAt = \Carbon\Carbon::instance($event->getTimestamp());
        $this->newlyRecordedEvents[] = $event;
        $this->recordedEvents->add($event);
    }

    public function releaseEvents(): array
    {
        $events = $this->newlyRecordedEvents;
        $this->newlyRecordedEvents = [];
        return $events;
    }

    public function getPrimary(): Uuid
    {
        return $this->uuid;
    }

    public static function getPrimaryName(): string
    {
        return 'uuid';
    }
}

Where Message is your entity, MessageWritten and MessageWasEdited are events which will be saved in message events table.