1

To show related Articles on a website, I need the Cast() function. My Query looks like:

SELECT
    *,
    (CAST(a.uploader = ?1 AS UNSIGNED)
    + CAST(a.param2 = ?2 AS UNSIGNED)
    ...
    ) AS matches_count
FROM articles AS a
ORDER BY matches_count DESC

It counts the matches and sorts by the highest number of matches_counts.

The problem is, that there's no Cast() function built into doctrine.

After hours of trial and error I found an already available custom DQL Function: https://github.com/beberlei/DoctrineExtensions/blob/master/src/Query/Mysql/Cast.php

I registered it inside my doctrine.yml. But it doesn't work, because it expects Cast(X AS Y) and not Cast(Y $comparisonOperator X).

When I'am using this inside my repository, by example:

$this->createQueryBuilder('a, (CAST(author=25 AS UNSIGNED) AS matches_count)')
            ->getQuery()
            ->getResult()
            ;

I get this error, because it doesn't expect a comparison operator:

[Syntax Error] line 0, col 29: Error: Expected Doctrine\ORM\Query\Lexer::T_AS, got '='

Do you know how to maybe extend that class for and not Cast(Y $comparisonOperator X) instead of Cast(X AS Y)? I didn't find any solution on the internet and tried it for hours.

Thank you in advance for taking the time to write an answer!

Update:

I changed line 37 in the above mentioned custom DQL class for Cast:

        //old
        //$this->fieldIdentifierExpression = $parser->SimpleArithmeticExpression();
        //new
        $this->fieldIdentifierExpression = $parser->ComparisonExpression();

and how to create the query:

$this->createQueryBuilder('a')
            ->select('a, (CAST(a.averageRating=:averageRating AS UNSIGNED) + CAST(a.author=:author AS UNSIGNED)) AS matches_count')
            ->setParameter('averageRating', $averageRating)
            ->setParameter('author', $author)
            ->orderBy('matches_count', 'DESC')
            ->getQuery()
            ->getResult();

and that seems to be it!

I hope its the right way of doing it, will help someone and that is the best way for this purpose.

To improve performance later, I plan to cache 10 ids of recommended articles for every single article page into its own table. So it doesn't need to do the calculation on page load. This table could get recreated every 24h via a cronjob.

ID | recommended_article_ids | article_id
1 | 10,24,76,88| 5

Feedback and tips are much appreciated!

emovere
  • 152
  • 1
  • 13

0 Answers0