Doctrine Query Builder - Search in collection - php

I'm trying to create fulltext search in Doctrine Query Builder. But I have here one problem.
In Post Entity I have this:
/**
* #ORM\Column(type="string")
*/
protected $title;
/**
* #ORM\ManyToMany(targetEntity="Tag", inversedBy="posts")
* #ORM\JoinTable(
* name="posts_tags",
* joinColumns={
* #ORM\JoinColumn(name="post_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="tag_id", referencedColumnName="id")
* }
* )
*/
protected $tags;
And I'm searching in post with this code:
$searched = $this->em->createQueryBuilder();
$searched->select('p')
->from(Post::getClassName(), 'p');
$searched->where(
$searched->expr()->like('p.title', $searched->expr()->literal('%' . $values->search . '%'))
);
But now I don't know how to search in "tags" because it's collection. Can somebody put me on right journey? Thank you very much.

Try an inner join:
$qb = $em->createQueryBuilder();
$posts = $qb->select('p')
->from(Post::getClassName(), 'p')
->innerJoin('p.tags', 't')
->where($qb->expr()->like('p.title', ':postTitle'))
->andWhere('t.name = :tagName')
->setParameter("postTitle", "%{$values->search}%")
->setParameter("tagName", "foobar")
->getQuery()->getResult();
Some variable names are made up, as I don't know your exact schema, but this should get you going.

Related

Doctrine join entities

I'm trying to make some efficient improvements when querying for an entity which has a relationship to another entity.
Entity A:
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #ORM\Column(name="name", type="string", length=225)
* #Assert\NotBlank(message="Please enter a name for your profile.")
*/
protected $display_name;
/**
* #ORM\OneToOne(targetEntity="Posts", mappedBy="profile", cascade={"all"}, fetch="LAZY")
*/
protected $posts;
Entity B:
/**
* #var integer
* #ORM\Column(name="profile_id", type="integer")
* #ORM\Id
*/
protected $profile_id;
/**
* #ORM\OneToOne(targetEntity="Profile", inversedBy="posts")
* #ORM\JoinColumn(name="profile_id", referencedColumnName="id")
*/
protected $profile;
/**
* #var string
* #ORM\Column(name="content", type="string")
*/
protected $content;
When I count the number of queries being executed I get two, I guess being doctrine runs two separate queries for each entity even through they have a relationship.
I am currently fetching entity A like so:
public function fetchById($id)
{
return $this->createQueryBuilder('p')
->where('p.id = :id')
->setParameter('id', $id)
->getQuery()
->getOneOrNullResult();
}
And then calling entity B like so:
$profile = $profileRepository->fetchById($user->getUserId());
$lastpost = $profile->getPosts()[0];
But I want to be able to join the second entity in this query so that I can just call one query rather than two. I was hoping to do something like this:
public function fetchById($id)
{
return $this->createQueryBuilder('p')
->select('p','pp')
->leftJoin(Posts::class, 'pp', \Doctrine\ORM\Query\Expr\Join::WITH, 'p.id = pp.profile_id')
->where('p.id = :id')
->setParameter('id', $id)
->getQuery()
->getResults();
}
However the left join returns an array of two entities. This is not what I am wanting, because I want to still be able to call for example the getPosts() method in entity A.
I essentially want to populate entity A, including all the related entities. But by only executing one query rather than two, is this possible in doctrine?
Some time ago I had a similar scenario (not with 1-1 but 1-n though), which I solved like this:
// ...Repository
public function findAllForUserWithAll(\AppBundle\Entity\User $u)
{
$query = $this->getEntityManager()->createQueryBuilder('ca1')
->add('select', 'c, s, i, m')
->add('from', 'AppBundle:Contact c')
->leftJoin('c.skills', 's')
->leftJoin('c.interests', 'i')
->leftJoin('c.metAt', 'm')
->where('c.user = :user')
->orderBy('c.lastname', 'ASC')
->setParameters([
'user' => $u,
])
->getQuery();
return $query->getResult();
}
This was in Symfony 2.8.* with Doctrine ^2.4.8 - and the result is one query with 3 joins (instead of 4 queries). Probably not the best code and honestly all the magic happened under the hood. Also your code looks alot alike. But maybe this is expected due to fetch="LAZY" (on entity A) - which my entity does not have?

createQueryBuilder / Join Column - Symfony

