如何在 Symfony 中继承自定义 Doctrine class 注解?

How to inherit custom Doctrine class annotation in Symfony?

我创建了一个自定义 @TimestampAware 注释,可用于在持久化或更新我的实体时自动更新时间戳 属性(请参阅下面的代码)。

直接在实体上使用此注释时 class 一切正常。但是,当在基础 class 上使用注释时,它在继承的子 class 上无法识别。

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

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

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

Doctrine 注释和注释 Reader class 的预期行为是否仅直接在当前 class 上查找注释而不包括其父 class是吗?还是我的实现有问题?

我的注释:

use Doctrine\Common\Annotations\Annotation;
use Doctrine\Common\Annotations\Reader;

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

注解监听器:

use Doctrine\Common\EventSubscriber;

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...
    }
}

Annotation Reader 只检查相关的 class,它不读取父 class 的注释。这可以很容易地用这样的代码来检查:

use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;

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`

如果您想检查父 classes,您必须自己做。 ReflectionClass 提供了 getParentClass() 方法,您可以用它来检查 class 层次结构。

切线地,我发现 this package 声称扩展注释以便它们可以直接用于继承。还没看好不好用

除了@yivi 的精彩回答之外,我还想分享一下我用来解决问题的代码。也许这可以帮助其他人如何遇到同样的问题:

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
    );
    
    while (!$timestampAware && $reflection = $reflection->getParentClass()) {
        $timestampAware = $this->reader->getClassAnnotation(
            $reflection,
            TimestampLogAware::class
        );
    }

    if (!$timestampAware) {
        return;
    }

    // update timestamp...
}