I am having trouble in creating a query on a search field. There are two tables I have in my database and when I perform my search functionality I need only the gsNumber as a requirement on the search field.
I got stuck on the query since I need to use partial, and I have a problem on what's needed to select on both tables. I don't get on what's the use of partial.
Here is my code below:
public function getDetails($gsNumber,$page = 1, $limit = 5 ){
$em = $this->getEntityManager();
$query = $em->createQuery(
'SELECT partial a.{ediTransactionId,gsNumber},
partial b.{}
FROM MatrixEdiBundle:Edi997Details b
JOIN a.ediTransaction b
WHERE a.errorCodeId != 1 AND a.gsNumber LIKE :gsNumber
AND b.flag = 1')
->setParameter('gsNumber', "%$gsNumber%")
->setFirstResult(($page-1)*$limit)
->setMaxResults($limit);
$paginator = new Paginator($query, $fetchJoinCollection = false );
$paginator->setUseOutputWalkers(false);
return $paginator;
}
Related
How to limits results by main entity, not with childrens?
If I do:
$queryBuilder = $em->createQueryBuilder();
$queryBuilder->select('n, c');
$queryBuilder->from('AppBundle:News', 'n');
$queryBuilder->leftJoin('n.comments', 'c');
$queryBuilder->setMaxResults(3);
$results = $queryBuilder->getQuery()->getResult();
And first News has for example 3 comments, then results return me only one record News. If I don't have comments or if I remove leftJoin, then this is working well.
You are using the wrong variable for your results.
You are using $queryBuilder to prepare your statement:
$queryBuilder = $em->createQueryBuilder();
But taking $qb to get your result;
$results = $qb->getQuery()->getResult();
Doctrine can behave unexpectedly with joins, multiple wheres and pagination.
The recommended way is to use the Paginator, and not directly call getResults.
The paginator handles the grouping of the results to the main entity.
Edit: to supply some sample code:
<?php
use Doctrine\ORM\Tools\Pagination\Paginator;
$dql = "SELECT p, c FROM BlogPost p JOIN p.comments c";
$query = $entityManager->createQuery($dql)
->setFirstResult(0)
->setMaxResults(100);
$paginator = new Paginator($query, $fetchJoinCollection = true);
$c = count($paginator);
foreach ($paginator as $post) {
echo $post->getHeadline() . "\n";
}
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/tutorials/pagination.html
I got 2 tables User and Group; each object Group got an array members[] that points to some instances of User.
I need to build this same query with QueryBuilder :
SELECT (users instances) FROM User
WHERE (users instances) IN (SELECT Group.members from Group WHERE group.id = $someId)
How can I achieve that?
You have to make 2 querybuilders:
$qb2 = $this->em->createQueryBuilder('group')
->select('group.members')
->where('group.id = $someId');
$qb = $this->em->createQueryBuilder('user')
->select(user instances)
-where($qb->expr()->in('user instances', $qb2->getDQL());
it will give you the idea how it works. Of course you have to adjust this code to yours.
Improving #Eimsas Answer it could be:
$em = $this->getEntityManager();
$qb2 = $em->createQueryBuilder('group')
->select('group.members')
->where('group.id = $someId');
$qb = $em->createQueryBuilder('user');
$query = $qb->select(user instances)
->where($qb->expr()->in('user instances', $qb2->getDQL());
$result = $query->getQuery->getResult();
I'm about to output a list that includes several documents(called waiver). However not every user should be allowed to see all documents and therefore I've implemented an filter to check if the user has the same "airline" and "market" assigned. So every user should only see the documents that are assigned to his "airline" and "market".
This is f.e. the getter for the airline of the user entity:
/**
* Get airlines
*
* #return array
*/
public function getAirlines()
{
if($this->airlines != null)
{
$airlines = explode(",", $this->airlines);
return $airlines;
}
return Array();
}
This is the controller logic:
//Get User:
$user = $this->container->get('security.context')->getToken()->getUser();
// Gets an Array of User markets
$user_markets = $user->getMarkets();
// Gets an Array of User carriers
$user_airlines = $user->getAirlines();
if(!$this->ROLE_IS(Array( 'ROLE_XY'))){
$query = $em->createQuery(
'SELECT w
FROM WaiverBundle:Waiver w
WHERE w.carrier = :carrier
AND w.market = :market
ORDER BY w.id DESC'
)
->setFirstResult($page*10-10)
->setMaxResults(10)
// I wan't to get the whole array and not just one position here:
->setParameters(array(':carrier'=>$user_airlines[0],
':market'=>$user_markets[0],
));
}else{
$query = $em->createQuery(
'SELECT u
FROM WaiverBundle:Waiver u
ORDER BY u.id DESC'
)
->setFirstResult($page*10-10)
->setMaxResults(10);
}
Question: How do I manage to compare the DQL attributes with an array and not just a string as a parameter?
I think you want to use "IN" syntax, not "=" syntax:
'SELECT w
FROM WaiverBundle:Waiver w
WHERE w.carrier IN (:carrier)
AND w.market = :market
ORDER BY w.id DESC'
Your query is not complicated. I think you should consider QueryBuilder instead of DQL in this case. Something like this would do the trick:
$qb = $em->createQueryBuilder();
$qb->select('w')
->from('WaiverBundle:Waiver', 'w')
->where($qb->expr()->in('w.carrier', ':carrier'))
->andWhere($qb->expr()->eq('w.market', ':market'))
->orderBy('w.id', 'DESC')
->setParameters(
array(
'carrier'=>$user_airlines[0],
'market'=>$user_markets[0)
);
I want to get the field of an entity that is associated with another .
My entity Offers has a last_offer field.
Offers is related to the Products entity.
Then , with a consultation in my entity Products, I want to get the latest offer associated with the entity Offer.
Controller:
public function lastAction($key)
{
$em = $this->getDoctrine()->getManager();
$last_offer = $em->getRepository('MyAppBundle:Products')->findOfferByKey($key);
$response = new JsonResponse();
return $response->setData($last_offer);
}
My repository:
public function findOfferByKey($key){
$em = $this->getEntityManager();
$dql = 'SELECT pr, of FROM MyAppBundle\AppBundle\Entity\Products pr
INNER JOIN pr.offers of
WHERE pr.key = :key';
$query = $this->getEntityManager()
->createQuery($dql)
->setParameter('key', $key)
->setHydrationMode(\Doctrine\ORM\Query::HYDRATE_ARRAY);
return $query->execute();
}
My routing:
last_offer:
path: /{key}/last_offer
defaults: { _controller: "MyAppBundle:Products:last" }
But, this return an array.
I want to return only last_offer element.
Or to return the entity offers without being in an array.
You're specifically telling doctrine to generate/return an array with this line
->setHydrationMode(\Doctrine\ORM\Query::HYDRATE_ARRAY);
I think all you need to do is remove that line and doctrine will generate/return entities instead. However, you will get a Collection back, so make sure to take that into account. Perhaps something like
public function findOfferByKey($key) {
$dql = 'SELECT pr, of FROM MyAppBundle\AppBundle\Entity\Products pr
INNER JOIN pr.offers of
WHERE pr.key = :key';
$query = $this->getEntityManager()
->createQuery($dql)
->setParameter('key', $key)
;
$results = $query->execute();
if (count($results) !== 1)
{
// It's up to you how to handle zero or multiple rows
}
return $results->current();
}
EDIT
I see what's happening - I wasn't paying attention to your SELECT caluse. You're not selecting just the last_offer columns, you're selecting the entirety of the Products and Offers entities = $results is going to be an array of all of these together. In this scenario, $query->execute() will return an array() instead of a collection.
If you want to select just the Offer entities, you need to modify the SELECT
$dql = 'SELECT of
FROM MyAppBundle\AppBundle\Entity\Products pr
INNER JOIN pr.offers of
WHERE pr.key = :key';
But be wary that this still may return more than one row.
I have a couple of pretty complex queries, and for each of them I have to write a second query counting results. So for example, in the model:
$dql = "SELECT u FROM AcmeBundle:Users u LEFT JOIN AcmeBundle:Products p WITH u.id = p.id";
I would have to create a duplicate query like this:
$countingQuery = "SELECT COUNT(u.id) FROM AcmeBundle:Users u LEFT JOIN AcmeBundle:Products p WITH u.id = p.id";
The main problem with that is that with every change in the first query, I would have to change the second either.
So I came up with another idea:
$countingSelect = "SELECT COUNT(u.id)";
$noncountingSelect = "SELECT u";
$dql = " FROM AcmeBundle:Users u LEFT JOIN AcmeBundle:Products p WITH u.id = p.id";
return $this->getEntityManager()->createQuery($noncountingSelect . $dql)
->setHint('knp_paginator.count', $this->getEntityManager()->createQuery($countingSelect . $dql)->getSingleScalarResult());
It works of course, but the solution seems quite ugly with larger selects.
How can I solve this problem?
I believe the Doctrine\ORM\Tools\Pagination\Paginator will do what you're looking for, without the additional complexity.
$paginator = new Paginator($dql);
$paginator
->getQuery()
->setFirstResult($pageSize * ($currentPage - 1)) // set the offset
->setMaxResults($pageSize); // set the limit
$totalItems = count($paginator);
$pagesCount = ceil($totalItems / $paginator->getMaxResults());
Code yanked from: http://showmethecode.es/php/doctrine2/doctrine2-paginator/
You can create a customer repository as explained in the docs and add your query to that with a minor edit like..
use Doctrine\ORM\EntityRepository;
class ProductRepository extends EntityRepository
{
public function findProducts()
{
return $this->findProductsOrCountProducts();
}
public function findCountProducts()
{
return $this->findProductsOrCountProducts(true);
}
private function findProductsOrCountProducts($count = false)
{
$queryBuilder = $this->createQueryBuilder('u');
if ($count) {
$queryBuilder->select('COUNT(u.id)');
}
$query = $queryBuilder
->leftJoin('AcmeBundle:Products', 'p', 'WITH', 'u.id = p.id')
->getQuery()
;
if ($count) {
return $query->getSingleScalarResult();
} else {
return $query->getResult();
}
}
}
Then you can call your method using...
$repository = $this->getDoctrine()
->getRepository('AcmeBundle:Users');
// for products
$products = $repository->findProducts();
// for count
$countProducts = $repository->findCountProducts();
Note:
I know it's not best practice to just say look at the docs for the customer repository bit s here' the YAML mapping...
# src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml
Acme\StoreBundle\Entity\Product:
type: entity
repositoryClass: Acme\StoreBundle\Entity\ProductRepository
# ...