I have a trouble to implement this SQL Request :
SELECT *
FROM project
LEFT JOIN user
ON project.idAuthor=user.id
WHERE project.isVisible = 1 AND
user.role = 'agency'
To a simple Symfony Query Builder :
$query = $this->createQueryBuilder('p')
->leftJoin('WebAwardsBundle:User', 'u')
->where('p.isVisible = 1')
->andwhere("u.role = 'agency'")
->orderBy('p.id', 'DESC')
->getQuery();
The response of this query give me all the project, include the project when role !== agency ...
I don't know where I can put the ON project.idAuthor=user.id
Mapping :
Project :
/**
* Project
*
* #ORM\Table(name="project")
*#ORM\Entity(repositoryClass="WebAwardsBundle\Repostory\ProjectRepository")
*/
class Project
{
...
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="User", inversedBy="projects")
* #ORM\JoinColumn(name="idAuthor", referencedColumnName="id")
*/
private $idAuthor;
...
User :
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="WebAwardsBundle\Repository\UserRepository")
*/
class User implements UserInterface, \Serializable{
...
/**
* #ORM\OneToMany(targetEntity="Project", mappedBy="idAuthor")
*/
private $projects;
...
The correct answer to implement this SQL Request :
SELECT *
FROM project
LEFT JOIN user
ON project.idAuthor=user.id
WHERE project.isVisible = 1 AND
user.role = 'agency'
is to join the correct column of the entity (project.idAuthor to user in this case) :
$query = $this->createQueryBuilder('p')
->join('p.idAuthor', 'u')
->where('p.isVisible = 1')
->andWhere("u.role = 'agency'")
->orderBy('p.id', 'DESC')
->getQuery();

Doctrine2 with MS Server pagination and order by not working under ZF2

I have a list of news ordered by the published date and I want to paginate it. I am using Doctrine 2.5 in a Zend Framework 2 project. Here's my entity:
<?php
namespace BuscadorJuridico\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\Form\Annotation as Form;
/**
* Class News
* #package BuscadorJuridico\Entity
*
* #ORM\Table(name="news")
* #ORM\Entity
*/
class News
{
/**
* #var int
*
* #ORM\Column(name="news_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="news_title", type="string", length=200, nullable=false)
*/
public $title;
/**
* #var string
*
* #ORM\Column(name="news_body", type="blob", nullable=false)
*
*/
public $body;
/**
* #var \DateTime
*
* #ORM\Column(name="news_date_published", type="date", nullable=true)
*
*/
protected $datePublished;
/**
* #var bool
*
* #ORM\Column(name="news_status", type="boolean", nullable=false)
*
*/
public $published;
}
I am using the Doctrine\DBAL\Driver\SQLSrv\Driver and the Paginator componenr of ZF2. Here is the pagination code:
$query = $this
->entityManager
->getRepository('BuscadorJuridico\Entity\News')
->createQueryBuilder('news')
->select()
->orderBy('news.datePublished', 'desc')
->andWhere('news.published = true');
$paginator = new Paginator(new DoctrineAdapter(new ORMPaginator($query)));
$paginator->setCurrentPageNumber($page);
$paginator->setItemCountPerPage($count);
But when I go to the list, I get the error:
SQLSTATE [42000, 8120]: [Microsoft][ODBC Driver 11 for SQL Server][SQL Server]Column 'dctrn_result.news_date_published_3' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
When I delete the ->orderBy('news.datePublished', 'desc') line I get the list, but naturally not ordered. This is part of the SQL that Doctrine is generating:
SELECT DISTINCT
news_id_0,
MIN(sclr_5) AS dctrn_minrownum,
ROW_NUMBER() OVER (ORDER BY news_date_published_3 DESC) AS doctrine_rownum
FROM (
SELECT n0_.news_id AS news_id_0,
n0_.news_title AS news_title_1,
n0_.news_body AS news_body_2,
n0_.news_date_published AS news_date_published_3,
n0_.news_status AS news_status_4,
ROW_NUMBER() OVER(ORDER BY n0_.news_date_published DESC) AS sclr_5
FROM news n0_
WHERE n0_.news_status = 1)
dctrn_result
GROUP BY news_id_0
With MySQL the code works. Any help appreciated.
I actually have a PR open to fix this. I'll see if I can poke that along.
https://github.com/doctrine/dbal/pull/818
I used the PR from wschalle and created my own Mssql Driver included the fix, maybe it help you.
https://github.com/kokspflanze/GameBackend/tree/master/src/GameBackend/DBAL

How to query doctrine for dependent entities in Symfony2 controller

I don't find an example here in SO, so i post my question:
I have a group entity, a shop entity, and a transaction entity.
A group has many shops, and a shop can belong to many groups. In Group.php:
/**
* #ORM\ManyToMany(targetEntity="Shop", inversedBy="groups")
* #ORM\JoinTable(name="group_shop",
* joinColumns={#ORM\JoinColumn(name="group_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="shop_id", referencedColumnName="id")}
* )
**/
private $shops;
And in Shop.php
/**
* #ORM\ManyToMany(targetEntity="Group", mappedBy="shops")
*/
private $groups;
Then, a shop makes transactions. In Transaction.php:
/**
* #ORM\ManyToOne(targetEntity="Shop", inversedBy="transactions")
* #ORM\JoinColumn(name="shop_id", referencedColumnName="id")
* */
private $shop;
And in Shop.php:
/**
* #ORM\OneToMany(targetEntity="Transaction", mappedBy="shop")
**/
private $transactions;
What I want to query is all transactions from a group. This must be very simple, buy i'm blinded.
What I have:
$query4 = $em->createQuery("SELECT t FROM MGFAppBundle:Transaction t
WHERE t.date > :from AND t.date < :to AND t.shop IN (/* HERE I'M STUCK */)")- >setParameters(array(
'from' => $from
'to' => $to
));
I don't know if this is the correct approach or... well, dql is kinda hard to get for me.
How do I write this dql query?
Thanks in advance.
In your repository do something like this:
public function findTransactionsByGroup(GroupInterface $group)
{
return $this->createQueryBuilder('g')
->select('s.transactions')
->leftJoin('g.shops','s')
->where('s.group = :groupid')
->setParameter('groupid', $group->getId())
->getQuery()
->getResult();
}

