How to properly write Doctrine 2 DQL queries? - php

I have upgraded to Doctrine 2.2.2 now. I have managed to successfully connect my database to my application and was able to generate proxies and repositories. I have no problem with those generation. I am just confused with regards to using the DQL of doctrine 2.2.2.
The case is this: I currently have a repository class responsible for user registration, authentication, etc. I have managed to execute the DQL on it but I just felt weird about this stuff (in my repository class).
$query = $em->createQuery("SELECT u FROM MyProject\\Entity\\AdminUsers u");
I tried also:
$query = $em->createQuery("SELECT u FROM AdminUsers u");
The last did not work but the first one works fine but it seems weird. Is it really the right way of executing DQL in doctrine 2? or am I missing something important.
NOTE: on the above declaration of this repository class is:
namespace MyProject\Repository;
use Doctrine\ORM\EntityRepository,
MyProject\Entity\AdminUsers;

It almost is the right way to do it. If you would use single quotes ', you could just use a single backslash \ instead of a double backslash \\.
Doctrine cant find out (or it would be extremely expensive to do so) which classes you imported via use statements.
But you can use a typed repository which you retrieve from the entity manager via:
$repo = $em->getRepository('MyDomain\Model\User');
$res = $repo->findSomeone();
And in the findSomeone() function you can do this:
$qb = $this->createQueryBuilder('u');
$dql = $qb->where('u.id = 1')->getDQL();
return $this->_em->createQuery($dql)->getSingleResult();
Meaning, the repository is already typed on your entity and knows which class to select from.
Some documentation:
Querying with doctrine
Querybuilder
10 step get started guide (which covers the basics including repositories)

Related

How to test custom DQL function?

