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

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()

Related

Key "productTitle" for array with keys "0, catTitle" does not exist in EagleShopBundle:global:product.html.twig

I'm trying to join two tables and print a value in twig template but I'm having this issue.
This is my Controller action.
/**
* #Route("products/display/{id}")
* #Template()
*/
public function displayAction($id) {
$em = $this->container->get('doctrine.orm.entity_manager');
$qb = $em->createQueryBuilder();
$qb->select('p, pc.catTitle')
->from('EagleShopBundle:Products', 'p')
->leftJoin('EagleShopBundle:ProductCategory', 'pc', \Doctrine\ORM\Query\Expr\Join::WITH, 'pc.id = p.category')
->where($qb->expr()->eq('p.id', '?5'))
->setParameter(5, $id);
$product = $qb->getQuery()->getOneOrNullResult();
return $this->render("EagleShopBundle:global:product.html.twig", array(
'product' => $product,
'image_path' => '/bundles/eagleshop/images/'
));
}
This is my twig file line related to the issue,
<h1>{{product.productTitle}}</h1>
I guess issue is related to this line
$qb->select('p, pc.catTitle')
This is the error I get,
Key "productTitle" for array with keys "0, catTitle" does not exist in
EagleShopBundle:global:product.html.twig
You could try next query:
$qb->select('p, partial pc.{id, catTitle}')
// if you need full productCategory object then write just 'p, pc'
->from('EagleShopBundle:Products', 'p')
->leftJoin('p.category', 'pc')
//productCategory is the field
//in product entity which has relation to product category entity,
//paste your field (not column!) name here
//if it is not productCategory
->where('p.id = :productId')
->setParameter('productId', $id);
P.S.
It is better to move queries to entity repositories :)
P.P.S.
Doctrine partial objects
UPD
Fixed query - with right field name

Method findBy() from Doctrine with DateTime

I have the following code, but this would not work:
$event= $this->getDoctrine()
->getRepository('AppBundle:Event')
->findBy(
array(
"datestart" => array(new \DateTime())
)
);
I need a list of the events, which are newer then now.
Where is my misstake?
Or isn't that able to get with the default findBy() function of doctrine ?
findBy only search for exactly matches while you need greater then. In this case and also many other cases you can write your own query. You could use DQL or the queryBuilder. If you want to be able to re-use your query on other places then you could place your query in a repository class which is a good practice too. (check the docs). Now here you find a DQL example that you can place into your controller:
$events = $this->getDoctrine()
->getManager()
->createQuery('SELECT e FROM AppBundle:Event e WHERE e.datestart > CURRENT_DATE()')
->getResult();
i changed your variabele to $events (with s) because you are expecting possible more than one event.

doctrine query onetonmany / notIn with objects / symfony forms querybuilder

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;

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