nextras / orm

Orm with clean object design, smart relationship loading and powerful collections.
https://nextras.org/orm
MIT License
310 stars 59 forks source link

Property hooks from PHP 8.4 #684

Open hrach opened 1 month ago

hrach commented 1 month ago

As PHP 8.4 comes with property hooks we could remove virtual properties, as they would be completely modelled by a custom property with getter hook.

This:

/** ...
 * @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

/** ...
 */
final class Author extends Entity
{
    public int $age {
        get {
            if ($this->born === null) {
                return 0;
            }

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

Please note, that this would not mean removing custom getters/setters support, as they're still needed for non-virtual properties.

mskocik commented 4 weeks ago

I have an use case where I fetch "extended" entity with properties that are not available in original table. Something like:

/**
 * ...
 * @property-read DateTimeImmutable $changedAt
 */
class JoinedEntity extends Entity {}

Where value for $changedAt comes from joined table:

SELECT
  pt.*,
  l.changed_at
FROM primary_table pt
LEFT JOIN (SELECT l.change_at FROM ... )

When given entity is being persisted, these extended properties are marked as virtual to avoid ORM attempting to persist them. After persisting they are reset.

    // Required for correct behavior on persisting values not present in mapper DB table
    public function onBeforeInsert(): void
    {
        parent::onBeforeInsert();
        $this->metadata->getProperty('changedAt')->isVirtual = true;
        $this->metadata->getProperty('changedBy')->isVirtual = true;
    }

    public function onAfterInsert(): void
    {
        parent::onAfterInsert();
        $this->metadata->getProperty('changedAt')->isVirtual = false;
        $this->metadata->getProperty('changedBy')->isVirtual = false;
    }

I don't remember exactly why it was implemented this way, but it was the only solution we came with.