Symfony2 join query - symfony

I have a table of videos and in that table I have field comment which contains id of comment in other table, now I used join query to get that in one query, but how do I get that comment?
Here is my code:
$Actions = $this->EntityManager()->getRepository('AppBundle:Video')
->createQueryBuilder('V')
->join('AppBundle:VideoComment', 'VC')
->where('V.videoId = :VideoID')
->andWhere('VC.videoId = :VideoID')
->setParameter('VideoID', $VideoID)
->getQuery()
->getResult();
How do I get the actual comment from that joined entity?

You can do what #cezar said earlier but with one little change: you have to define field to retrieve related entries from comments table.
So, your query might look like this:
$em = $this->get('doctrine.orm.entity_manager');
$videos = $em->createQuery('select v
from YourBundle:Video v
left join YourBundle:Comment c
where v.comment = c.id')
->getResult();
or you can do the similar stuff using query builder:
$videos = $em->createQueryBuilder('v')
->add('select', 'v, c')
->add('from', 'YourBundle:Video v')
->leftJoin('YourBundle:Comment', 'c')
->where('v.comment = c.id')
... // some other conditions if you need
->getQuery()
->getResult();
Both cases I described account for that Video and Comment entity might not be in formal relations (I mean their relations might not be described in your doctrine/orm file).

Here is one proposal:
<?php
namespace You\AppBundle\Repository; // You is your vendor name, AppBundle is your bundle
use Doctrine\ORM\EntityRepository;
class VideoCommentRepository extends EntityRepository
{
public function getVideoComment($VideoId)
{
$query = $this->getEntityManager()->createQuery(
'SELECT v FROM YouAppBundle:Video v LEFT JOIN v.comment c
WHERE v.id = :id'
)->setParameter('id', $VideoId);
return $query->getResult();
}
}
As you said you have a table 'video' and in that table there is a field 'comment' that contains the IDs of the comments. I suppose you have 'oneToMany' relation from 'video' to 'comment'. With this simple query you should be able to get all comments for a given VideoID. I didn't test this, but I think it should work. Try it out and adapt it as needed.

Related

How to combine queryBuilder and raw queries on join table from many to many relation?

I have got following query
SELECT news.*, count(*) AS common_tags
FROM news
JOIN news_news_tag ON news.id = news_news_tag.news_id
WHERE news_tag_id IN
(SELECT news_tag_id FROM news_news_tag WHERE news_id = 2 )
AND news.id != 2 GROUP BY news.id ORDER BY common_tags DESC
What I want to achieve is to get hydrated news objects ordered by number of common tags with provided news id. News and Tag are many to many relation with news_news_tag join table.
News entity have got much more other relations. This is why I don't want to create a native query by myself to handle all other relations.
I would like to convert above query to use it with query builder. I wasn't able to use a DQL because my where statement uses a join (junction) table and I also need to use a join on that table.
All in all I have got 2 problems:
How can I create DQL subquery to select something from many to many join table)? If I know that I could do something like: ->where($queryBuilder->expr()->in('u.id', $mySubQueryAsDQL))
How to add that join statement that I could use news_tag_id in where statement?
If it is not possible I think that I would need to create two bidirectional one-to-many and many-to-one relations instead of many-to-many and work on special joining entity.
I finally came up with following solution. I decided to split that query into two separate ones. In first I can use just simple raw query as I need results only to where statement. Second query can be build using query builder in a normal way.
You have to only get rid of an extra count column at the end.
However if someone knows the solution how to use my first raw query directly inside query builder as a raw subquery for where statement please share with me.
public function getRelatedNews($newsId, $limit = 6)
$connection = $this->getEntityManager()->getConnection();
$sql = 'SELECT news_tag_id FROM news_news_tag WHERE news_id = :newsId';
try {
$stmt = $connection->prepare($sql);
} catch (DBALException $e) {
return [];
}
$stmt->execute(['newsId' => $newsId]);
$newsTagId = $stmt->fetchAll();
if (empty($newsTagId)) {
return [];
}
$newsTagId = array_column($newsTagId, 'news_tag_id');
$query = $this->createQueryBuilder('n')
->addSelect('COUNT(n.id) as common_tags_count')
->innerJoin("n.tags", "t")
->andWhere('t.id IN(:tagsId)')->setParameter('tagsId', array_values($newsTagId))
->andWhere('n.id != :newsId')->setParameter('newsId', $newsId)
->orderBy('common_tags_count', 'DESC')
->setMaxResults($limit)
->groupBy('n.id')
;
$results = $query->getQuery()->getResult();
$news = [];
foreach ($results as $result) {
$news[] = $result[0];
}
return $news;
}

Symfony / Doctrine findBy