Complex Query in Query Builder

How can I execute this query using QueryBuilder in Symfony 2
SELECT um.id, sv.patentgroup_id, pm.portfolio_id, pp.id
FROM sv_patents sv
LEFT JOIN pm_patentgroups pm ON sv.patentgroup_id = pm.id
LEFT JOIN pm_portfolios pp ON pm.portfolio_id = pp.id
LEFT JOIN um_users um ON pp.user_id
The associations in entity classes are defined as
In SvPatents I have
/**
* #var PmPatentgroups
*
* #ORM\ManyToOne(targetEntity="PmPatentgroups")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="patentgroup_id", referencedColumnName="id")
* })
*/
private $patentgroup;
In PmPatentgroups I have
/**
* #var PmPortfolios
*
* #ORM\ManyToOne(targetEntity="PmPortfolios")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="portfolio_id", referencedColumnName="id")
* })
*/
private $portfolio_id;
In PmPortfolios I have
/**
* #var UmUsers
*
* #ORM\ManyToOne(targetEntity="UmUsers")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* })
*/
private $user;
I tried this but it is giving me the whole results from the table looks like joins are not working propoerly
$repository = $this->getDoctrine()->getRepository('MunichInnovationGroupBundle:SvPatents');
$qb = $repository->createQueryBuilder('sv')
->leftJoin('sv.patentgroup','pm')
->leftJoin('pm.portfolio_id','pp')
->leftJoin('pp.user','um')
->getQuery();
$patents = $qb->getArrayResult();
I am quite new to Symfony2 and Doctorine2. I read the whole documentation but could not find such complex example actually i am bit confused with how the querybuilder build the query. It would be great if you explain it a little in simple words
Thanks in advance
I found the solution to my problem. If u looks for the records of a specific user in the table then you need to add one where clause. This is how your query should looks like
$user_id = 1;
$repository = $this->getDoctrine()->getRepository('MunichInnovationGroupBundle:SvPatents');
$qb = $repository->createQueryBuilder('sv')
->select('sv')
->leftJoin('sv.patentgroup','pm')
->leftJoin('pm.portfolio_id','pp')
->leftJoin('pp.user','um')
->where('pp.user = :user_id')
->setParameter('user_id', $user_id)
->getQuery();
$patents = $qb->getArrayResult();
Thats all :)
You're very close. You just have to add a ->select() query part:
$repository = $this->getDoctrine()->getRepository('MunichInnovationGroupBundle:SvPatents');
$qb = $repository->createQueryBuilder('sv')
->select('um.id, pm.id, pp.id') // add this part
->leftJoin('sv.patentgroup','pm')
->leftJoin('pm.portfolio_id','pp')
->leftJoin('pp.user','um')
->getQuery();
$patents = $qb->getArrayResult();

Categories