Lazy instantioning from PHP 8.4
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);
}
}
This is technically possible even without PHP 8.4. It needs a few hacks, but I managed to create quite clear implementation in orisai/object-mapper. I can help with backporting it if it's welcome. My current project will be stuck on PHP 7.4 for a while, so I can't test new cool stuff...
Btw, there should be a way to distinguish between mapped property and regular property - for dependencies and memory cache.
Just from the first sight, it seems that the definition is more verbose. Hard to see this is the way.
Just from the first sight, it seems that the definition is more verbose. Hard to see this is the way.
I agree. More verbose and harder to read. That's beauty of current implementation, easy to read and write.