prevent dql from entity joins - php

I want to write a DQL query that select post and join to another Entity's
here is my code
$dql = '
SELECT p , h ,t ,m
FROM App:Post p
LEFT JOIN p.mentions m
LEFT JOIN p.tags t
LEFT JOIN p.file h
WHERE p.user
IN (
SELECT f FROM App:User u
JOIN u.followers f
WHERE u.id = :uid
)
OR p.user = :uid ';
$query = $this->getEntityManager()
->createQuery($dql)
->setMaxResults(1)
->setParameters(['uid' => $user->getId()]);
$paginator = new Paginator($query, $fetchJoinCollection = true);
but the problem is the circular reference, for example, Post -> Tags -> Posts that is used in serialization and make project freeze and shows a blank page.
here is dump export
how can I handle that Except using loop look at PersistentCollection
UPDATE ::
here is my seriallizer code
$posts= [];
foreach ($paginator as $post) {
$posts[] = $post;
}
$serializer = SerializerBuilder::create()->build();
$gifts = $serializer->toArray($posts);

You can use serialization groups to avoid circular reference issues. Basically, this lets your define a group (or multiple ones) to each property, then you can ask only specific(s) group(s) to be serialized.
For symfony native serializer :
http://symfony.com/doc/current/serializer.html#using-serialization-groups-annotations
https://symfony.com/blog/new-in-symfony-2-7-serialization-groups
For JMS : https://jmsyst.com/libs/serializer/master/cookbook/exclusion_strategies

Related

Get children's posts in parents with Doctrine Extensions Tree Nested set

I'm using the nested set behaviour in Symfony2 with StofDoctrineExtension.
The category and post model are well configured, and the category tree works fine.
To show the posts of a category I use this query from my repository:
public function findAllPosts($category)
{
return $this->queryAllPosts($category)->getResult();
}
public function queryAllPosts($category)
{
$em = $this->getEntityManager();
$query = $em->createQuery('
SELECT p, c FROM AppBundle:Post p JOIN p.category c
WHERE c.slug = :category
ORDER BY p.created DESC
');
$query->setParameter('category', $category);
return $query;
}
But how could I do to show the posts of the children of the categories too?
If your CategoryRepository inherits from NestedTreeRepository you could do something like this:
$categories = $em->getRepository('XBundle:Category')
->childrenQueryBuilder($category)
->addSelect('posts')
->join('node.posts', 'posts')
->getQuery()
->getResult();
foreach ($categories as $category) {
$category->getPosts();
// do stuff
}
You should be able to do this in one query which will be close to this one as i am not pro SQL it usually takes me time and tests before i get it right but this is where i would start :
SELECT parent.* , children.* FROM
(SELECT p, c FROM AppBundle:Post p JOIN p.category c WHERE c.slug = :category) AS parent
INNER JOIN
(SELECT p1 FROM AppBundle:Post p1 JOIN p.category c1 ON c1.parent = parent.id ) AS children
not sure if you need to do the ON inside the inner select or the wrapper select for the join but you can try :)
I found the way. The query would be like this:
/*
* GET POSTS FROM PARENT AND CHILDREN
*/
public function getPostsParentAndChildren($children)
{
$em = $this->getEntityManager();
$posts = $em->createQueryBuilder()
->select(array('p', 'c'))
->from('AppBundle:Post', 'p')
->join('p.category', 'c')
->where('c.id IN (:children)')
->orderBy('p.created', 'DESC')
->getQuery();
$posts->setParameter('children', $children);
return $posts->getResult();
}
We pass an array with the children to the query, which we obtain with the function getChildren($categoryId). Remember that you have to pass the id (with this query), so you could get the ids like this:
$category = $repo->findOneBy(array('slug' => $slug1));
$children = $repo->getChildren($category);
$childrenIds[] = $category->getId();
foreach ($children as $child){
$id = $child->getId();
$childrenIds[] = $id;
}
$posts = $em->getRepository('AppBundle:Category')->getPostsParentAndChildren($childrenIds);

Symfony createNativeQuery, add count()

I have a query in a repository like :
$rsm = new ResultSetMapping;
$rsm->addEntityResult('\My\ProjectBundle\Entity\News', 't');
$rsm->addFieldResult('t', 'id', 'id');
$rsm->addMetaResult('t', 'account_id', 'account_id');
$qb = $this->_em->createNativeQuery(
'SELECT t.*
FROM news as t
LEFT JOIN
LEFT JOIN
WHERE
CONDITIONS CONDITIONS
',
$rsm
);
return $qb->getResult();
I simplified the above query which is used to retrieve the news that meet specific conditions.
I need to add a count() function to this query.
I have an other ManyToOne entity-relationship between Comment and News.
How to modify the query to get the comments number a given news has ?
I'm trying to add a left join to comment and add Count() in the select but I always get errors. How could I resolve this problem ?
Raw SQL with Doctrine is easier like this :
$em = $this->getDoctrine()->getManager()->getConnection();
$query = "
SELECT t.*
FROM news as t
LEFT JOIN
LEFT JOIN
WHERE
CONDITIONS CONDITIONS
";
$stmt = $em->prepare($query);
$stmt->execute();
$result = $stmt->fetchAll();

