Skip to content

Lazy instantioning from PHP 8.4 #683

Open
@hrach

Description

@hrach

With the newly added feature, maybe, there is a new way how to write entities.
Consequently, this would utilize property types, annotations & property hooks as well.

So the original entity like:

/**
 * @property int|null                $id              {primary}
 * @property string                  $name
 * @property DateTimeImmutable|null  $born            {default "2021-03-21 08:23:00"}
 * @property string                  $web             {default "http://www.example.com"}
 * @property Author|null             $favoriteAuthor  {m:1 Author::$favoredBy}
 * @property OneHasMany<Author>      $favoredBy       {1:m Author::$favoriteAuthor}
 * @property OneHasMany<Book>        $books           {1:m Book::$author, orderBy=[id=DESC], cascade=[persist, remove]}
 * @property OneHasMany<Book>        $translatedBooks {1:m Book::$translator}
 * @property OneHasMany<TagFollower> $tagFollowers    {1:m TagFollower::$author, cascade=[persist, remove]}
 * @property-read int                $age             {virtual}
 */
final class Author extends Entity
{
	protected function getterAge(): int
	{
		if ($this->born === null) {
			return 0;
		}

		return ((int) date('Y')) - ((int) $this->born->format('Y'));
	}
}

would become

class Author extends Entity
{
    #[Primary]
    public int|null $id;
    public string $name;
    #[Default("2021-03-21 08:23:00")]
    public DateTimeImmutable|null $born;
    public string $web = "http://www.example.com";

    #[ManyHasOne(Author::class, "favoredBy")]
    public Author|null $favoriteAuthor;

    #[OneHasMany(Author::class, "favoriteAuthor")]
    public OneHasMany $favortedBy;

    #[OneHasMany(Book::class, 'author', orderBy: ['id' => 'desc'], cascade: ['persist', 'remove'])
    public OneHasMany $books;

    #[OneHasMany(Book::class, 'translator')
    public OneHasMany $translatedBooks;

    #[OneHasMany(TagFollower::class, 'author', cascade: ['persist', 'remove'])
    public OneHasMany $tagFollowers;

    public int $age {
       get {
           if ($this->born === null) return 0;
           return ((int) date('Y')) - ((int) $this->born->format('Y'));
       }
    }

    // example of still public/open constructor
    public __construct(string $name) {
        parent::__construct();
        $this->name = $name;
    }
}

Implementation comments:

  • *HasOne relationships's backing relationship instance would have to be stored somewhere else, i.e. property wrappers have to be separated into user-facing and backing field ones and the second has to be handled separately.
  • There is still an open question of how "setting" a hasOne relationship could properly propagate to its backing field relationship; a required setter with sideffect? or utilize hooks to avoid hidden backing-fields?
  • The whole mechanism could be implemented as another option and it would be up to the user when to migrate to this new way of entity definition.
  • old getters/setters would be deprecated

HasOne options:

class Author {
    #[ManyHasOne(Author::class, "favoredBy")]
    private ManyHasOne $favoriteAuthorRel;
    public Author|null $favoriteAuthor { get => $this->favoriteAuthorRel->getEntity(); }
}

or

class Author {
    #[ManyHasOne(Author::class, "favoredBy")]
    public Author|null $favoriteAuthor { 
       set {
          $this->favoriteAuthor = $value;
          $this->updateReverse('...', $value);
       }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions