Skip to content
Advertisement

How to inherit custom Doctrine class annotation in Symfony?

I have created a custom @TimestampAware annotation which can be used to automatically update a timestamp property when persisting or updating my entities (see code below).

When using this annotation directly on an entity class everything works fine. However, when using the annotation on a base class, it is not recognized on the inherited sub-classes.

/**
 * @TimestampAware
 */
class SomeEntity { ... }  // works fine

 /**
 * @TimestampAware
 */
class BaseEntity { ... }

class SubEntity extends BaseEntity { ... } // Annotation is not recognized

Is it the intended behavior of Doctrine annotations and the Annotation Reader class to only look for annotation directly on the current class and no include its parent classes? Or is there something wrong with my implementation?

My annotation:

use DoctrineCommonAnnotationsAnnotation;
use DoctrineCommonAnnotationsReader;

/**
 * @Annotation
 * @Target("CLASS")
 */
final class TimestampAware { }

The annotation listener:

use DoctrineCommonEventSubscriber;

class TimestampAwareSubscriber implements EventSubscriber {
    protected $reader;
    protected $logger;

    public function __construct(Reader $reader, LoggerInterface $logger) {
        $this->reader = $reader;
        $this->logger = $logger;
    }

    public function getSubscribedEvents() {
        return [
            Events::prePersist,
            Events::preUpdate,
        ];
    }

    public function prePersist(LifecycleEventArgs $args) {
        $this->onPersistOrUpdate($args);
    }    

    public function preUpdate(LifecycleEventArgs $args) {
        $this->onPersistOrUpdate($args);
    }    

    protected function onPersistOrUpdate(LifecycleEventArgs $args) {
        $this->logger->info("Reader: ".get_class($this->reader));
 
        $entity = $args->getEntity();
        $reflection = new ReflectionClass($entity);
    
        $timestampAware = $this->reader->getClassAnnotation(
            $reflection,
            TimestampAware::class
        );
    
        if (!$timestampAware) {
            return;
        }

        // update timestamp...
    }
}

Advertisement

Answer

The Annotation Reader inspects only the relevant class, it does not read the annotations of the parent class. This can easily be checked with code like this:

use DoctrineCommonAnnotationsAnnotationReader;
use DoctrineCommonAnnotationsAnnotationRegistry;

AnnotationRegistry::registerLoader('class_exists');

/**
 * @Annotation
 * @Target("CLASS")
 */
class Annotated {}

/**
 * @Annotated
 **/
class ParentFoo {}

class ChildFoo extends ParentFoo {}

$reader = new AnnotationReader();

$parentAnnotated = $reader->getClassAnnotation(
    new ReflectionClass(ParentFoo::class),
    Annotated::class
);

var_dump($parentAnnotated);
// Outputs `object(Annotated)#10 (0) {}`

$childAnnotated = $reader->getClassAnnotation(
    new ReflectionClass(ChildFoo::class),
    Annotated::class
);

var_dump($childAnnotated);

// outputs `null`

If you want to check parent classes, you’ll have to do it yourself. ReflectionClass provides the getParentClass() method which you could do to check the class hierarchy.

Tangentially, I’ve found this package that claims to extend annotations so that they are usable with inheritance directly. Haven’t checked if it’s any good.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement