Skip to content

Optional entities

In the previous chapter you've seen what an Entity is. But an Entity can't always be required!

For our example, not every User wan't to specify an Address.

This is how you make it optional:

User.php
use Formal\ORM\{
    Id,
    Definition\Contains,
};
use Innmind\Immutable\Maybe;

final readonly class User
{
    /**
     * @param Id<self> $id
     * @param Maybe<Address> $address
     */
    private function __construct(
        private Id $id,
        private Name $name,
        #[Contains(Address::class)]
        private Maybe $address,
    ) {}

    public static function new(Name $name): self
    {
        return new self(Id::new(self::class), $name, Maybe::nothing());
    }

    public function name(): Name
    {
        return $this->name;
    }

    public function rename(Name $name): self
    {
        return new self($this->id, $name, $this->address);
    }

    public function addAddress(Address $address): self
    {
        return new self(
            $this->id,
            $this->name,
            Maybe::just($address),
        );
    }
}

The #[Contains(Address::class)] tells Formal the kind of Entity contained inside the Maybe.

The Maybe monad is a way to describe the possible absence of data. This is kind of a nullable type.

Note

Formal doesn't use null to represent the possible absence of an Entity as it would force it to load all optional entities when fetching the aggregate.

With Maybe it can lazy load them when first used after fetching an aggregate from the storage.

If you're not familiar with the Maybe monad you can start learning it here. You can follow up by reading this documentation describing all its methods.

Info

The Contains attribute is here to avoid to have to parse the docblock to extract the information specified for static analysis tools such as Psalm.

If a standard emerges between static analysis tools in attributes to specify this kind of information then Formal may migrate to it.