In a recent project (virtualidentityag/hydra-twitter) i tried to create a Symfony2 form field that lets you choose from the list of entities managed by doctrine (so that the user could decide which entity he wants to map a REST-resultset to). I thought that there was no easy way doing this. Heres how i found a solution.
My first guess was to kind of reflect the schema and found that there exists a SchemaManager. Unfortunately creating a schema using the schema manager was not helping, i could not derive the class names of the entities. However i was able to find a list of tables that are currently managed (i am working inside a symfony2 controller):
$em = $this->getDoctrine()->getManager(); $tables = $em->getConnection()->getSchemaManager()->listTables();
The tables are all instances of Doctrine\DBAL\Schema\Table. You cannot derive the entity class from the table directly because doctrine does not work that way. Doctrine uses a Hydrator to “deserialize” the result from the database to your own entities.
My second guess was to search Doctrine for functions that would return the full qualified class name for a Table instance. Unfortunately this was not possible. In fact i have to admit that i did not fully understand how doctrine resolves the names that you use in DQL-statements because the dql-parsing code of Doctrine is quite complicated.
As i remembered from Symfony2 example code you do not have to provide the FQCN in a DQL query – it suffices to write “YourBundle:YourEntity”. Doctrine will then automatically find your Entity-class name by getting the namespace for your bundle, supposing you have a sub-namespace called “Entity” and then construct the class name from that coding convention.
But my code should not be dependent from bundle names or any bundle at all. So hard coding this into my code (and following the Symfony/Doctrine convention) was no option for this situation.
On further investigation of Doctrines code i found a way to list all namespaces that are searched for entities using the configuration-object of Doctrine:
$em = $this->getDoctrine()->getManager(); $namespaces = $em->getConfiguration()->getEntityNamespaces();
From here on the next step was easy although dirty and i didn’t like what i’ve done here:
// warning: don't use that! $entities = array(); $em = $this->getDoctrine()->getManager() $tables = $em->getConnection()->getSchemaManager()->listTables(); $namespaces = $em->getConfiguration()->getEntityNamespaces(); foreach ($tables as $table) { foreach ($namespaces as $namespace) { if (class_exists($namespace.'\\'.$table->getName())) { break; } } $entities[] = $namespace.'\\'.$table->getName(); }
I showed the code to a friend of mine (@traktorjosh) and he pointed out, that my code would stop working the moment somebody decides to give his table a different name using the Table-annotation: @Table(name=”notWorking”).
Back to start. I was frustrated. Then i decided to take a look at the UpdateCommand that is called when you execute “app/console doctrine:schema:update”. And there i found what i desperately was looking for: the getAllMetadata-method. Who thought it might hide here? The method returns a list of ClassMetadata objects which let you “Get fully-qualified class name of this persistent class”. So the final solution was straight forward:
$entities = array(); $em = $this->getDoctrine()->getManager(); $meta = $em->getMetadataFactory()->getAllMetadata(); foreach ($meta as $m) { $entities[] = $m->getName(); }
Finally it looks like i have found a clean method listing all entities. Again hours of headache for 4 lines of code. If you have any suggestions how to make this better please let me know.
doctrine entity symfony
thanx, this was very useful,… have a question thought, this displays a complete path to the entities, how would i make it display one word that links to a my own route,..
@Don , this depends on how your routes are defined.
if by path you mean the namespace it could get you problems if you strip it off. if you reduced the class to its simple name e.g. by array_pop(explode(‘\\’, $m->getName())) you might have naming conflicts and your routes might not be identified correctly.
however this is not true if you are sure you are only using entities from one namespace.
post the code how you plan to create the links to your route on http://pastebin.com/ and i might be able to help!
To get entities for one bundle in particular, you can do:
$manager = new DisconnectedMetadataFactory($this->getContainer()->get(‘doctrine’));
$bundle = $this->getKernel()->getBundle($bundleName);
$metadata = $manager->getBundleMetadata($bundle);
It nice and really save my time.
This is dognail, not solution.
$entityClassNames =
$entityManager
->getConfiguration()
->getMetadataDriverImpl()
->getAllClassNames();
I found this incredibly useful for getting all table names handled by doctrine – just change your method to getTableName() – which can be compared to all existing tables, like so:
$em = $this->getDoctrine()->getManager();
$doctrineTables = array();
$allTables = array();
$meta = $em->getMetadataFactory()->getAllMetadata();
foreach ($meta as $m) {
$doctrineTables[] = $m->getName();
}
$tables = $em->getConnection()->getSchemaManager()->listTables();
foreach ($tables as $table) {
$allTables[] = $table->getName();
}
Thanks for the post!
Sorry, copy-paste error, that should be:
$doctrineTables[] = $m->getTableName();