doctrine query onetonmany / notIn with objects / symfony forms querybuilder - symfony

I am using symfony 2 and doctrine to prefilter a form field type 'entity' with the help of a querybuilder.
My querybuilder should return all products which the user has not already added to a list.
All relations are bidirectionnal.
I have products linked to userIngredients (oneToMany) each linked to one user (manyToOne)
I have come with this so far but it's not working, I get products not added by other users.
return $this
->createQueryBuilder('p')
->leftJoin('p.userIngredients', 'i')
->where('i.user <> ?1')
->setParameter(1,$user);
1; Any clue on how to correct this ?
Alternatively, I could select the products I don't want and then reselect those who don't match but using an expression and NotIn seems to only work for strings
$products = $this
->createQueryBuilder('p')
->leftJoin('p.userIngredients', 'i')
->where('i.user = ?1')
->setParameter(1,$user)
->getQuery()
->getResult();
return $this
->createQueryBuilder('p')
->where($this->createQueryBuilder('p')->expr()->notIn('p', $products));
2; how could we correct this to make it work with objects ?
3; alternatively : is there a way to pass not a querybuilder but an array of results to symfony form builders ?

I got thinks thanks to Javad:
(slight modification, I'm using an array result, not dql):
$qb = $this->_em->createQueryBuilder();
$ids = $qb
->select('p.id')
->from('AppBundle:MarketPlace\Product','p','p.id')
->leftJoin('p.userIngredients', 'i')
->where('i.user = ?1')
->setParameter(1,$user)
->getQuery()
->getResult();
//I don't know why I couldn't directly get an array of ids otherwise... if you know how to do better directly from the query, I'm interested (getScalarResult does not make it)
$ids=array_keys($ids);
$result = $this
->createQueryBuilder('p')
->where($this->createQueryBuilder('p')->expr()->notIn('p.id', $ids));
return $result;

Related

Symfony QueryBuilder andWhere for User-Relation not Working

I want to load items for a given User. Between Item and User lives a One-To-Many relationship.
If I use the following, everything is working well:
$result = $em→getRepository(Item::class)→findBy(['user' => $user]);
The $result has 3 entries.
Now I have to give additional conditions to the query. So I want to use the queryBuilder insight the repository:
$result = $this->createQueryBuilder('i')
->andWhere('i.user = :user')
->setParameter('user', $user)
->getQuery()
->getResult();
The result is empty but the query runs without any errors.
Can someone tell me what I'm doing wrong?
In both cases the $user is the related App\Entity\User entity.
I found the solution. It didn't work because of the Uuid. This works:
$result = $this->createQueryBuilder('i')
->andWhere('i.user = :user')
->setParameter('user', $user->getId()->toBinary())
->getQuery()
->getResult();

Symfony4 / Doctrine, how select 'distinct' objects ? getResult() return an array of string, not an array of object

From Symfony4/Doctrine, I need to select all "distinct" objects with a entity's repository custom method, below a simple example :
An minimal example table of an entity :
A simple $em->getRepository(MyEntity::class)->findAll(); return all data of my table in object structure.
I'm looking for a way to do the same query with a distinct in relation with the column name. With my example, this query must return the objects 1, 3 and 5.
I tried this entity's repository custom method :
public function getDistinct(){
$query = $this->createQueryBuilder('myentity');
$res = $query
->select("myentity.name")
->distinct(true)
->getQuery()
->getResult();
return $res;
}
But the $res var contains an array of string results, not a array of object (I want an array of entity Objects).
How can I do that ?
Try group by myentity.name instead of distinct.
$res = $query
->select("myentity")
->groupBy("myentity.name")
->getQuery()
->getResult();
This should do the job as expected.

Symfony2 simple query: can't fetch an object, only array

The following query in Symfony2.6 works with getArrayResult(); BUT doesn't work with getResult(); or getOneOrNullResult();. This means that I can't fetch an object but only an array.
If I use this query, all I get is a white/blank page (not even the symfony debug toolbar). Note that in my twig template I just do a {{ dump() }} of the query result.
The table structure is easy (it's a list of books read by the users):
id, user, book, status, vote, review
(user, book and status are foreign keys)
public function selectOneRecordBy2Ids($user_id, $book_id)
{
/* #var $qb QueryBuilder */
$qb = $this->createQueryBuilder('l');
$qb->select('l');
$qb = $qb->Where($qb->expr()->eq('l.user', ':first'));
$qb = $qb->andWhere($qb->expr()->eq('l.book', ':second'));
$qb = $qb->setParameters(array('first' => $user_id, 'second' => $book_id));
return $qb->getQuery()->getOneOrNullResult();
}
I've noticed a few bad practices here, so let me correct them:
public function selectOneRecordBy2Ids(User $user, Book $book)
{
/* #var $qb QueryBuilder */
$qb = $this->createQueryBuilder('l');
$qb
->andWhere($qb->expr()->eq('l.user', ':first'))
->andWhere($qb->expr()->eq('l.book', ':second'))
->setParameters(array('first' => $user, 'second' => $book));
return $qb->getQuery()->getResult();
}
Select is not necessary if you work only with one entity and you don't fetch any relations. QB returns $this so you can chain the method calls.
Try to use entities as parameters instead of primitive types (if it is possible). If not, then you have to use primitive types as primitives in QB. In this case you'll need a few joins:
->select('l')
->join('l.user', 'u')
->join('l.book', 'b')
->andWhere($qb->expr()->eq('u.id', ':first'))
->andWhere($qb->expr()->eq('b.id', ':second'))
->setParameters(array('first' => $user_id, 'second' => $book_id));
If you want to fetch only one record, then you may have to limit the results by setting the max results:
->setMaxResults(1)
I hope this helps and solves your original problem as well.
getOneOrNullResult() does not return the record, but tells you if any record in database is found, or not.
You might want to use getSingleResult()

Symfony2 Doctrine2 use of partials

From reading the documentation and another stackoverflow post, I thought that if I only want to return a couple of columns data, the correct method in doctrine was to use a partial. (This is a read only query).
However the below code returns all 100 columns instead of the 3 I identified. Can someone explain why?
Thanks,
Manisha
public function showAction(Request $request)
{
if ($request->getMethod() == 'GET') {
$id = $request->get('locationid');
$kfType = $request->get('type');
$em = $this->getDoctrine()
->getManager();
$data = $em->createQueryBuilder()
->select ( array( 'partial d.{id, locationid, kfFyp}' ))
->from('DashDataBundle:Data', 'd')
->where('d.locationid = :locationid')
->setParameter('locationid', $id)
->setMaxResults(100)
->getQuery()
->getResult();
}
This query will return doctrine entities which have many fields. But then you use partial keywords this fields would be empty. Only specified fields would be filled with data.
If you doesnt want to hydrate objects you can get data in simple array if you specify it
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY)

Count Rows in Doctrine QueryBuilder

I'm using Doctrine's QueryBuilder to build a query, and I want to get the total count of results from the query.
$repository = $em->getRepository('FooBundle:Foo');
$qb = $repository->createQueryBuilder('n')
->where('n.bar = :bar')
->setParameter('bar', $bar);
$query = $qb->getQuery();
//this doesn't work
$totalrows = $query->getResult()->count();
I just want to run a count on this query to get the total rows, but not return the actual results. (After this count query, I'm going to further modify the query with maxResults for pagination.)
Something like:
$qb = $entityManager->createQueryBuilder();
$qb->select('count(account.id)');
$qb->from('ZaysoCoreBundle:Account','account');
$count = $qb->getQuery()->getSingleScalarResult();
Some folks feel that expressions are somehow better than just using straight DQL. One even went so far as to edit a four year old answer. I rolled his edit back. Go figure.
Here is another way to format the query:
return $repository->createQueryBuilder('u')
->select('count(u.id)')
->getQuery()
->getSingleScalarResult();
It's better to move all logic of working with database to repositores.
So in controller you write
/* you can also inject "FooRepository $repository" using autowire */
$repository = $this->getDoctrine()->getRepository(Foo::class);
$count = $repository->count();
And in Repository/FooRepository.php
public function count()
{
$qb = $repository->createQueryBuilder('t');
return $qb
->select('count(t.id)')
->getQuery()
->getSingleScalarResult();
}
It's better to move $qb = ... to separate row in case you want to make complex expressions like
public function count()
{
$qb = $repository->createQueryBuilder('t');
return $qb
->select('count(t.id)')
->where($qb->expr()->isNotNull('t.fieldName'))
->andWhere($qb->expr()->orX(
$qb->expr()->in('t.fieldName2', 0),
$qb->expr()->isNull('t.fieldName2')
))
->getQuery()
->getSingleScalarResult();
}
Also think about caching your query result - http://symfony.com/doc/current/reference/configuration/doctrine.html#caching-drivers
public function count()
{
$qb = $repository->createQueryBuilder('t');
return $qb
->select('count(t.id)')
->getQuery()
->useQueryCache(true)
->useResultCache(true, 3600)
->getSingleScalarResult();
}
In some simple cases using EXTRA_LAZY entity relations is good
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html
If you need to count a more complex query, with groupBy, having etc... You can borrow from Doctrine\ORM\Tools\Pagination\Paginator:
$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($query);
$totalRows = count($paginator);
Since Doctrine 2.6 it is possible to use count() method directly from EntityRepository. For details see the link.
https://github.com/doctrine/doctrine2/blob/77e3e5c96c1beec7b28443c5b59145eeadbc0baf/lib/Doctrine/ORM/EntityRepository.php#L161
Example working with grouping, union and stuff.
Problem:
$qb = $em->createQueryBuilder()
->select('m.id', 'rm.id')
->from('Model', 'm')
->join('m.relatedModels', 'rm')
->groupBy('m.id');
For this to work possible solution is to use custom hydrator and this weird thing
called 'CUSTOM OUTPUT WALKER HINT':
class CountHydrator extends AbstractHydrator
{
const NAME = 'count_hydrator';
const FIELD = 'count';
/**
* {#inheritDoc}
*/
protected function hydrateAllData()
{
return (int)$this->_stmt->fetchColumn(0);
}
}
class CountSqlWalker extends SqlWalker
{
/**
* {#inheritDoc}
*/
public function walkSelectStatement(AST\SelectStatement $AST)
{
return sprintf("SELECT COUNT(*) AS %s FROM (%s) AS t", CountHydrator::FIELD, parent::walkSelectStatement($AST));
}
}
$doctrineConfig->addCustomHydrationMode(CountHydrator::NAME, CountHydrator::class);
// $qb from example above
$countQuery = clone $qb->getQuery();
// Doctrine bug ? Doesn't make a deep copy... (as of "doctrine/orm": "2.4.6")
$countQuery->setParameters($this->getQuery()->getParameters());
// set custom 'hint' stuff
$countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountSqlWalker::class);
$count = $countQuery->getResult(CountHydrator::NAME);
For people who are using only Doctrine DBAL and not the Doctrine ORM, they will not be able to access the getQuery() method because it doesn't exists. They need to do something like the following.
$qb = new QueryBuilder($conn);
$count = $qb->select("count(id)")->from($tableName)->execute()->fetchColumn(0);
To count items after some number of items (offset), $qb->setFirstResults() cannot be applied in this case, as it works not as a condition of query, but as an offset of query result for a range of items selected (i. e. setFirstResult cannot be used togather with COUNT at all). So to count items, which are left I simply did the following:
//in repository class:
$count = $qb->select('count(p.id)')
->from('Products', 'p')
->getQuery()
->getSingleScalarResult();
return $count;
//in controller class:
$count = $this->em->getRepository('RepositoryBundle')->...
return $count-$offset;
Anybody knows more clean way to do it?
Adding the following method to your repository should allow you to call $repo->getCourseCount() from your Controller.
/**
* #return array
*/
public function getCourseCount()
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('count(course.id)')
->from('CRMPicco\Component\Course\Model\Course', 'course')
;
$query = $qb->getQuery();
return $query->getSingleScalarResult();
}
You can also get the number of data by using the count function.
$query = $this->dm->createQueryBuilder('AppBundle:Items')
->field('isDeleted')->equals(false)
->getQuery()->count();

Resources