Doctrine QueryBuilder complex non-table query with subqueries - php

I have an SQL query I want to build with symfony/Doctrine2 QueryBuilder.
select
(SELECT group_concat(DISTINCT sender)
FROM transactions) as senders,
(SELECT group_concat(DISTINCT receiver)
FROM transactions) as receivers,
(SELECT group_concat(DISTINCT message_type)
FROM transactions) as types
As seen in the description above, the main-query doesn't have a table
Is there a way to implement this in Querybuilder without writing the SQL Command as it is shown?

According to:
The same way you build a normal Query, you build a QueryBuilder object, just providing the correct method name. Here is an example how to build a QueryBuilder object:
//$em instanceof EntityManager
// example1: creating a QueryBuilder instance
$qb = $em->createQueryBuilder();`
http://doctrine-orm.readthedocs.io/en/latest/reference/query-builder.html#constructing-a-new-querybuilder-object
But this will require your root query to map against an entity. Also the querybuilder doesn't provide any real support for subqueries, but there are some examples on how to achieve is anyway:
// build root query
$query = Doctrine_Query::create()
->from('Movie m')
->where('name = ?', 'Prometheus')
;
// build subquery
$subquery = $query->createSubquery()
->from('SeenMovie sm')
->where('m.name = sm.name')
;
// nest subquery and execute
$query->where('EXISTS (' . $subquery->getDql() . ')')->execute();
Borrowed from: https://www.philipphoffmann.de/2012/08/29/a-bulletproof-pattern-for-creating-doctrine-subqueries-of-any-complexity/

Related

Why is my result array with a hydrated associated entity shaped differently using query builder vs createQuery?

Doctrine's documentation describes two ways to select a record and its relation, one with using a "fetch join" and one not:
Fetch join
Fetch join of the address:
<?php
$query = $em->createQuery("SELECT u, a FROM User u JOIN u.address a WHERE a.city = 'Berlin'");
$users = $query->getResult();
When Doctrine hydrates a query with fetch-join it returns the class in the FROM clause on the root level of the result array. In the previous example an array of User instances is returned and the address of each user is fetched and hydrated into the User#address variable. If you access the address Doctrine does not need to lazy load the association with another query.
The other way
Retrieve a ForumUser and its single associated entity:
<?php
$query = $em->createQuery('SELECT u, a FROM ForumUser u JOIN u.avatar a');
$users = $query->getResult(); // array of ForumUser objects with the avatar association loaded
echo get_class($users[0]->getAvatar());
My question
When I use the query builder it performs like a fetch join and I receive an array with all users and the associations in its root. Like:
$qb = $this->em->createQueryBuilder()
->select('u')
->from(User::class, 'u');
$qb->join(Address::class, 'a', Join::WITH, 'u.a = a');
$qb->addSelect('a');
// Returns
$result = [
'user' => // User
'address' => // Address
];
The User object does also have the Address hydrated, as expected.
However I don't want this, I want it shaped like the second example: an array of just Users, with the Address only accesible via the User object. I could simply filter the array to get the desired shape, but I feel like that's a workaround due to my lack of understanding.
Is it possible to use the query builder to get a result array shaped like the second example above? Or do I have to filter the result array for that? The confusing part for me is that the actual SQL generated by both is identical!
I come from Laravel and Doctrine is brand new to me, so please forgive my naivety on the subject. If I need to clarify anything, just let me know.
Thanks!
I think I found my answer in the related questions section: Symfony2 query builder joins to get objects as a single array
Instead of joining with the fully qualified names of the entities you should join with the association. So the query becomes:
SELECT hs,s,h
FROM \App\SoBundle\Entity\HandsetSubscription hs
JOIN hs.subscription s with s.id = hs.subscription
AND s.mins = 150
AND s.mb = 250
AND s.sms = 150
JOIN hs.handset h with h.id = hs.handset

Phalcon PhP - how to execute query inside a controller with parameters

I would like to know how can I execute a query, inside a phalcon controller, using parameters.
My SQL Query:
SELECT a.*, getApplicationData(a.id) as json_data
FROM application a
INNER JOIN application_data d on d.application_id = a.id
WHERE a.form_id=1
AND d.firstname LIKE '%:searchQuery:%' ;
Here is how I'm trying to execute (I found it in Phalcon's doc, but the example was not inside a controller).
$applications = $this->db->query(
$sqlQuery,
array('searchQuery'=>$searchQuery)
)->fetchAll();
I know that since you have the ORM I shouldn't be querying the DB like this, but for the feature I'm working on it has to be like this, this query is dymanic.
My question is how to pass the parameter for the :searchQuery: in the query.
Thanks in advance for any help.
You almost got it right. You only had to add the percents (%) in the bind array.
$sqlQuery = 'SELECT * FROM events WHERE title LIKE :searchQuery';
$applications = $this->db->query(
$sqlQuery,
array('searchQuery'=> '%' . $searchQuery . '%')
)->fetchAll();
Also notice how I binded the :searchQuery in the $sqlQuery. It only uses single : instead of surrounding the parameter. This is because Raw queries use directly the DB connection in our case it is PDO.
More examples can be found here: https://docs.phalconphp.com/en/latest/api/Phalcon_Db_Adapter_Pdo.html

Creating a query using eloquent in Laravel

Ok, so I'm needing to create a query below in eloquent or Laravel in some matter. I'm just not sure how I would create something like this:
select * from
(select * from Bulk
where Login = 'moga' and ApptDate='2015-02-25'
order by FormatTime asc) as A
group by Name
This query will run just fine as a raw query in the database, I'm just not sure how I would pull this query off using query helper or eloquent.
You can use either Eloquent or Query Builder. Here's an example with the Query Builder:
DB::table(DB::raw('(select * from Bulk where Login = "moga" and ApptDate="2015-02-25" order by FormatTime asc) as A'))
->groupBy('Name')
->get();
With Eloquent you'd use ModelName::from() instead of DB::table(). But keeping in mind that DB::raw does not escape variable values inserted into the query string, you could build the nested query using the builder as well, to make sure you're not vulnerable to SQL injection:
$from = DB::table('Bulk')
->where('Login', $login)
->where('ApptDate', $date)
->orderBy('FormatTime')
->toSql();
DB::table(DB::raw('(' . $from . ') A'))->groupBy('Name')->toSql();

Doctrine 2 delete with query builder

I have two Entities with relation OneToMany, Project and Services. Now i want to remove all the services by project_id.
First attempt:
$qb = $em->createQueryBuilder();
$qb->delete('Services','s');
$qb->andWhere($qb->expr()->eq('s.project_id', ':id'));
$qb->setParameter(':id',$project->getId());
This attempt fails with the Exception Entity Service does not have property project_id. And it's true, that property does not exists, it's only in database table as foreign key.
Second attempt:
$qb = $em->createQueryBuilder();
$qb->delete('Services','s')->innerJoin('s.project','p');
$qb->andWhere($qb->expr()->eq('p.id', ':id'));
$qb->setParameter(':id',$project->getId());
This one generetate a non valid DQL query too.
Any ideas and examples will be welcome.
You're working with DQL, not SQL, so don't reference the IDs in your condition, reference the object instead.
So your first example would be altered to:
$qb = $em->createQueryBuilder();
$qb->delete('Services', 's');
$qb->where('s.project = :project');
$qb->setParameter('project', $project);
If you really can't get project object and you have to handle only with id you can use this.
Citation from doctrine documentation:
There are two possibilities for bulk deletes with Doctrine. You can either issue a single DQL DELETE query or you can iterate over results removing them one at a time. (Below I paste only first solution)
DQL Query
The by far most efficient way for bulk deletes is to use a DQL DELETE query.
Example that worked in my project
$q = $em->createQuery('delete from AcmeMyTestBundle:TemplateBlock tb where tb.template = '.intval($templateId));
$numDeleted = $q->execute();
In entity TemplateBlock I have property called template which is mapped to template_id in db.
But I agree that highly preferred way of doing it is using objects.

Symfony 2: INNER JOIN on non related table with doctrine query builder

I'm trying to build a query with the doctrine query builder which joins a non related table like this:
$query = $this->createQueryBuilder('gpr')
->select('gpr, p')
->innerJoin('TPost', 'p')
->where('gpr.contentId = p.contentId')
But this doesn't work. I still get an error:
Error: Identification Variable TPost used in join path expression but was not defined before.
I searched for this error message and everybody answered to use the table alias + attribute like p.someAttribute. But the table I want to join isn't related in the table I start my select from.
As a normal mysql query i would write it like this:
SELECT * FROM t_group_publication_rel gpr
INNER JOIN t_post p
WHERE gpr.content_id = p.content_id
Any ideas what i'm doing wrong?
Today I was working on similar task and remembered that I opened this issue. I don't know since which doctrine version it's working but right now you can easily join the child classes in inheritance mapping. So a query like this is working without any problem:
$query = $this->createQueryBuilder('c')
->select('c')
->leftJoin('MyBundleName:ChildOne', 'co', 'WITH', 'co.id = c.id')
->leftJoin('MyBundleName:ChildTwo', 'ct', 'WITH', 'ct.id = c.id')
->orderBy('c.createdAt', 'DESC')
->where('co.group = :group OR ct.group = :group')
->setParameter('group', $group)
->setMaxResults(20);
I start the query in my parent class which is using inheritance mapping. In my previous post it was a different starting point but the same issue if I remember right.
Because it was a big problem when I started this issue I think it could be also interesting for other people which don't know about it.
Joins between entities without associations were not possible until version 2.4, where you can generate an arbitrary join with the following syntax:
$query = $em->createQuery('SELECT u FROM User u JOIN Blacklist b WITH u.email = b.email');
Reference: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html
Want to improve this post? Add citations from reputable sources by editing the post. Posts with unsourced content may be edited or deleted.
$dql = "SELECT
a, md.fisrtName , md.LastName, mj
FROM MembersBundle:Memberdata md
INNER JOIN MembersBundle:Address a WITH md = a.empID
INNER JOIN MembersBundle:Memberjob mj WITH md = mj.memberData
...
WHERE
a.dateOfChange IS NULL
AND WHERE
md.someField = 'SomeValue'";
return $em->createQuery( $dql )->getResult();
A DQL join only works if an association is defined in your mapping. In your case, I'd say it's much easier to do a native query and use ResultSetMapping to populate your objects.

Categories