Hove to create custom Repository function who query by json field. I have params column in my database who look like this:
"params": {
"product": "stopper",
"itemIdentifier": ""
}
I want to query record by product value. In this case stopper term.
You can achieve this with a classic example :
In your repository :
For one result
public function findOneProduct($value): ?Params
{
return $this->createQueryBuilder('p')
->andWhere('p.product = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
For multiple result
public function findParamsByProduct($value): ?Params
{
return $this->createQueryBuilder('p')
->andWhere('p.product = :val')
->setParameter('val', $value)
->orderBy(/*some field */)
->setMaxResults(/*if needed*/)
->getQuery()
->getResults()
;
}
In your controller:
$stoppers = $entityManager->getRepository(Params::class)->findParamsByProduct('stopper');
If I understood your question correctly, you have a table with a column named params. And inside this mysql column, you store JSON text.
And then you want to query that table and filter by looking into the JSON in your column.
This can be a bit tedious and was also highly discouraged in the past (prior to the JSON Type in Mysql 5.7.8).
Best practices would be to have a NoSQL DB such as MongoDB which is actual JSON stored in a collection(table).
Anyways, there is a solution for you.
Taking into account #AppyGG explained how to make a custom repository function.
First of all, we have to make a query using pure SQL.
It can be done two ways:
1.Return arrays containing your data.
$conn = $this->getEntityManager()->getConnection();
$sql = '
SELECT * FROM product p
WHERE p.price > :price
ORDER BY p.price ASC
';
$stmt = $conn->prepare($sql);
$stmt->execute(['price' => $price]);
// returns an array of arrays (i.e. a raw data set)
return $stmt->fetchAll();
2.Return hydrated Entities
use Doctrine\ORM\Query\ResultSetMappingBuilder;
$rsm = new ResultSetMappingBuilder($entityManager);
$rsm->addRootEntityFromClassMetadata('MyProject\Product', 'p');
$sql = '
SELECT * FROM product p
WHERE p.price > :price
ORDER BY p.price ASC
';
$nql = $this->_em->createNativeQuery( $sql, $rsm );
$nql->setParameter('price', $price);
//Return loaded entities
return $nql->getResult();
Now, knowing how to make make a MySQL query with doctrine, we want to select results filtered in JSON data.
I'm am referencing this beautiful stackoverflow which explains it all:
How to search JSON data in MySQL?
The easiest solution proposed in there requires at least MySQL 5.7.8
Your MySQL query would be as follow:
//With $entity->getParams() == '{"params": {"product":"stopper", "itemIdentifier":""}}'
$conn = $this->getEntityManager()->getConnection();
$sql = '
SELECT * FROM Entity e
WHERE JSON_EXTRACT(e.params, "$.params.product") = :product
';
//Or Like this if the column is of Type JSON in MySQL(Not doctrine, yes check MySQL).
$sql = '
SELECT * FROM Entity e
WHERE e.params->"$.params.product" = :product
';
$stmt = $conn->prepare($sql);
$statement->bindValue("product","stopper");
$stmt->execute();
return $statement->fetchAll();
Hope this helps!
P.S: Note that my example uses a column named 'params' with a Json containing also a named attribute 'params', this can be confusing. The intended purpose is to show how to do multiple level filtering.

Delete function using join in DQL

I'm trying to use the Doctrine QueryBuilder to perform the Delete function using query
I need to delete a record that is present in 2 tables ,
in TcTracks table the id is "id" and in TcWall teh id is "related_id"
my controller
public function deleteAction(Request $request){
$deleteQuery = $this->getDoctrine()
->getManager()
->createQueryBuilder('d')
->delete('TcPlayerBundle:TcTracks', 'd')
->innerJoin('TcprofileBundle:TcWall', 't', 'ON', 'd.id = t.related_id')
->where('d.id = :dId')
->setParameter('wId', $request->get('related_id'))
->setParameter('dId', $request->get('id'))
->getQuery();
$deleted = $deleteQuery->getResult();
$deleted->flush();
return $this->render('TcPlayerBundle:Default:all.html.twig',array(
'tracks' => $tracks
));
}
i need to delete same record in two tables , but its not performing for both tables, kindly help me
i'm getting error as
Invalid parameter number: number of bound variables does not match number of tokens
Remove the line below:
->setParameter('wId', $request->get('related_id'))
There is only :dId but no :wId.
Actually you did inner join by 'd.id = t.related_id', so related_id is not necessary to set any more.
You don't have wID parameter anywhere in query. In the same time you're using ->setParameter('wId', $request->get('related_id')) which is obsolete in this case. Try to remove this row and it should be fine.
Is one-to-many realation with your entities
TcPlayerBundle:TcTracks
and
TcprofileBundle:TcWall
if like that,you can do some like:
oneToMany:
cascade: [remove]
in your config 'yourentity.orm.yml' file..
hope hlep you!

Symfony2 Doctrine Join Entity

I have a entity with the next join:
class blogComment
{
....
/**
* #ORM\OneToMany(targetEntity="BlogComment", mappedBy="replyTo")
*/
protected $replies;
....
}
Now I get successfully all the replies. But I only want to get: where active = true
How to do that?
Oke if you guys recommend to get the comments by query in the controller how to build a nested array to get result like this:
For solving the part where you only want active replies there are a couple of options:
1) Use some custom DQL in a repository:
$dql = 'SELECT bc FROM BlogComment bc WHERE bc.replyTo = :id AND bc.active = :active';
$q = $em->createQuery($dql)
->setParameters(array('id' => $id, 'active' => true));
2) Using ArrayCollection::filter() in the getter:
public function getReplies()
{
return $this->replies
->filter(function ($reply) {
return $reply->isActive();
})
->toArray();
}
3) Using ArrayCollection::matching() (Collection Criteria API) in the getter:
use Doctrine\Common\Collections\Criteria;
// ...
public function getReplies()
{
$criteria = new Criteria::create()
->where(Criteria::expr()->eq('active', true));
return $this->replies
->matching($criteria)
->toArray();
}
4) Use Filters. These can add where clauses to queries regardless of where that query is generated. Please see the docs.
If you want to be able to fetch an entire set of replies, nested and all, in a single query, you need to implement some kind of "tree" of "nested set" functionality. I'd advise you to look at the Tree behavior of l3pp4rd/DoctrineExtensions.
http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html
Wherever you are obtaining your blog comments to display them (probably on a controller), you need to customise your query so that only the active replies are extracted. Something like:
$query = $em->createQuery('SELECT b FROM blogComment b JOIN b.replies r WHERE r.active = :active');
$query->setParameter('active', true);
$blogComments = $query->getResult();
EDIT:
For your new requirement of nested replies, you would need to specify a relationship between a comment entity and its parent comment.

