VolCh
VolCh

Reputation: 369

What is the best way to set/update association lookup field inside Doctrine entity

I have the main entity

/**
 * @ORM\Entity()
 */
class Document
{
    /**
     * @var int
     * @ORM\Id()
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @var DocumentStatus
     * @ORM\ManyToOne(targetEntity="DocumentStatus")
     */
    private $status;
    /**
     * @var string
     * @ORM\Column(type="text")
     */
    private $text;
}

and the lookup "enum" entity (seeding on application deploy)

/**
 * @ORM\Entity(repositoryClass="DocumentStatusRepository");
 */

class DocumentStatus
{
    const DRAFT = 'draft';
    const PENDING = 'pending';
    const APPROVED = 'approved';
    const DECLINED = 'declined';
    /**
     * @var int Surrogate primary key
     * @ORM\Id()
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string  Natural primary key (name for developers)
     * @ORM\Column(type="string", unique=true)
     */
    private $key;
    /**
     * @var string Short name for users
     * @ORM\Column(type="string", unique=true)
     */
    private $name;
    /**
     * @var string Full decription for users
     * @ORM\Column(type="string", nullable=true, unique=true)
     */
    private $description;
}

with simple repository

class DocumentStatusRepository extends EntityRepository
{
    public function findOneByKey($key)
    {
        return parent::findOneBy(['key' => $key]);
    }
}

I want to encapsulate domain logic of document lifecycle by intoducing methods like

public function __construct($text)
{
   $this->text = $text;
   $this->status = $something->getByKey(DocumentStatus::DRAFT);
}

public function approve()
{
    try {
        $this->doSomeDomainActions();
        $this->status = $something->getByKey(DocumentSatus::DRAFT);
    } catch (SomeDomainException($e)) {
        throw new DocumentApproveException($e);
    }
}
...

or

public function __construct($text)
{
   $this->text = $text;
   $this->status = $something->getDraftDocumentStatus()
}

public function approve()
{
    $this->status = $something->getApprovedDocumentStatus()
}
...

without public setters. Also I want keep Document loose coupling and testable.

I see the next ways:

Are there other ways? Which way is easier and more convenient to use in the long term?

Upvotes: 1

Views: 164

Answers (1)

Dariss
Dariss

Reputation: 1258

  1. Using generated Identities for Document.
    Now you generate the identity on the side of the database. So you save Document from the domain perspective in inconsistent state. Entity/Aggregate should be identified, if it has no id it shouldn't exists.
    If you really want to keep to database serials, add method to the repository which will generate id for you.
    Better way is to use uuid generator for example ramsey/uuid. And inject the id to the constructor.

  2. DocumentStatus as Value Object
    Why Document Status is Entity? It does look like a simple Value Object. Then you can use Embeddable annotation. So it will leave within same table in the database, no need for doing inner joins.
    DocumentStatus gets behaviour for example ->draftDocumentStatus(), which returns NEW DocumentStatus with draft status, so you can switch the old instance one with new one. ORM will do the rest.

  3. DocumentStatusRepository
    If you really want to keep DocumentStatus as entity, which in my opinion is wrong you shouldn't have DocumentStatusRepository.
    Document is your aggregate root and the only entrance to the DocumentStatus, should be by aggregate root.
    So you will have DocumentRepository only, which will be responsible for rebuilding the whole aggregate and saving it. Also you should change the mapping.
    It should have FETCH=EAGER type, so it will retrieve DocumentStatus with Document together.
    Secondly you should do mapping with CASCADE=ALL and ORPHANREMOVAL=TRUE.
    Otherwise, if you remove Document, DocumentStatus will stay in the database.

Upvotes: 1

Related Questions