Fetch results from $start variable - doctrine-orm

Here is a simple query:
//...
$query = $this->getEntityManager()
->createQuery('
SELECT r
FROM AcmeGolferBundle:Golfer g
LEFT JOIN g.shots r
WHERE g.id = :id'
)
->setParameter('id',$id);
return $query->getResult();
Is there a way to fetch the results from $start to $end? For example, if there are 50 results, is it possible to fetch the results from $start=20 to $end=30?
I suppose I just have to do this to fetch a total of 10 objects:
$query->setMaxResults(10);
But, how can I fetch all the results from the 20th result?

Use
$query->setFirstResult(20);
Update:
See the docs for DQL Query.

Related

DQL getResult return array

I'm playing with Doctrine, and I think I probably miss something.
So I have a table for some relations, and here is the columns:
id | user_id | workshop_id
Basically, I use this table to know that a user is register for a workshop.
I want to count how many users subscribe for a workshop.
So in my Repository, I use a DQL request:
/** #var EntityManager $entityManager */
$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery("
SELECT COUNT('uw.id')
FROM App\Entity\UserWorkshops AS uw
LEFT JOIN App\Entity\User AS u WITH u.userId = uw.user
WHERE uw.workshop = :workshop_id");
$count = $query
->setParameters(['workshop_id' => $workshopId])
->getResult();
And here, and example of the result:
I just need to retrieve 3, as an integer.
How can I return something different ?
thanks
You can use getSingleScalarResult, from the doc:
Query#getSingleScalarResult(): Retrieves a single scalar value from
the result returned by the dbms. If the result contains more than a
single scalar value, an exception is thrown. The pure/mixed
distinction does not apply.
As example:
$count = $query
->setParameters(['workshop_id' => $workshopId])
->getSingleScalarResult();

UPDATE with JOIN not SELECTing expected value

I'm trying to UPDATE a temporary transaction table with values from a holdings table. The fields I want to get are from the holding with the lowest date that is higher than the transaction date.
When I use below SELECT statement, the right values are shown:
SELECT h.*
FROM transaction_tmp tt
JOIN holdings h
ON tt.isin = h.isin
AND tt.portfolio = h.portfolio
WHERE h.start_date > tt.tr_date
ORDER BY h.start_date
LIMIT 1
However, when I use below UPDATE statement, incorrect values are selected/updated in transaction_tmp:
UPDATE transaction_tmp tt
JOIN holdings h
ON tt.isin = h.isin
AND tt.portfolio = h.portfolio
SET
tt.next_id = h.id,
tt.next_start_date = h.start_date
WHERE h.start_date > tt.tr_date
ORDER BY h.start_date
LIMIT 1
I'm thinking the WHERE statement is not working appropriately, but unfortunately I cannot figure out how to fix it.
Appreciate any help here!
-Joost
should work using a subquery
UPDATE transaction_tmp tt
JOIN (
SELECT h.*
FROM transaction_tmp tt
JOIN holdings h
ON tt.isin = h.isin
AND tt.portfolio = h.portfolio
WHERE h.start_date > tt.tr_date
ORDER BY h.start_date
LIMIT 1
) tx on ON tt.isin = tx.isin
AND tt.portfolio = tx.portfolio
SET
tt.next_id = tx.id,
tt.next_start_date = tx.start_date
I'm surprised your syntax works. The MySQL documentation is pretty clear that LIMIT and ORDER BY are only allowed when there is a single table reference:
UPDATE [LOW_PRIORITY] [IGNORE] table_reference
SET assignment_list
[WHERE where_condition]
[ORDER BY ...]
[LIMIT row_count]
They are not allowed for the multiple table version of UPDATE:
UPDATE [LOW_PRIORITY] [IGNORE] table_references
SET assignment_list
[WHERE where_condition]
. . .
For the multiple-table syntax, UPDATE updates rows in each table named in table_references that satisfy the conditions. Each matching row is updated once, even if it matches the conditions multiple times. For multiple-table syntax, ORDER BY and LIMIT cannot be used.
I get an error if I try such syntax.

Doctrine 2 | Criteria vs DQL | Performance & Usability

I was wondering what is the best practice for fetching collections by complex queries in Doctrine 2. I have been using the Cirteria matching functionality, but I want to know if it is usable for large scale databases.
$expr = Criteria::expr();
$criteria = Criteria::create();
$criteria->where(
$expr->andX(
$expr->gte('start', $start),
$expr->lte('end', $end)
)
);
$result = (new ArrayCollection($em->getRepository(Entity::class)->findAll())->matching($criteria);
What is the difference in performace with same filter written in DQL.
$qb = $em->createQueryBuilder();
$qb->select('e')
->from('Entity', 'e')
->where('e.start >= :start')
->andWhere('e.end <= :end')
->setParameters(array('start' => $start, 'end' => $end));
return $qb->getQuery()->getArrayResult();
And a native SQL one.
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Entity', 'e');
$query = $this->_em->createNativeQuery('SELECT * FROM Entity WHERE start >= ? AND end <= ?', $rsm);
$query->setParameter(1, $start);
$query->setParameter(2, $end);
$result = $query->getResult();
I really like the criteria one as it is easy to read and easy to maintain. But is it usable? How does the performace suffer when fetching 1 000, 10 000 or even 100 000 records.
The second question is does the Doctrine Criteria fetches all result and then filters them or does it create the specific query first?
The docs says this, but does it apply to my case?
"Collections have a filtering API that allows to slice parts of data from a collection. If the collection has not been loaded from the database yet, the filtering API can work on the SQL level to make optimized access to large collections."
Doctrine 2 Criteria documentation
The problem is that you are using Criteria to match records after calling findAll() in your Repo. You should use criteria direct over your Repo, not to the array collection.
$criteria = new Criteria();
$criteria->where(Criteria::expr()->gte('legajo', $this->legajoDesde));
$criteria->andWhere(Criteria::expr()->lte('legajo', $this->legajoHasta));
$criteria->andWhere(Criteria::expr()->eq('idOrganizacion', $this->idOrganizacion));
$criteria->orderBy(['legajo' => Criteria::ASC]);
$empleados = $empleadoRepository->matching($criteria);

INNER JOIN in DQL in Doctrine 2 / Zend Framework 2

I have an issue with DQL in Doctrine 2.
Subqueries seem to be unavailable in DQL, so I don't know how to transform :
SELECT DISTINCT a.ID_DOMAINE, L_DOMAINE, b.ID_SS_DOMAINE, L_SS_DOMAINE, c.ID_COMPETENCE, L_COMPETENCE
FROM ((qfq_prod.REF_DOMAINE a inner join qfq_prod.REF_SS_DOMAINE b on a.id_domaine = b.id_domaine)
inner join qfq_prod.REF_COMPETENCE c on b.id_ss_domaine = c.id_ss_domaine)
inner join qfq_prod.REF_PERS_COMP d on c.id_competence = d.id_competence
into a DQL expression.
I tried it and got
"Error: Class '(' is not defined."
I saw that we can use Query Builder to do this as well.
Being new with Doctrine 2, can someone explain to me how I can do this please ?
My DQL is currently :
$query = $this->getEntityManager()->createQuery ( "SELECT DISTINCT a.ID_DOMAINE, L_DOMAINE, b.ID_SS_DOMAINE, L_SS_DOMAINE, c.ID_COMPETENCE, L_COMPETENCE
FROM ((BdDoctrine\Entity\Domaine a inner join BdDoctrine\Entity\SsDomaine b on a.id_domaine = b.id_domaine)
inner join BdDoctrine\Entity\Competence c on b.id_ss_domaine = c.id_ss_domaine)
inner join BdDoctrine\Entity\LienPersComp d on c.id_competence = d.id_competence" );
$res = $query->getResult ();
Subqueries seem to be unavailable in DQL, so I don't know how to transform :
Actually, they are. Your code (no offence) is hardly readable so I will give you an example:
//controller
$repo = $this->getDoctrine()->getRepository("Your:Bundle:Category") ;
$results = $repo->findAllForSomePage() ;
// CategoryRepository.php
public function findAllForSomePage()
{
return $this->createQueryBuilder("o")
->innerJoin("o.products", "p", "WITH", "p.price>:price")->addSelect("p")
->setParameter("price", 50)
->where("o.id IN (SELECT s1.id FROM Your:Bundle:Something s1 WHERE s1.col1=5)")
->getQuery()->getResult() ;
}
Here is presumed you have Category hasMany Products relation and that you defined CategoryRepository file. You should never create queries in controller.
This example will fetch Categories only if they have Products with price bigger than 50, AND the ID of categories are those fetched by fictional subquery. This 100% works.
You should apply the same logic on your requirement.
Also, you should not use ON statement when using joins, that is handled by doctrine.
If you have the relationships properly defined in your entities, then you can make your joins on those relationships. And as Zeljko mentioned, you don't need to specify the ON condition, as the entities should already know how they are related. You are joining entities not tables. (That's under the hood.)
I don't know what your entities look like, so I made a guess at the relationship names below, but it should give you the idea.
$dql =
<<<DQL
SELECT
DISTINCT a.ID_DOMAINE, b.L_DOMAINE, b.ID_SS_DOMAINE, b.L_SS_DOMAINE, c.ID_COMPETENCE, c.L_COMPETENCE
FROM
BdDoctrine\Entity\Domaine a
JOIN a.ss_domaine b
JOIN b.competence c
JOIN c.lien_pers_comp d
DQL;
$query = $this->getEntityManager()->createQuery($dql);
$res = $query->getResult();

Is this the proper way to handle an ordered array with Doctrine2's WHERE IN expression?

Using Zend Lucene Search, I am returning a list of relevance-ordered IDs that map to blog records that I will fetch from the database.
Is this the proper way of handling an array with Doctrine2's WHERE IN expression:
$dql = "SELECT b FROM BlogPost WHERE b.id IN (" . implode(', ', $ids) . ")";
$query = $em->createQuery($dql);
...
Or is there a better way of maybe passing in the actual $ids array as a parameter to the query?
Also, the Zend Search returns the array of IDs based on relevance. Will using the above technique preserve the order of relevance in retrieving the blog posts?
If it makes you feel better, you can use the ExpressionBuilder.
$ex = $em->getExpressionBuilder();
$dql = 'SELECT b FROM BlogPost b WHERE ' . $ex->in('b.id', $ids));
$query = $em->createQuery($dql);
function cmp($a, $b) {
global $ids;
return (array_search($a->getId(), $ids) < array_search($b->getId(), $ids)) ? -1 : 1;
}
usort($res, 'cmp');
It's a bit cleaner, but does the same as you behind the screens.
You should pas the $ids array through setParameter() function as this is a best practice in doctrine:
$query = $this->_em->createQuery('SELECT b FROM BlogPost WHERE b.id IN (?1)');
$query->setParameter(1, implode(',', $ids));
I think the IN statement will not preserve the order of the passed ID's as IN will match the first found $id not depending on the order.