Reputation: 522
I have implemented my own Doctrine\Common\EventSubscriber
that subscribes to "onFlush" events and compares the EntityChangeSet to a hard coded list of class properties (like User->Name
and Event->Date
) and logs the change if necessary.
As property names of the other classes might change, I'd much rather prefer to annotate these properties with a custom made @Loggable
.
I have built the Annotation class "Loggable", added use MyNamespace\Annotations\Loggable;
to the User
and Event
classes and have a method in my EventSubscriber that creates a Doctrine\Common\Annotations\AnnotationReader
and uses its getPropertyAnnotation($property, 'Loggable')
to check for a non-null value.
As one could expect (or not), this particular reader throws
[Semantical Error] The annotation "@Id" in property MyNamespace\Entities\User::$id was never imported.
and is not aware of any other ORM-annotations that the reader in my EntityManager
knows about.
Do I actually have to add use Doctrine\ORM\Mapping as ORM;
to every Entity class and prefix every ORM-annotation with ORM\
just to please this newly created reader or is there a way to reuse the reader in my EntityManager
(a Doctrine\Common\Annotations\SimpleAnnotationReader
by default, if I understood correctly?)
I did my research and read through most of the answers related to Doctrine and Annotations, but I seem to be missing some conceptual understanding. Maybe someone can point me in the right direction?
Upvotes: 0
Views: 1473
Reputation: 522
Thanks to @Frank Gamess' explanations I finally managed to wade through the jungle of Doctrine's codebase and find a messy, but working solution, that doesn't involve explicit namespaces for every file.
Instead of using the simpler Setup::createAnnotationMetadataConfiguration()
when creating my EntityManager, I went a bit more verbose and performed most of the steps of the configuration myself:
// preloading all of Doctrine's Annotation classes
$rc = new \ReflectionClass(Configuration::class);
$dir = dirname($rc->getFileName());
AnnotationRegistry::registerFile($dir . '/Mapping/Driver/DoctrineAnnotations.php');
// preloading my own classes
$dir = $dir ?? self::BASE_DIR . '/src/Annotations'; // BASE_DIR is my project root directory
// CustomAnnotations.php is built the same way as DoctrineAnnotations.php;
AnnotationRegistry::registerFile($dir . '/CustomAnnotations.php');
$reader = new SimpleAnnotationReader();
$reader->addNamespace('Doctrine\ORM\Mapping');
$reader->addNamespace('MyProject\Annotations');
$mapping_driver = new AnnotationDriver($reader, [self::BASE_DIR . '/src/Entities']);
$config = Setup::createConfiguration();
$config->setMetadataDriverImpl($mapping_driver);
$config->setAutoGenerateProxyClasses(true);
$EM = EntityManager::create($db_params, $config, new EventManager());
// adding the objects to my ORM-objects for later use
$this->annotation_reader = $reader;
$this->EM = $EM;
Now I can use my annotations without any use-statements or FQCN. Maybe not cleaner, but less repetitive.
As this works, I might be able to go on to more urgent things, but if anyone can point out possibilities for refactoring, I'm all ears!
Upvotes: 0
Reputation: 1324
Basically, the AnnotationReader class use getClassAnnotations and getClassAnnotation (which use getClassAnnotations in fact).
getClassAnnotations:
public function getClassAnnotations(ReflectionClass $class)
{
$this->parser->setTarget(Target::TARGET_CLASS);
$this->parser->setImports($this->getClassImports($class));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
}
getClassAnnotation:
public function getClassAnnotation(ReflectionClass $class, $annotationName)
{
$annotations = $this->getClassAnnotations($class);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
Just take a look at this line:
$this->parser->setImports($this->getClassImports($class));
The annotation reader collects the use statements in order to detect any other annotation that you use. So, about your question
Do I actually have to add use Doctrine\ORM\Mapping as ORM; to every Entity class and prefix every ORM-annotation with ORM\ [...]?
I should answer: yes, you do.
Upvotes: 2