How to apply equality criteria in doctrine findBy condition? - doctrine-orm

Is it possible to apply criteria >= condition in Doctrine findBy. I have a query like this:
select * from source where id >= 10
I am looking to apply in following way which will return with reference to Object source.
$this->entityManager->getRepository(Source::class)->findBy(['id' ])
I can use createQueryBuilder but it returns me array result. I need result in same format as above:
$qb = $this->createQueryBuilder('s')
->andWhere('s.id >= :id')
->setParameter('id', 10);
Expected Result:
0 => App\Entity\Source {#845
-id: 10
Can anybody help me?
Thanks in advance.

You could use the repository's matching method.
$criteria = Criteria::create()
->andWhere(Criteria::expr()->gte('id', 10));
$repository->matching($criteria);
Be aware that doctrine can not always hydrate your database data on Entities, when using custom queries. The result is that it only returns array data.

Related

Doctrine createQuery() conditional params

I am trying to put a conditional parameter in my query. if orgID is null i do not want the query to use it as a filter.
AND (:orgID IS NULL OR o.id = :orgID)
I am struggling trying to do this. What am i doing wrong?
$em = $this->getEntityManager();
$query = $em->createQuery('
select count(d.id)
from \***\OrganizationBundle\Entity\Organization o
inner join \***\OrganizationBundle\Entity\Facility f
inner join \***\OrganizationBundle\Entity\Department d
where d.active = true
AND f.active = true
AND o.active = true
AND d.name LIKE :name
AND (:orgID IS NULL OR o.id = :orgID)
');
$query->setParameter(':name', '%' . $name . '%');
$query->setParameter(':orgID', $orgID);
$count = $query->getSingleScalarResult();
Both #Cerad and #xabbuh from comment section are right. Either skip appending the predicate or use QueryBuilder to conditionally make your query string.
In any case, remember that prepared statements placeholders cannot be used for table names and columns.
Also, from performance standpoint, you need not to run a query if you know for a fact that it will return previously known results. That's just waste of resources.
I agree with what others have said - use QueryBuilder as this is the Symfony / Doctrine way.
But if you want to solve it on the MySQL query level, then you will need something like "conditional WHERE clause", for example using CASE expression.
Replace
AND (:orgID IS NULL OR o.id = :orgID)
with
AND
CASE
WHEN :orgID IS NULL THEN TRUE
ELSE o.id = :orgID
END
The first condition explicitly evaluates to TRUE, which is essentially equal to your requirement: "I do not want the query to use it as a filter". (Although technically this is still a filter).

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

doctrine does not hydration when select custom fields

i have tried select fields with doctrine query buidler.
$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder->select('au.id, au.firstName')
->from('Api\V1\Entity\AgencyUser', 'au')
->orderBy('au.firstName', 'asc');
$queryBuilder->andWhere('au.agency = :agencyId')
->setParameter('agencyId', $agency->getId());
print_r($queryBuilder->getQuery()->getResult(Query::HYDRATE_OBJECT));exit;
result :
Array
(
[0] => Array
(
[id] => 1
[firstName] => agency
)
)
why this is an array ? i want to hydrated result. any idea ?
You want to use Doctrine's Partial Object Syntax
$queryBuilder->select('partial au.{id, firstName}')
As to why it's not returning an object, this is from the same documentation linked above:
By default when you run a DQL query in Doctrine and select only a subset of the fields for a given entity, you do not receive objects back. Instead, you receive only arrays as a flat rectangular result set, similar to how you would if you were just using SQL directly and joining some data.

How to order by a computed value in DQL

I'm trying to order the results of my query by whether or not they match my original entity on a property. I could do this easily in mySQL with the following query:
SELECT * FROM table
ORDER BY prop = 'value' DESC;
However, in Doctrine, when I attempt the following:
// $qb is an instance of query builder
$qb->select('e')
->from('Entity', 'e')
->orderBy('e.prop = :value', 'DESC')
->setParameter('value', 'value');
// grab values
I get a Doctrine syntax error, 'end of string'. I looked into creating a custom function, but that seems like overkill. I'm fairly new to Doctrine, is there a better way to do this?
Since Doctrine ORM 2.2, you can use the HIDDEN keyword and select additional fields, in this case with a CASE expression:
SELECT
e,
CASE WHEN e.prop = :value THEN 1 ELSE 0 END AS HIDDEN sortCondition
FROM
Entity e
ORDER BY
sortCondition DESC
As I struggeled a while to figure out how to create that query using php syntax here's what I came up with:
$value = 'my-value';
$qb->select('e')
->from('Entity', 'e')
->addSelect('CASE WHEN e.prop = :value THEN 1 ELSE 0 END AS HIDDEN sortCondition')
->setParameter('value', $value)
->addOrderBy('sortCondition', 'DESC');

Is there a simple way to combine IS NULL and = :value in Doctrine 2 DQL?

I regularly come across a scenario, where I want to query an entity with a specific value:
$query = $em->createQuery('SELECT e FROM Entity e WHERE e.parent = :parent');
$query->setParameter('parent', $parent);
Often, this value can be NULL, but WHERE e.parent = NULL yields no results, forcing me to hack around like this:
if ($parent === null) {
$query = $em->createQuery('SELECT e FROM Entity e WHERE e.parent = IS NULL');
}
else {
$query = $em->createQuery('SELECT e FROM Entity e WHERE e.parent = :parent');
$query->setParameter('parent', $parent);
}
While I understand the rationale behind NULL != NULL in SQL / DQL, the fact is, the consequence is really annoying in this case.
Is there a cleaner way to perform this query, when the parameter can be null?
It's not possible at the moment. (Tried for myself just several ways).
bindValue() with null only works for INSERT/UPDATE value binding.
I think the limitation is in PDO or SQL Syntax itself and not Doctrine.
You can use the QueryBuilder, so you only need to "duplicate" the WHERE part, instead of the whole query: http://doctrine-dbal.readthedocs.org/en/latest/reference/query-builder.html#where-clause
EDIT: It's possible in native SQL: https://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#operator_equal-to
Sadly doctrine does not have that operator: http://doctrine1-formerly-known-as-doctrine.readthedocs.org/en/latest/en/manual/dql-doctrine-query-language.html#operators-and-operator-precedence
You can use query builder:
$em = \Zend_Registry::get('em');
$qb_1 = $em->createQueryBuilder();
$q_1 = $qb_1->select('usr')
->from( '\Entities\user', 'usr' )
->where( 'usr.deleted_at IS NULL' )
->orWhere( 'usr.status='.self::USER_STATUS_ACTIVE )
->andWhere('usr.account_closed_on is null');
$q_1->getQuery()->getResult();
Was just trying to solve the same issue and I actually found a solution for PostgreSQL.
$sql = 'SELECT * FROM company WHERE COALESCE(:company_id, NULL) ISNULL OR id = :company_id;'
$connection->executeQuery($sql, ['company_id' => $id]);
Based on given $id it returns the particular company or all, if null is passed. The problem seems to be that the parser does not know what you are actually going to do with the argument, so by passing it in to COALESCE function it knows that it is passing it into a function, so problem solved.
So if it started working in the "pure" SQL solution, using it inside the DQL should not be a problem. I didn't try that but Doctrine should have COALESCE support so the DQL should be easy to update.