Recommand · June 3, 2021 0

Semantical error when using QueryBuilder with an expression

I have a problem with getting data from the database using the expr() method function. I would like to get data where isPublic = true and objectType = $objectType OR user = $user and objectType = $objectType, no matter what the value of isPublic is.

I’m getting this error:

[Semantical Error] line 0, col 76 near 'user-avatar)': Error: 'user' is not defined.

My code in repository:

    public function findByObjectType($objectType, $user)
    {
        $qb = $this->createQueryBuilder('s');

        return $qb->where($qb->expr()->andX(
                $qb->expr()->eq('s.isPublic', true),
                $qb->expr()->eq('s.objectType', $objectType)
            ))
            ->orWhere($qb->expr()->andX(
                $qb->expr()->eq('s.user', $user->getId()),
                $qb->expr()->eq('s.objectType', $objectType)
            ))
            ->getQuery()
            ->getResult();
    }

where: $objectType = 'user-avatar'; $user = UserInterface

expr()->eq() will treat the expression as literals, trying to use them literally as they appear on method call.

As mentioned by the library author:

You are not using parameter binding. Expressions use string concatenation internally, so this outcome is actually expected.

In your case, you should be doing something like::

 return $qb->where($qb->expr()->andX(
        $qb->expr()->eq('s.isPublic', ':true'),
        $qb->expr()->eq('s.objectType', ':objectType')
    ))
    ->orWhere($qb->expr()->andX(
        $qb->expr()->eq('s.user', ':userId'),
        $qb->expr()->eq('s.objectType', ':objectType')
    ))

    ->setParameter('true', true)
    ->setParameter('userId', $user->getId())
    ->setParameter('objectType', $objectType)

    ->getQuery()
    ->getResult();

This way your code is easier to read, safer and more portable.

When using $qb->expr()->eq() you will need supply the exact value for the query. In this case you need to change your query to something like this:

$qb->expr()->eq('s.objectType', '"' . $objectType .'"')

This way to string will be correctly quoted in the db query. The same goes for booleans by the way. Casting true to a string will result in 1. That’s why you didn’t encounter an error in this case. false however gets cast to an empty string, which would result in an error.

To better understand what’s going on, here is the part of code that converts the eq() expression to the query:

 /**
 * @return string
 */
public function __toString()
{
    return $this->leftExpr . ' ' . $this->operator . ' ' . $this->rightExpr;
}