Doctrine2: using the querybuilder for many-to-many relationships

To search people in a system I have written a function in a repository.
This function receives an array with field names and criteria, and uses the querybuilder to formulate a valid DQL-statement.
Everything works fine, until the moment I want to make a search rule like:
"Find all people that belong to a certain domain."
...where the Person and Domain entities have a many-to-many relationship.
According to Symfony2 that relationship is defined correctly, and indeed everything works in Twig templates and controllers.
The function looks as follows:-
private function query($extra_conditions = null)
{
$qb = $this->_em->createQueryBuilder();
$query =
$qb->select('p')
->from('AppMainBundle:Person', 'p')
->leftjoin('AppMainBundle:Domain', 'd')
->where("p.firstname IS NOT NULL")
->andWhere("p.lastname IS NOT NULL")
->andWhere("p.function IS NOT NULL");
if ($extra_conditions!=null)
{
foreach ($extra_conditions as $condition)
{
#filter on firstname
if (key($condition)=='firstname')
$query = $query->andWhere('p.firstname LIKE ?1')
->setParameter(1, $condition['firstname']);
#filter on lastname
if (key($condition)=='lastname')
$query = $query->andWhere('p.lastname LIKE ?1')
->setParameter(1, $condition['lastname']);
#filter on gender
if (key($condition)=='gender')
$query = $query->andWhere('p.gender = '.$condition['gender']);
#filter on domain(s)
if (key($condition)=='domains')
$query = $query->andWhere('d.id IN ('.$condition['domains'].')');
#filter on town
if (key($condition)=='town')
$query = $query->andWhere('p.town LIKE ?1')
->setParameter(1, $condition['town']);
}
}
$query = $query->orderBy("p.lastname", "ASC");
$query = $qb->getQuery();
return $query->getResult();
}
There is no error, but whatever domains are given as search criteria; people from all domains are found and returned.
Domains are provided as an array like (2, 4).
What can I change to make it possible to find people belonging to certain domains?
After some trial-and-error I found two possible answers:
1) Apparently in the querybuilder you can refer to fields that live in join-tables, and are not actually part of an entity. Hence one solution would be:
#filter on domain(s)
if (key($condition)=='domains')
$query = $query->andWhere('p.domain_id IN ('.$condition['domains'].')');
...where domain_id lives in the join table "person_domain" created by Symfony2 unther the hood.
2) The inner join is now no longer needed. Using an inner join is possible as well, however it was written the wrong way and would not have worked anyway. It should have been written as:
->leftjoin('p:domain', 'd', 'ON p.id = d.id')
Hope that other people may benefit from the above.

Resources