How to write/extend custom DQL Function for Cast(X $comparisonOperator Y) in Doctrine? (Goal: Show related Objects) - symfony

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!

Related

How to express select with max() and ifnull() in peewee

I have a SQLite-Select statement like this to get the next available number:
SELECT IFNULL(MAX(current_nr)+1, 1) FROM rm_evaluation;
I already have a corresponding model in python peewee:
class RmRiskEvaluation(BaseModel):
...
current_nr = IntegerField(unique=True)
class Meta:
table_name = 'rm_evaluation'
But how can I express the SQL-statement from above.
=> Get the last number, add 1 to it and return the whole thang; if there is no last number at all, calculate with 1 beforehand.
If you weren't so lazy and had spent even a couple minutes reading the docs or searching, you would have found your answer.
fn.IFNULL(fn.MAX(RmRiskEvaluation.current_nr) + 1, 1)

LINQ: Get all members with LAST order failed

I'm learning LINQ, and I'm trying to figure out how to get all members with the last order failed (each member can have many orders). For efficiency reasons I'd like to do it all in LINQ before putting it into a list, if possible.
So far I believe this is the right way to get all the members with a failed order which joined recently (cutoffDate is current date -10 days).
var failedOrders =
from m in context.Members
from o in context.Orders
where m.DateJoined > cutoffDate
where o.Status == Failed
select m;
I expect I need to use Last or LastOrDefault, or possibly I need to use
orderby o.OrderNumber descending
and then get the First or FirstOrDefault as suggested in this stackoverflow answer.
Note that I want to look at ONLY the last order for a given member and see if that has failed (NOT just find last failed order).
Normally you would write something like:
var failedOrders = from m in context.Members
where m.DateJoined > cutoffDate
select new
{
Member = m,
LastOrder = m.Orders.OrderByDescending(x => x.OrderNumber).FirstOrDefault()
} into mlo
// no need for null checks here, because the query is done db-side
where mlo.LastOrder.Status == Failed
select mlo; // or select mlo.Member to have only the member
This if there is a Members.Orders relationship

Doctrine 2 select entities with none or less than relations

I'm trying to find all rows with none or less than 3 relations. What would be the best approach for that?
I tried the following code with several different selects, having, joins but I can't figure out how to do it the right way:
$db = $this->createQueryBuilder('u');
$db->addSelect('COALESCE(COUNT(errors), 0) AS HIDDEN errorCount');
$db->join('u.errors', 'errors');
$db->where('u.field1 IS NULL AND u.field2 NOT LIKE \'\'');
$db->having('errorCount < 3');
Thank you very much in advance.
The COUNT function is an aggregate function which will return a single result unless you instruct it to group by something.
$db = $this->createQueryBuilder('u');
$db->addSelect('COUNT(errors) AS HIDDEN errorCount');
$db->leftJoin('u.errors', 'errors');
$db->where('u.field1 IS NULL AND u.field2 NOT LIKE \'\'');
$db->having('errorCount < 3');
$db->groupBy('u');
Additionally, if you want to count the number of relations that may or may not exist use a LEFT JOIN. The count on these results will be 0 which is < 3.

Doctrine PHPCR-ODM Query Builder selecting document inside specific node

I have the following nodes:
/
/applications
/applications/1
/applications/1/pages [contains Page documents]
I want to select all Page documents inside the node /applications/1. I used the following to do that (which seems right), but it throws an error:
$qb = $this->dm->createQueryBuilder();
$qb->from()->document('Mango\CoreDomainBundle\Document\Page', 'page');
// I want to select the pages which are a child of /applications/1
$qb->where()->child('/applications/1', 'application');
$qb->getQuery()->execute();
When I execute this, it throws the following error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'n1.id' in 'field list'
This is the SQL query that is send back to the database:
SELECT
n0.id AS n0_id,
n0.path AS n0_path,
n0.parent AS n0_parent,
n0.local_name AS n0_local_name,
n0.namespace AS n0_namespace,
n0.workspace_name AS n0_workspace_name,
n0.identifier AS n0_identifier,
n0.type AS n0_type,
n0.props AS n0_props,
n0.depth AS n0_depth,
n0.sort_order AS n0_sort_order,
n1.id AS n1_id,
n1.path AS n1_path,
n1.parent AS n1_parent,
n1.local_name AS n1_local_name,
n1.namespace AS n1_namespace,
n1.workspace_name AS n1_workspace_name,
n1.identifier AS n1_identifier,
n1.type AS n1_type,
n1.props AS n1_props,
n1.depth AS n1_depth,
n1.sort_order AS n1_sort_order
FROM
phpcr_nodes n0
WHERE
n0.workspace_name = 'mango'
AND n0.type IN ('nt:unstructured' , 'rep:root')
AND (n1.parent = 'applications/1'
AND (EXTRACTVALUE(n0.props,
'count(//sv:property[#sv:name="phpcr:class"]/sv:value[text()="Mango\CoreDomainBundle\Document\Page"]) > 0')
OR EXTRACTVALUE(n0.props,
'count(//sv:property[#sv:name="phpcr:classparents"]/sv:value[text()="Mango\CoreDomainBundle\Document\Page"]) > 0')))
I hope somebody can help me out! Thanks!
Hmm, it looks like I was just dumb :P The alias used as the second argument of the child condition has to be the alias of the document you run the query builder on.
Wrong:
$qb = $this->dm->getRepository('Mango\CoreDomainBundle\Document\Page')
->createQueryBuilder('page');
$qb->where()->child('/applications/456', 'alias_1');
Good
$qb = $this->dm->getRepository('Mango\CoreDomainBundle\Document\Page')
->createQueryBuilder('page');
$qb->where()->child('/applications/456', 'page');
Also, in my case, I had to use descendant rather than child. See http://docs.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/reference/query-builder-reference.html#descendant
Topic can be closed :)

Drupal function to return content types associated with a term

I am looking for a function which will list out the content types which a specific term (tid) can be applied to.
There doesn't seem to be a direct way to do it from http://api.drupal.org/api/search/6/taxonomy.
Any ideas?
No, there doesn't seem to be one. You should be able to use this query, though.
$c = db_query(db_rewrite_sql("SELECT v.* FROM {vocabulary} v INNER JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = '%s' ORDER BY v.weight, v.name", 'v', 'vid'), $node->type);
Source: http://api.drupal.org/api/drupal/modules--taxonomy--taxonomy.module/function/taxonomy_form_alter/6
Or a simplified version of that, if you for example only need the vid.

Resources