3

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.

Shairyar
  • 3,268
  • 7
  • 46
  • 86
  • Use ->setParameter('roles', '"%'.$role.'%"'); – Jasmin Mistry Dec 21 '16 at 13:42
  • @JasminMistry I tried that too but the same error appears. – Shairyar Dec 21 '16 at 13:44
  • `->where('CAST(u.roles AS TEXT) LIKE :roles')` should work. Your `roles` is a `json` typed column and such cannot be used instead of string types directly (where the engine expects a string type (f.ex. `text`) -- the `LIKE` operator (also `~~`) is just like that). – pozs Dec 21 '16 at 13:54
  • @pozs thanks for sharing the information, I did not know that. After making the change I am seeing [Syntax Error] line 0, col 44: Error: Expected known function, got 'CAST' – Shairyar Dec 21 '16 at 14:00
  • @Baig then it seems Doctrine parses what you gave to `where()` and disallow some pretty basic SQL too. [It's not even supported in DQL](http://stackoverflow.com/questions/7405342/casting-attributes-for-ordering-on-a-doctrine2-dql-query). As a worst case, you could use native queries. – pozs Dec 21 '16 at 14:07
  • @pozs thanks for the tip, much appreciated. – Shairyar Dec 21 '16 at 14:17

6 Answers6

4

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();
}
Lou Zito
  • 41
  • 3
2

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();
}
Barkati.med
  • 620
  • 5
  • 11
  • What are you trying to address? please elaborate – Shairyar Dec 21 '16 at 15:01
  • Ok What its your Field Type In ORM – Barkati.med Dec 21 '16 at 15:21
  • the role is database is saved as ["ROLE_USER","ROLE_EMPLOYER"] – Shairyar Dec 21 '16 at 15:25
  • Okay well I'm Sorry but i have already got This problem and i don't find a good solution for it i can give you an alter solution its to create a new Fields Type Text and save a copy of your role Like This ROLE_USER,ROLE_EMPLOYER and use a simple DQL LIKE to find your Objects – Barkati.med Dec 21 '16 at 16:25
  • I ended up fixing it, please see my answer. I hope that helps you out. I was tempted to add the field as you suggested but I did not want to do that as there had to be a solution to this. – Shairyar Dec 22 '16 at 07:20
1

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

Community
  • 1
  • 1
Shairyar
  • 3,268
  • 7
  • 46
  • 86
0
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
Kwido
  • 1,382
  • 8
  • 21
  • Thanks for having a look but this does not work, gives me the same error. While the query you and I wrote work with MySQL database but I am working on PostgreSQL and this fails. – Shairyar Dec 21 '16 at 14:57
  • Okay well too bad, good luck on searching for the solution. If you found it I would like to see the (accepted) answer. – Kwido Dec 21 '16 at 15:32
  • 1
    I ended up fixing the problem, please see my answer. – Shairyar Dec 22 '16 at 07:20
  • Accept your own answer to mark the question as done. – Kwido Dec 22 '16 at 07:34
  • Will do, it says I can accept it tomorrow. that was some crazy searching i did for 2 days. – Shairyar Dec 22 '16 at 07:36
0

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?

Alvin Bunk
  • 7,621
  • 3
  • 29
  • 45
0

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.

pocketrocket
  • 365
  • 2
  • 8