Doctrine 2 simple "bigger than" criteria - php

In PDO / SQL i have a simple query with "bigger than":
DELETE * FROM tablexyz WHERE access > :old
Whats the equivalent in Doctrine 2 ORM?
I have already written a little code:
$criteria = ['access'=>$old];
$dataRepo = $entityManager->getRepository('tablexyz')->findBy($criteria)->delete();
My problem is, i want to use the bigger than operator in Doctrine 2 ORM but i don't want to make a function or class for that. Does somebody know a better or shorter solution for that?

You can use either a query builder og just do a direct DQL (or SQL) query for it.
$qb = $entityManager->createQueryBuilder();
$qb->select('e')
->from('Entityxyz', 'e')
->where('e.access > :old')
->setParameter('old', $old);
$entities = $qb->getQuery()->getResult();
or
$query = 'SELECT e FROM AppBundle\Entity\Entityxyz WHERE e.access > :old';
$entities = $entityManager->createQuery($query)
->setParameter('old', $old)
->getResult();`
I'd suggest you do create a repository method for it though, it's the correct place to have it. Using the query builder inside the repository is also simpler as it knows which entity you're referencing (and can skip calling select and from)
class EntityxyzRepository extends EntityRepository
{
public function getNewerThan($newerThan)
{
$qb = $this->createQueryBuilder('e');
$qb->where('e.access > :newerThan')
->setParameter('newerThan', $newerThan);
return $qb->getQuery()->getResult();
}
}

Related

Symfony Doctrine query builder LIKE not working in PostgreSQL

So I have a UserRepository that contains the following code
namespace AppBundle\Repository;
use \Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
{
public function findByRole($role)
{
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from($this->_entityName, 'u')
->where('u.roles LIKE :roles')
->setParameter('roles', '%"'.$role.'"%');
return $qb->getQuery()->getResult();
}
}
This seems to be working perfectly fine if my database is MySQL but if i change the database to PostgreSQL this query throws the following error
An exception occurred while executing 'SELECT p0_.id AS id_0,
p0_.username AS username_1, p0_.password AS password_2, p0_.is_active
AS is_active_3, p0_.roles AS roles_4, p0_.name AS name_5, p0_.street
AS street_6, p0_.city AS city_7, p0_.state AS state_8, p0_.zip_code AS
zip_code_9, p0_.phone_number AS phone_number_10, p0_.dob AS dob_11,
p0_.company_name AS company_name_12, p0_.company_slug AS
company_slug_13, p0_.company_logo AS company_logo_14,
p0_.company_details AS company_details_15, p0_.stripe_customer_id AS
stripe_customer_id_16, p0_.created_at AS created_at_17, p0_.updated_at
AS updated_at_18 FROM px_user p0_ WHERE p0_.roles LIKE ?' with params
["%\"ROLE_EMPLOYER\"%"]:
SQLSTATE[42883]: Undefined function: 7 ERROR: operator does not exist:
json ~~ unknown LINE 1: ...at AS updated_at_18 FROM px_user p0_ WHERE
p0_.roles LIKE $1 ^ HINT: No operator matches the given name and
argument type(s). You might need to add explicit type casts.
This is the first time I am working with PostgreSQL so I am not getting what the problem is. After playing around with it for a while if I change the generated query to by adding the following piece
WHERE
p0_.roles::text LIKE '%ROLE_EMPLOYER%'
Everything works fine. Note the ::text.
So now how can i add that to the query builder so it works with PostgreSQL as well.
I solved the problem with the module boldtrn/jsonb-bundle but it created an error depending on the version of Postgres used.
I also solved the issue without the module by a native query like this :
public function findEmailsByRole($role)
{
return $this->_em->getConnection()->executeQuery(
"SELECT email FROM public.utilisateur WHERE roles::text LIKE :role",
['role'=>'%"' . $role . '"%']
)->fetchAll();
}
You can create your Function Like this
PS: Im Working on PostgreSQL Too
public function findByRole($role) {
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('u')
->from($this->getEntityName(), 'u')
->where("u.roles LIKE '%$role%'")
;
return $qb->getQuery()->getResult();
}
I solved the problem by using JsonbBundle.
Following steps I took to fix it
$ composer require "boldtrn/jsonb-bundle
Updated the config.yml by adding the following in its respective place.
doctrine:
dbal:
types:
jsonb: Boldtrn\JsonbBundle\Types\JsonbArrayType
mapping_types:
jsonb: jsonb
orm:
dql:
string_functions:
JSONB_AG: Boldtrn\JsonbBundle\Query\JsonbAtGreater
JSONB_HGG: Boldtrn\JsonbBundle\Query\JsonbHashGreaterGreater
JSONB_EX: Boldtrn\JsonbBundle\Query\JsonbExistence
Changed the roles property to type jsonb
And inside the repository the following query worked
$query = $this->getEntityManager()->createQuery("SELECT u FROM AppBundle:User u WHERE JSONB_HGG(u.roles , '{}') LIKE '%EMPLOYER%' ");
$users = $query->getResult();
return $users;
The credit goes to Doctrine query postgres json (contains) json_array
public function findByRole($role)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('u')
->from($this->getEntityName(), 'u')
->where($qb->expr()->like('u.roles', ':roles')
->setParameter('roles', $qb->expr()->literal('%'.$role.'%'));
return $qb->getQuery()->getResult();
}
Replaced your custom string DQL to the QueryBuilder syntax.
I don't know if it might be related to the syntax you've got within your like statement: '%".$var."%', which might bug it. Hope this helps you solve it.
Doctrine Querybuilder documentation like :
// Example - $qb->expr()->like('u.firstname', $qb->expr()->literal('Gui%'))
public function like($x, $y); // Returns Expr\Comparison instance
Looks like from the PostgreSQL documentation on LIKE, you might need to do this:
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from($this->_entityName, 'u')
->where('u.roles LIKE :roles')
->setParameter('roles', '\'%'.$role.'%\'');
return $qb->getQuery()->getResult();
Essentially having single quotes outside the percent signs by escaping them. I'm not sure if that will work, but can you try it?
Building up on #Lou Zito answer:
If you are dealing with Postgres please learn the power of json/b operations!
Casting a json/b array into text for like comparison is not a good approach. Cast it into jsonb instead (or even better: change your setup / migration(s) to use jsonb fields directly instead <3)
You can use ? operator, or the contains operator #> as an example.
See this full list of jsonb operators available
Please be aware, that ? will get interpreted as doctrine placeholder, you need to use ?? to escape it!
public function findByRole()
{
return $query = $this->getEntityManager()
->getConnection()
->executeQuery(<<<'SQL'
SELECT id FROM public.user
WHERE roles::jsonb ?? :role
ORDER BY last_name, first_name
SQL,
['role' => User::ROLE_XYZ]
)->fetchAllAssociative();
}
Hint: As you can see in the example above, I usually extract roles as public string constants in the User entity for easy access. I strongly recommend that as best practice as well.

Symfony Doctrine disable cache

In my symfony project I have two entities that are related via one to many.
I need to find the first and last child, so I use repository functions that look like this:
public function getFirstPost(Topic $topic)
{
$query = $this->createQueryBuilder('t')
->addSelect('p')
->join('t.posts', 'p')
->where('t.id = :topic_id')
->setParameter('topic_id' => $topic->getId())
->orderBy('p.id', 'ASC')
->setMaxResults(1)
->getQuery();
return $query->getOneOrNullResult();
}
public function getLastPost(Topic $topic)
{
$query = $this->createQueryBuilder('t')
->addSelect('p')
->join('t.posts', 'p')
->where('t.id = :topic_id')
->setParameter('topic_id' => $topic->getId())
->orderBy('p.id', 'DESC')
->setMaxResults(1)
->getQuery();
return $query->getOneOrNullResult();
}
So the only difference is in in ->orderBy(), for the first Post I use ASC and for the last I use DESC.
Now If I use one of those functions from my controller, the return the expected result and work just fine. But If I run them both at the same time from my controller, they return the same result, which they shouldn't.
My guess is that Doctrine caches these queries and the results somehow and that's why the return the same so I tried using $query->useResultCache(false) but that didn't do anything.
So my question is, why is this happening and how can I fix it?
Well, it is cache issue indeed, but mostly it is query issue. Instead of returning a post in these function you return the whole topic with joined posts.
What you can do is to rewrite these queries to select Post entity directly and join Topic entity to it which will be filtered by.
If you really(dont do this) need these queries to work you can detach first topic returned by one of those methods and then call the other method:
$this->getDoctrine()->getManager()->detach($firstTopic);

How convert SQL query to Doctrine2?

I want to convert this in Doctrine2.
Similar to these questions:
Symfony2 / Doctrine2 - How to convert this SQL request with QueryBuilder?
How to convert a complex MySQL Query to Doctrine2
Convert SQL to Doctrine 2 Query Builder or DQL by using NOT IN?
Is it possible?
SELECT
SUM(q.amount)
FROM(
SELECT amount
FROM expense_report e0_
WHERE
(e0_.is_account_transfer = 0)
AND (e0_.remove_date IS NULL)
LIMIT 0, 2) q
Thanks
you can archive your problem with this custom repository method:
public function getAmount()
{
$qb = $this->createQueryBuilder("q");
return
$qb
->select("sum(q.amount)")
->where($qb->expr()->isNull('q.remove_date'))
->andWhere('q.is_account_transfer = 0')
->setFirstResult(0)
->setMaxResults(2)
->getQuery()
->getSingleScalarResult();
}
And simply use it, as example, in a controller method as:
public function testAction()
{
....
$totalAmount = $this->getDoctrine()
->getRepository("AcmeDemoBundle:ExpenseReport")
->getAmount();
...
}
Hope this help

How to use a findBy method with comparative criteria

I'd need to use a "magic finder" findBy method using comparative criteria (not only exact criteria). In other words, I need to do something like this:
$result = $purchases_repository->findBy(array("prize" => ">200"));
so that I'd get all purchases where the prize is above 200.
The class Doctrine\ORM\EntityRepository implements Doctrine\Common\Collections\Selectable API.
The Selectable interface is very flexible and quite new, but it will allow you to handle comparisons and more complex criteria easily on both repositories and single collections of items, regardless if in ORM or ODM or completely separate problems.
This would be a comparison criteria as you just requested as in Doctrine ORM 2.3.2:
$criteria = new \Doctrine\Common\Collections\Criteria();
$criteria->where(\Doctrine\Common\Collections\Criteria::expr()->gt('prize', 200));
$result = $entityRepository->matching($criteria);
The major advantage in this API is that you are implementing some sort of strategy pattern here, and it works with repositories, collections, lazy collections and everywhere the Selectable API is implemented.
This allows you to get rid of dozens of special methods you wrote for your repositories (like findOneBySomethingWithParticularRule), and instead focus on writing your own criteria classes, each representing one of these particular filters.
This is an example using the Expr() Class - I needed this too some days ago and it took me some time to find out what is the exact syntax and way of usage:
/**
* fetches Products that are more expansive than the given price
*
* #param int $price
* #return array
*/
public function findProductsExpensiveThan($price)
{
$em = $this->getEntityManager();
$qb = $em->createQueryBuilder();
$q = $qb->select(array('p'))
->from('YourProductBundle:Product', 'p')
->where(
$qb->expr()->gt('p.price', $price)
)
->orderBy('p.price', 'DESC')
->getQuery();
return $q->getResult();
}
You have to use either DQL or the QueryBuilder. E.g. in your Purchase-EntityRepository you could do something like this:
$q = $this->createQueryBuilder('p')
->where('p.prize > :purchasePrize')
->setParameter('purchasePrize', 200)
->getQuery();
$q->getResult();
For even more complex scenarios take a look at the Expr() class.
$criteria = new \Doctrine\Common\Collections\Criteria();
$criteria->where($criteria->expr()->gt('id', 'id'))
->setMaxResults(1)
->orderBy(array("id" => $criteria::DESC));
$results = $articlesRepo->matching($criteria);
The Symfony documentation now explicitly shows how to do this:
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT p
FROM AppBundle:Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', '19.99');
$products = $query->getResult();
From http://symfony.com/doc/2.8/book/doctrine.html#querying-for-objects-with-dql
I like to use such static methods:
$result = $purchases_repository->matching(
Criteria::create()->where(
Criteria::expr()->gt('prize', 200)
)
);
Of course, you can push logic when it is 1 condition, but when you have more conditions it is better to divide it into fragments, configure and pass it to the method:
$expr = Criteria::expr();
$criteria = Criteria::create();
$criteria->where($expr->gt('prize', 200));
$criteria->orderBy(['prize' => Criteria::DESC]);
$result = $purchases_repository->matching($criteria);
Copying the findBy query and modifying it to return your expected result is a good approach.

Doctrine ORM, two different querys produce the same result set

I'm using Doctrine 1.2 and Symfony 1.4.
In my action, I have two different query that return different result set. Somehow the second query seem to change the result (or the reference?) of the first one and I don't have any clue why..
Here is an example:
$this->categories = Doctrine_Query::create()
->from('Categorie AS c')
->innerJoin('c.Activite AS a')
->where('a.archive = ?', false)
->execute();
print_r($this->categories->toArray()); // Return $this->categories results, normal behavior.
$this->evil_query = Doctrine_Query::create()
->from('Categorie AS c')
->innerJoin('c.Activite AS a')
->where('a.archive = ?', true)
->execute();
print_r($this->categories->toArray()); // Should be the same as before, but it return $this->evil_query results instead!
Why Doctrine behave this way ? It's totally driving me crazy. Thanks!
To make it simple it seem like the Query 2 are hijacking the Query 1 result.
Use something like this between queries ($em - entity manager):
$em->clear(); // Detaches all objects from Doctrine!
http://docs.doctrine-project.org/en/2.0.x/reference/batch-processing.html
In the API docs for the toArray() method in Doctrine_Collection it says:
Mimics the result of a $query->execute(array(), Doctrine_Core::HYDRATE_ARRAY);
I suspect to answer this question to your satisfaction you're going to have to go through the source code.
This seems like it has to be an issue of $this->categories and $this->evil_query pointing to the same place. What are the results of $this->evil_query === $this->categories and $this->evil_query == $this->categories?
Hubert, have you tried storing the query into a separate variable and then calling the execute() method on it?
I mean something like:
$good_query = Doctrine_Query::create()
->from('...')
->innerJoin('...')
->where('...', false)
;
$evil_query = Doctrine_Query::create()
->from('...')
->innerJoin('...')
->where('...', true)
;
$this->categories = $good_query->execute();
$this->evil_query = $evil_query->execute();
It seems like both attributes (categories and evil_query) are pointing at the same object.
Erm, after both queries you print_r the result of the first query - change the last line to print_r($this->evil_query->toArray()); to see the difference :)

Categories