How to use CDbCriteria in Yii to apply SQL filters on Model

I try to get this mysql query to work with Yii model but i can't.
SELECT COUNT( qhc.countries_id) AS counter, q.question, co.name
FROM questions AS q , countries as co, questions_has_countries AS qhc
WHERE qhc.questions_id = q.id
AND co.id = qhc.countries_id
GROUP BY question
HAVING counter = 2
So far i have this, but somehow thou it seems ok, it doesnt work :
$criteria = new CDbCriteria();
$criteria->select = 'question, COUNT(countries_id) as counter';
$criteria->with = array('countries', 'categories');
$criteria->addInCondition('countries.id' , $_POST['Questions']['countries']);
$criteria->group = 'question';
$criteria->having = ('counter = 1');
$model = Questions::model()->findAll($criteria)
Pls help, I'am pretty new to Yii framework.
Thanks.
Sql from the log :
SELECT `t`.`question` AS `t0_c1`,
COUNT(countries_id) as counter, `t`.`id` AS `t0_c0`, `countries`.`id` AS
`t1_c0`, `countries`.`name` AS `t1_c1`, `categories`.`id` AS `t2_c0`,
`categories`.`name` AS `t2_c1` FROM `questions` `t` LEFT OUTER JOIN
`questions_has_countries` `countries_countries` ON
(`t`.`id`=`countries_countries`.`questions_id`) LEFT OUTER JOIN `countries`
`countries` ON (`countries`.`id`=`countries_countries`.`countries_id`)
LEFT OUTER JOIN `questions_has_categories` `categories_categories` ON
(`t`.`id`=`categories_categories`.`questions_id`) LEFT OUTER JOIN
`categories` `categories` ON
(`categories`.`id`=`categories_categories`.`categories_id`) WHERE
(countries.id=:ycp0) GROUP BY question HAVING (counter = 2). Bound with
:ycp0='1'
You have done most of work. Now you need to call the $criteria into model. Just like this
$rows = MODEL ::model()->findAll($criteria);
Where MODEL is model class of table which you want to apply criteria on.
To learn more about this you can follow this CActiveRecord Class.
Try to set together in CDbCriteria
...
$criteria->together = true;
$model = Question::model()->findAll($criteria);
when you use "as counter", your model must have a property named "counter" or it will not load it into your model.
if you don't have a property named "counter", try using another one of your models property that you are not selecting right now : "as someColumn"
and use condition or addCondition or .. instead of having
cheers

Doctrine fetch join

First I will give an example with some pseudo code and then I will explain what is the problem. Let me say I have two entities User and Phonenumber. Their relation is one-to-many. In my UserRepository I can have something like that:
class UserRepository
{
public function getUser($id, $type)
{
$users = $this->createQuery("SELECT u, p FROM User u JOIN u.phonenumbers p
WHERE u.id = :id AND p.type = :type")
->setParameters(array(
'id' => $id,
'type' => $type,
))
->getResult();
return $users[0];
}
}
In my app if I have something like:
$user = $userRepo->getUser(1, 'home');
var_dump($user->getPhonenumbers()); // here phonenumbers collection is ok
$user = $userRepo->getUser(1, 'work');
var_dump($user->getPhonenumbers()); // Here phonenumbers collection is wrong.
// It's exactly the same as the previous one.
So my questions is: Is it possible to use fetch join (with different criteria) and to get the proper collection each time?
Fetch joining and filtering a collection are not things that work together quite well. Here's how you should do it:
SELECT
u, p
FROM
User u
JOIN
u.phonenumbers p
JOIN
u.phonenumbers p2
WHERE
u.id = :id
AND
p2.type = :type
This applies filtering on the second joined (and not hydrated) p2, which results in correct hydration and filtering.
Use querybuilder, it is much simpler.
public function getUser($id, $type)
{
return $this->createQueryBuilder("u")
->leftJoin("u.Phonenumbers", "p", "WITH", "p.type=:type")
->where("u.id=:id")
->setParameters(.....)
->getQuery()
->getOneOrNullResult() ;
}

CakePHP, special select

I will be blunt, I need the model list to be built from this special select:
SELECT * FROM posts p WHERE p.user_id IN
(SELECT u.id FROM users u, following f
WHERE u.id = f.followee_id AND f.follower_id = $id)
ORDERED BY p.created
LIMIT $limit;
Is it possible to put this as a function into the model? I am quite new to CakePHP, I must admit.
I can't use the find functions here because then the posts wouldn't be properly "mixed".
Yes, you can add your own method to the Model class that will execute the query you write.
But don't forget to sanitize your input (it is not done automatically when you write your own queries) and to add table prefixes.
App::import('Sanitize');
class YourModel extends AppModel {
public function specialSelect($id, $limit) {
$id = Sanitize::paranoid($id); // or use (int) to convert it to integer
$limit = Sanitize::paranoid($limit);
return $this->query(
'SELECT * FROM '.$this->tablePrefix.'posts p WHERE p.user_id IN '.
'(SELECT u.id FROM '.$this->tablePrefix.'.users u, '.$this->tablePrefix.'.following f '.
'WHERE u.id = f.followee_id AND f.follower_id = '.$id.') '.
'ORDER BY p.created LIMIT '.$limit
);
}
}

Categories