I'm wondering what exactly are the pros of using doctrine to build queries such as :
<?php
// $qb instanceof QueryBuilder
$qb->select('u')
->from('User u')
->where('u.id = :identifier')
->orderBy('u.name ASC');
->setParameter('identifier', 100); // Sets :identifier to 100, and thus we will fetch a user with u.id = 100
The only advantage i see here is data sanitizing and parameter binding, which can be easily done using PDO.
Also another example :
<?php
// $qb instanceof QueryBuilder
// example8: QueryBuilder port of: "SELECT u FROM User u WHERE u.id = ? OR u.nickname LIKE ? ORDER BY u.surname DESC" using Expr class
$qb->add('select', new Expr\Select(array('u')))
->add('from', new Expr\From('User', 'u'))
->add('where', $qb->expr()->orX(
$qb->expr()->eq('u.id', '?1'),
$qb->expr()->like('u.nickname', '?2')
))
->add('orderBy', new Expr\OrderBy('u.name', 'ASC'));
Is it really worth the extra dependencies are performance cost ? The above syntax seems more complicated to me than a simple string containing the query.
It is more structural this way. It also removes DQL(or SQL) code from the PHP code that looks much better this way.
Related
I can't find an example of DQL, here's what the semi-pseudocode would look like:
Bring back invoices
- Where company id = 5
AND
(
->where('DATE(i.startPeriod) BETWEEN :startDate AND :endDate')
->orWhere('DATE(i.endPeriod) BETWEEN :startDate AND :endDate')
->orWhere(':startDate BETWEEN DATE(i.startPeriod) and DATE(i.endPeriod)')
->orWhere(':endDate BETWEEN DATE(i.startPeriod) and DATE(i.endPeriod)')
)
So you have four OR's nested within one encapsulating AND.
Does anyone know how to do that with Doctrine DQL? Nest a bunch of OR's within one giant AND?
You would need to use the Expr() class with the query builder.
// $qb instanceof QueryBuilder
$qb->select('i')
->from('invoices', 'i')
->where('c.id = :cid')
->andWhere($qb->expr()->orX(
$qb->expr()->between('i.startPeriod',':startdate',':enddate'),
$qb->expr()->between('i.endPeriod',':startdate',':enddate'),
...
You can read more about the Expr() class in the documentation.
EDIT:
Just realized your initial question asked specifically about DQL. You can use parens in DQL to group things, so something like this.
$query = $em->createQuery(
'SELECT i FROM Invoices i
WHERE c.id = :id
AND (
(i.startPeriod BETWEEN :startDate AND :endDate)
OR
(i.endPeriod BETWEEN :startDate AND :endDate)
OR
(:startDate BETWEEN i.startPeriod AND i.endPeriod)
OR
(:endDate BETWEEN i.startPeriod AND i.endPeriod)
) JOIN i.company c');
How can I convert below query to Doctrine:
SELECT restaurants.restaurant_name ,
restaurants.restaurant_id,
j.LASTPRICE
FROM restaurants
LEFT JOIN
(
SELECT f.food_id AS fid,
f.restaurants_restaurant_id AS rid,
Max(f.food_last_price) AS LASTPRICE
FROM foods AS f
LEFT JOIN restaurants AS r
ON r.restaurant_id = f.restaurants_restaurant_id
WHERE f.food_last_price IS NOT NULL
GROUP BY r.restaurant_id) j
ON restaurants.restaurant_id = j.rid
Here is my code:
$qb = $this->_em->createQueryBuilder();
$qb2 = $this->_em->createQueryBuilder();
$getMaxPercentage = $qb2
->select(
'MAX (Food.foodLastPrice) AS LASTPRICE ',
'Food.foodId AS fId',
'Food.restaurantsRestaurantId AS rID'
)
->from($this->entityClass,'Restaurant')
->innerJoin('Restaurant.foods','Food')
->where('Food.foodLastPrice IS NOT NULL')
->groupBy('Restaurant.restaurantId')
->getDQL();
$restaurantList = $qb
->select('Restaurants.restaurantName, Restaurants.restaurantId , j.LASTPRICE')
->from($this->entityClass,'Restaurants')
->leftJoin($getMaxPercentage,'j','WITH','Restaurants.restaurantId = j.rID')
->getQuery()->execute();
dd($restaurantList);
I give an error :
SELECT Restaurants.restaurantName,': Error: Class 'SELECT' is not defined.
I've already known I could set sub queries in main query, Although in this case I does not want to use sub query in Where expression. Any suggestion for using select in LeftJoin in doctrine?
EDITED : I've tried to use DQL in my query:
$query = $this->_em->createQuery(
'
SELECT Restaurants.restaurantName , Restaurants.restaurantId
FROM App\\Restaurant AS Restaurants
LEFT JOIN (
SELECT f.foodId AS fid,
f.restaurantsRestaurantId AS rid,
Max(f.foodLastPrice) AS LASTPRICE
FROM App\\Food AS f
LEFT JOIN App\\Restaurant AS r
WITH r.restaurantId = f.restaurantsRestaurantId
GROUP BY r.restaurantId) AS J
ON Restaurants.restaurantId = j.rid
');
But I gave an another error :
[Semantical Error] Error: Class '(' is not defined.
Is it possible to use select in left join in Doctrine?
EDITED 2 : I read a similar question and I've decided to write in another way :
$qb = $this->_em->createQueryBuilder();
$qb2 = $this->_em->createQueryBuilder();
$subSelect = $qb2
->select(
array(
'Food.foodLastPrice AS LASTPRICE ',
'Food.foodId AS fId',
'Food.restaurantsRestaurantId AS rId')
)
->from($this->entityClass,'Restaurant')
->innerJoin('Restaurant.foods','Food')
->where('Food.foodLastPrice IS NOT NULL')
->groupBy('Restaurant.restaurantId')
->getQuery()->getSQL();
$restaurantList =
$qb->select(
'Restaurant1'
)
->from($this->entityClass, 'Restaurant1')
->leftJoin('Restaurant1',sprintf('(%s)',$subSelect),'internalQuery','Restaurant1.restaurantId = internalQuery.rId')
->getQuery()->getSQL();
dd($restaurantList);
Again, I got an error:
near 'Restaurant1 (SELECT': Error: Class 'Restaurant1' is not defined.
What you're trying to do is impossible :
https://github.com/doctrine/doctrine2/issues/3542 (november 2013, but doctrine concept didn't change since and doctrinebot closed this on 7 Dec 2015)
DQL is about querying objects. Supporting subselects in the FROM
clause means that the DQL parser is not able to build the result set
mapping anymore (as the fields returned by the subquery may not match
the object anymore). This is why it cannot be supported (supporting it
only for the case you run the query without the hydration is a no-go
IMO as it would mean that the query parsing needs to be dependant of
the execution mode).
In your case, the best solution is probably to run a SQL query instead
(as you are getting a scalar, you don't need the ORM hydration anyway)
You can find workarounds like raw sql, or rethinking your query.
i have the following code in my Entity Repository class:
$qb
->select('d')
->addOrderBy('d.dtrDate', 'DESC')
->where($qb->expr()->isNotNull('d.deletedAt'))
->leftJoin('d.user', 'u')
->where('u.id = :user_id')
->setParameter('user_id', $user->getId());
So basically, I dont want to select an entity where deletedAt property is not null. However it gives me a query like this:
>
SELECT
d0_.id AS id_0,
d0_.dtr_date AS dtr_date_1,
d0_.clock_in AS clock_in_2,
d0_.clock_out AS clock_out_3,
d0_.total_time AS total_time_4,
d0_.memo AS memo_5,
d0_.last_update_IP AS last_update_IP_6,
d0_.created_at AS created_at_7,
d0_.updated_at AS updated_at_8,
d0_.deleted_at AS deleted_at_9,
d0_.user_id AS user_id_10
FROM
daily_time_record d0_
LEFT JOIN user u1_ ON d0_.user_id = u1_.id
WHERE
u1_.id = ?
ORDER BY
d0_.dtr_date DESC
As you can see my first where is not being called. So how can i work on this?
You should read the documents, or read the ORM source to see how things work actually. TO be short, the answer to your question is:
$qb
->select('d')
->leftJoin('d.user', 'u')
->where($qb->expr()->isNotNull('d.deletedAt'))
->andWhere('u.id = :user_id')
->setParameter('user_id', $user->getId())
->orderBy('d.dtrDate', 'DESC');
This is the current query that I have, and it works fine:
$q = $this->_em->createQuery("SELECT s FROM app\models\Sub s
LEFT JOIN s.case c
WHERE s.type LIKE '%$type'
AND c.id = '$id'");
foreach ($q->getResult() as $row) {
$results[$row->getId()] = $row;
}
I want to convert this to the QueryBuilder API structure, this is what I have so far, but it's not working correctly. (And I need the results as the same format as above).
$q = $this->_em->createQueryBuilder();
$q->add('select', 's')
->add('from', 'app\models\Sub s')
->add('leftJoin', 's.case c')
->add('where', 's.type LIKE :type')
->add('where', 'c.id = :case');
->setParameter('type', "%$type");
->setParameter('case', $id);
And again, this isn't working properly, and I don't know how to get the results in the same format as above. Thanks!
Close, but no cigar. You'll need to learn how to use the Expr classes that comes with Doctrine in order to create the correct WHERE clause.
http://www.doctrine-project.org/docs/orm/2.1/en/reference/query-builder.html#expr-classes
In the example you provided, you are actually aiming for something like this (not tested):
$q->add('select', 's')
->add('from', 'app\model\Sub s')
->add('leftJoin', 's.case c')
->add('where', $q->expr()->andX($q->expr()->like('s.type', ':type'), $q->expr()->eq('case', ':case')))
->setParameter('type', "%$type")
->setParameter('case', $id);
As your WHERE clause gets more complicated, so will the nesting of expressions.
I'm using Doctrine 2.
I want to get all Entities of an entity class except for the one with id = 0.
I could use QueryBuilder like this:
// $em is EntityManager
$em->createQueryBuilder()
->select('c')
->from('Category', 'c')
->where('c.id <> 0')
->getQuery()
->getResult();
But looking at this code, it feels like too much for such a simple task.
I'm curious to know if there is any other simpler way in Doctrine for doing this.
Nop, that's how you should do it. You could omit query builder and pass entire DQL query:
$em->createQuery("SELECT c FROM Category c WHERE c.id != 0")->getResult();
It's not the better practice to pass the ID number, you might pass the Category object like this :
public function findAllExcept($category) {
$qb = $this->createQueryBuilder('Category');
$qb->add('select', 'c')
->add('from', 'Category c')
->add('where', 'c != :category')
->setParameter('category', $category);
return $qb->getQuery()->getResult();
}
For comparisons and conditions I recommend use Doctrine as well. It will save your time in case of switch between databases, for instance MySQL to PostgreSQL:
$this->createQueryBuilder()
->select('c')
->from('Category', 'c')
->where($qb->expr()->neq('c.id', '?1'))
->setParameter('1', $category)
->getQuery()
->getResult()