I wrote a custom DQL function based on this tutorial.
I would like to write some tests, but the functionality of DQL is quite complex.
Is there any way how to test it?
As I see, even doctrine project not tests own DQL functions :(
I know it's an old quesion but for the folks who goggled it out as I did.
Testing is simple in fact. The general idea is to generate a query using your custom funciton then compare it against the expected result. Here is the example.
Let's say you have a custom DQL function which translates MY_FUNC(field) to MySQL_INTERNAL_FUNC(field). Then you test it as the following.
/* Somewhere in the PHPUnit test */
/* Assuming $em represents your EntityManager instance */
$query = $em->createQuery('
SELECT MY_FUNC(entity.someField)
FROM MyBundle:MyEntity entity
');
self::assertEquals('
SELECT MySQL_INTERNAL_FUNC(v0_.some_field) AS sclr_0
FROM my_entity v0_
', $query->getSQL());
That does the trick. Note the indents. They are there just to make the code clear. In practice Doctrine will remove them making SQL. So the expected SQL code should be in one line.

Weird alias [0_ added by doctrine while trying to run with SQL SERVER

I'm trying to implement APYDataGridBundle on Symfony, with SQL Server. Symfony throws in this exception:
SQLSTATE[HY000]: General error: 105 General SQL Server error: Check messages from the SQL Server [105] (severity 15) [SELECT TOP 50 [0_.PI_PK AS PI_PK0, [0_.ProductName AS ProductName1, [0_.ProductDetails AS ProductDetails2 FROM [TSOFT_LEARN].[dbo].[tblProductDemo] [0_]
I tried:
$repo = $em->getRepository("ProductOrderLookupBundle:Product");
$product = $repo->findAll();
and everything worked fine, but it breaks for over 1 million records. Someone suggested to me to use APYDatagridBundle like here. I have tried ThraceDataGrid Bundle before and it gave me this same problem.
If I remove the "[0_" and everything worked fine while running the query on SQL Server.
Can anybody tell me what might be the problem?
Doctrine always create alias for tables in the generated queries. I always had the same kind of queries, i don't know if it can be disabled or not but better not because i don't know how it will react for joins , what you can do is to add a function in your repository :
public function getAllProducts(){
$qb = $this->_em->createQueryBuilder()
->select('partial p.{ /*add all fields needed separated by coma optimized for big queries get only whats needed */ }')
->from('Product', 'p');
$results = $qb->getQuery()->getResult();
return $results;
}
**SOLVED:**The problem was with how I defined my table names in the doctrine entities. I gave the name
[TSOFT_LEARN].[dbo].[tblOrders]
and it should be
TSOFT_LEARN.dbo.tblOrders
Because of that my alias was becoming [0_ instead of t0_. I suppose its how doctrine creates its aliases by taking the first character of the table name. Since the table name was starting with "[" the alias got converted to [0_.

How to get Propel model generator to properly pluralize nouns?

I'm using Symfony 1.4 and Propel 1.6. I was confounded at first by various classnames that used improper pluralization.
For example some table relations were things like CommerceItemss, which was easily traced down in my schema.yml where I specified plural instead of singular table names.
After I corrected that, I was still left with one more type of error in the auto generated classes. Namely, I had a table named "Match" which Propel was pluralizing to Matchs.
For example, lines like:
if (null === $this->matchsScheduledForDeletion) {
...
$this->matchsScheduledForDeletion = clone $this->collMatchs;
So I'm left with the question of, "how to get Propel to pluralize properly"?
The solution was buried deep in the Propel ORM docs:
http://propelorm.org/reference/buildtime-configuration.html
Namely, edit your default.properties:
./plugins/sfPropelORMPlugin/lib/vendor/propel/generator/default.properties
./plugins/propel/generator/default.properties
Look for the line that says:
propel.builder.pluralizer.class = builder.util.DefaultEnglishPluralizer
Replace with:
propel.builder.pluralizer.class = builder.util.StandardEnglishPluralizer
It handles the problem with Match->Matchs properly (and I presume would also handle Category->Categories, etc.) so this could be the solution if you have a similar problem.

Symfony2 - Show all tables from database

I would like to retrieve all the tables of my database as a list.
i tried to do a "Show databases" on a query but as i'm not using a class I defined (entity) in symfony it's not working.
And with DQL :
$em = $this->getDoctrine()->getEntityManager();
$query = $em->createQuery(
'show databases');
$result = $query->getResult();
This error :
[Syntax Error] line 0, col 0: Error: Expected SELECT, UPDATE or DELETE, got 'show'
Any idea to help me ?
As mentioned in a different answer, you can use Doctrine\DBAL for that:
/** #type \Doctrine\DBAL\Connection $connection */
$connection = ...;
/** #type \Doctrine\DBAL\Schema\MySqlSchemaManager $sm */
$sm = $connection->getSchemaManager();
And then just list the tables as Array:
var_dump( $sm->listDatabases() );
My 2 cents:
getContainer()->get('doctrine.dbal.default_connection')->getSchemaManager()->listTableNames()
This will give you an array of table names.
i have a couple cases where I need to use complex sql statements/functions that I just couldn't do in DQL. Luckily, Symfony2/doctrine provide a method to grab the current database connection and bypass doctrine entirely.
//get connection
$conn = $this->get('database_connection');
//run a query
$users= $conn->fetchAll('select * from users');
Be very careful when using this method, however. Since you are bypassing doctrine, you need to handle any security concerns like SQL injection yourself.
You can add Doctrine DBAL to your project and it will give you the tools you need. You can list databases, tables from a database, columns from a table etc etc
More in Doctrine DBAL documentation: http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/schema-manager.html
Doctrine is a ORM, it is not intended to list all the databases. Beside that, usually for the current user you don't have the right to show all databases in the server, this can prove to be a big security breach.
Basicly, doctrine does not know how to interpret your query, you have to use a native query for this: Doctrine Native Query

Lazy Loading with Doctrine2 and Symfony2 using DQL

I have a tree structure with a parent field. Currently I am trying to get all parent nodes to display the path to the current node.
Basically I am doing a while-loop to process all nodes.
$current = $node->getParent();
while($current) {
// do something
$current = $current->getParent();
}
Using the default findById method works. Because the entity has some aggregated fields, I am using a custom repository method, to load all basic fields with one query.
public function findNodeByIdWithMeta($id) {
return $this->getEntityManager()
->createQuery('
SELECT p, a, c, cc, ca, pp FROM
TestingNestedObjectBundle:NestedObject p
JOIN p.actions a
LEFT JOIN p.children c
LEFT JOIN c.children cc
LEFT JOIN c.actions ca
LEFT JOIN p.parent pp
WHERE p.id = :id
')
->setParameter('id', $id)
->setHint(
\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
)
->getOneOrNullResult();
}
With that code, loading the parents fails. I only get the immediate parent (addressed by LEFT JOIN p.parent pp) but not the parents above. E.g. $node->getParent()->getParent() returns null.
Whats wrong with my code? Did I misunderstood the lazy loading thing?
Thanks a lot,
Hacksteak
It looks like your are using the adjacency model for storing trees in a relational database. Which in turn means, that you will need a join for every level to get all ancestors with a single query.
As you are already using the Doctrine Extension Library I recommend to have a look at the Tree component.
My Answer involves not using DQL and instead creating a NestedSetManager which has access to your DBAL connection so you can use SQL. I never felt like the ORM's did a good job with NestedSets query logic.
With a NestedSetManager, you can then write a bunch of clean methods and it's really simple because all these queries are well documented. See this link. Some of the method in my NestedSetManager are:
setNode();
setRoot();
loadNestedSet();
moveNodeUp();
modeNodeDown();
getRootNode();
addNodeSibling();
getNodesByDepth();
getParents();
getNodePath();
childExists();
addChildToNode();
renameNode();
deleteNode();
// And many more
You can have a ball and create a lot of create NestedSet functionality if you're not tied down by an ORM's somewhat complex functionality.
Also -- Symfony2 makes all this really really easy. You create your NestedSetManager class file and reference it in your Services.yml and pass in your Dbal connection. Mine looks like this:
services:
manager.nestedset:
class: Acme\CoreBundle\Manager\NestedSetManager
arguments: [ #database_connection ]
you can then access your nestedsets with:
$path = $this->get('manager.nestedset')->setNode(4)->getNodePath(); // in your controller
Moral of the story, ORM/NestedSets drove me bonkers and this solution work really well. If you're being forced to use DQL and have no other options, this answer probably wont be acceptable.

Categories