How to build this query on Symfony with QueryBuilder? - symfony

Please I am a beginner on symfony and I have 2 entities: category and professionnal with ORM many to many. And 'City' is an entity with ORM oneToMany with 'professionnal'.
In professionnel I have a property city. I want to show professionnal grouped by categories who have a special cityId putted on parameter in the url.
I put this query on sql
(for example city_id= 10873)
, it gives me the results.
SELECT * FROM sub_category AS a LEFT JOIN professionnel ON professionnel.city_id = 10873
but I don't know how to write with querybuilder.
I put this solution but I have errors:
$city = $paramFetcher->get('city');
$queryBuilder = $em->getRepository(SubCategory::class)
->createQueryBuilder('a')
->leftJoin('App\Entity\Professionnel','p')
->where('p.city = :city')
->setParameter('city', $city);
return $queryBuilder->getQuery()->getResult();
in log:
request.CRITICAL: Uncaught PHP Exception Doctrine\DBAL\Exception\SyntaxErrorException: "An exception occurred while executing 'SELECT c0_.id AS id_0, c0_.name AS name_1 FROM category c0_ LEFT JOIN professionnel p1_ WHERE p1_.city_id = ?' with params ["10873"]: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'WHERE p1_.city_id = '10873'' at line 1"
I search on forums too and I found that I can replaced the 'ON' of leftJoin as following, but shows me all professionnals:
$city = $paramFetcher->get('city');
$queryBuilder = $em->getRepository(SubCategory::class)
->createQueryBuilder('a')
->leftJoin('App\Entity\Professionnel','p','WITH','p.city = :city')
->setParameter('city', $city);
return $queryBuilder->getQuery()->getResult();
For the mapping, I put it like this:
class Category
{
/**
* Many professionnels have Many categories.
* #ORM\ManyToMany(targetEntity="App\Entity\Professionnel", mappedBy="Categories")
* #ORM\JoinTable(name="professionnel_category")
*/
private $professionnels;
}
class Professionnel extends User
{
/**
* Many professionel have Many categories.
* #ORM\ManyToMany(targetEntity="App\Entity\Category", inversedBy="professionnels")
* #Groups({"Default", "professionnel", "user"})
*/
private $categories;
}
Thank you so much for your help.

So, assuming that you have your entity relation set up correctly (please update your question with those), I see one mistake above.
If you look at the official Doctrine Query Builder documentation, the leftJoin method's third argument is either WITH or ON constant and not join predicate. That one comes as a 4th argument. But, even then, that join predicate is only used is a very specific use-case, where you want to further define your join relation. By looking at your example, I doubt that is your case.
With that being said, I think your code should look like this:
$city = $paramFetcher->get('city');
$queryBuilder = $em->getRepository(Category::class)
->createQueryBuilder('a');
->leftJoin('App\Entity\Professionnel','p')
->where('p.city_id = :city')
->setParameter('city', $city);
return $queryBuilder->getQuery()->getResult();
Now the question: is that really just a city_id (plain integer) or is that relation to some entity named City?
Please update your question with those details and I will update the answer, if necessary...
Hope this helps...

Firstly, you need describe relations in entities. Read more about this here
Secondary, you must call in join field (read as property), not a Entity.
inside CategoryRepository:
/** #return ArrayCollection|Category[] */
public function getByCityId(int $cityId): ArrayCollection
{
$qb = $this->createQueryBuilder('category');
$qb->leftJoin('category.professional', 'professional')
->leftJoin('category.city', 'city')
->where($qb->expr()->eq('city.id', $cityId));
return $qb->getQuery()->getResult();
}
Inside SomeController:someAction()
$cityId = $paramFetcher->get('city');
$categoriesList = $this->get('doctrine.orm.entity_manager')
->getRepository(Category::class)
->getByCityId($cityId);
As result you'll be have list of category, with joined professional and city.
I suggest to read this article on symfony docs

Related

Doctrine select many from the one (many-to-one unidirectional (different bundles))

Working on a legacy project which restricts the options available, has left me in a situation where I need to solve the following problem, ideally with doctrine.
I have two entities in different bundles that have a unidirectional many-to-one link.
BundleA has dependency on BundleB and the entities are linked similar to this:
BundleA/Entity/TheMany:
/**
* #var TheOne $theOne
* #ORM\ManyToOne(targetEntity="BundleB\Entity\TheOne")
* #ORM\JoinColumn(name="theone_id", referencedColumnName="id", onDelete="SET NULL")
*
*/
private $theOne;
From BundleB I now need to select all TheOne entities, and for each I need all of the TheMany entities.
The query also needs to be sortable on any property of TheOne entity, or the count of related TheMany entities.
It is fairly simple in Doctrine to build a query which brings back all TheOne entities and one of TheMany for each... however I am having some difficulty coming up with a Doctrine query that will bring back all of the related TheMany entities rather than just one.
I was hoping someone might have encountered a similar issue and therefore have some insight?
This may not have been explained clearly enough, in which case please direct me to explain further.
In the end I was able to achieve what I needed by using GROUP_CONCAT (which required inclusion of https://github.com/beberlei/DoctrineExtensions).
The query looks something like this:
$queryBuilder->select(
'to,
GROUP_CONCAT(DISTINCT tm.id SEPARATOR \',\') as theManyIds,
COUNT(DISTINCT tm.id) as HIDDEN theManyCount'
)
->from('BundleB\Entity\TheOne', 'to')
->leftJoin(
'BundleA\Entity\TheMany',
'tm',
Join::WITH,
'to.id = tm.theOne'
)
->groupBy('to.id')
->orderBy($sortString, $direction)
->setFirstResult($start)
->setMaxResults($limit);
I compromised by accepting the consequences of linking the two bundles - however that could have been avoided by making use of Native SQL and Result Set Mapping (http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/native-sql.html).
So what you are trying to do is to get all the ones and for each one find all the many. But you want to put all the many in one array or you want to create an array of array for the entities ? (what i did here)
$em = $this->getDoctrine()->getManager();
$theones = $em->getRepository('BundleA:theOne')
->createQueryBuilder('p')
->OrderBy(//your ordering)
->getQuery()
->getArrayResult()
$theManies = [];
for($theones as $theOne){
$theManies [] = $em->getRepository('BunbleB:theMany')
->createQueryBuilder('p')
->Where('p.theOne = :theOne')
->setParameter('theOne', $theOne)
->getQuery()
->getArrayResult();
$finalOnes[$theOne->getId()] = sizeof($theManies)
}
asort($finalOnes);
return array_keys($finalOnes);

Doctrine2 QueryBuilder select entity and count of associated entities

I'm having a huge problem with ORM QueryBuilder. What I need to do is:
I need to fetch order with count of its products and plenty of associated entities (associated with order), but I assume they're not relevant here. I also need to order result by that count.
Could anyone give me an example of how this can be achieved? I would like to avoid "inline" DQLs if possible.
You can get data via Doctrine Query Builder.
You are supposed to left join products from Order and then group by order id. You can have COUNT(product.id) in your select statement and use the alias in order by clause to make your orders sorted. Below is a small code snippet from Repository.
/**
* #return \Doctrine\ORM\Query
*/
public function getHotelAndRoomType()
{
$qb = $this->createQueryBuilder('order')
->select('partial order.{id, orderId} as order, count(product.id) as total_products_in_order')
->leftJoin('AppBundle:Product', 'product', 'WITH', 'product.order = order.id')
->groupBy('order.id')
->orderBy('total_products_in_order', 'DESC')
;
return $qb->getQuery()->execute();
}
Note : Code not tested.

Getting custom getter values from Entity with DQL in symfony2

I'm using FOSUserBundle and FOSRestBunldle with Symfony 2.8 and have to get a short user List with age, however there is no field 'age' in the user database, which is why my User entity class has this custom getter to calculate the age from the birthdate field
public function getAge()
{
$now = new \DateTime('now');
$age = $this->birthdate->diff($now);
return $age->format('%y');
}
Getting my user list with "u.age" and serializing it to json however, does not work, as there is no real field associated with u.age
$query = $em->createQuery('SELECT u.username, u.age FROM AppBundle:User u');
$users = $query->getResult();
Using the Repository with ->findAll() would get the ages, but also loads a lot of other related entities (posts, comments by the user) which are not needed here and would be a bit of an overload.
How can i get a list of my users with just their username and respecive age?
thank you for your help!
I have found the solution. Using the Annotation #VirtualProperty tells the serializer to get the value.
use JMS\Serializer\Annotation\VirtualProperty;
has to be included into Entity class, so annotation works.
/**
* #VirtualProperty
*/
public function getAge()
{
You don't have age field in DB, which also means you don't have it in Doctrine mapping, therefore you can't select it. Doctrine doesn't know anything about it.
You need to select data that is required to calculate it inside entity instance. In this case in order to calculate user's age you probably want to select birthday date.
E.g.:
$query = $em->createQuery('SELECT u.username, u.birthday FROM AppBundle:User u');
$users = $query->getResult();
Then you should be able to get age from your entity in PHP code.

limit columns returned in relationnal entity symfony2

Is it possible to filter an entity and display only few columns in symfony2?
I think I can do a custom query for this, but it seems a bit dirty and I am sure there is a better solution.
For example I have my variable $createdBy below, and it contains few data that shouldnt be displayed in this parent entity such as password etc...
/**
* #var Customer
*
* #ORM\ManyToOne(targetEntity="MyCompany\Bundle\CustomerBundle\Entity\Customer")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="created_by", referencedColumnName="id", nullable=false)
* })
*/
protected $createdBy;
So I need to display my Customer entity, but only containing fields like id and name for example.
EDIT :
I already have an instance of Project, the entity with my createdBy field, and I want to grab my customer data 'formatted' for this entity and not returning too much fields like password ...
Thanks
It sounds like expected behavior to me. The doctrine documentation seems to imply that eager fetching is only one level deep.
According to the docs:
Whenever you query for an entity that has persistent associations and
these associations are mapped as EAGER, they will automatically be
loaded together with the entity being queried and is thus immediately
available to your application.
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-eager-loading
The entity being queried has eager on createdBy so it will be populated.
to bypass you can create a method in your entity repository as following :
// join entities and load wanted fields
public function findCustom()
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM AppBundle:Product p ORDER BY p.name ASC'
)
->getResult();
}
hope this helps you
try this and let me know if it works, you should fill the right repository name
'XXXXBundle:CustomerYYYY', 'c'
public function findUser($user_id){
$qb = $this->_em->createQueryBuilder('c')
->select(array('c', 'cb.id', 'cb.name'))
->from('XXXXBundle:Customer', 'c')
->where('c.id <> :id')
->leftJoin('c.createdBy', 'cb')
->setParameter('id', $user_id)->getQuery();
if ($qb != null)
return $qb->getOneOrNullResult();
return null;
}

Query Builder or Closure or DQL, which is the right one for get ride of this complex SQL query with ManyToMany relationship?

So I've this two entities:
class TipoRegistro
{
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\TipoTramite", inversedBy="tipoRegistros", cascade={"persist"})
* #ORM\JoinTable(name="nomencladores.tipo_registro_tipo_tramite", schema="nomencladores",
* joinColumns={#ORM\JoinColumn(name="tipo_registro_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="tipo_tramite_id", referencedColumnName="id")}
* )
*/
protected $tipoTramites;
}
class TipoTramite
{
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\TipoRegistro", mappedBy="tipoTramites", cascade={"persist"})
*/
protected $tipoRegistros;
}
I need to get all the TipoRegistro associated to TipoTramite trhough ManyToMany relationship as show before as the SQL below shows:
SELECT tr.*
FROM
nomencladores.tipo_registro tr
LEFT JOIN
nomencladores.tipo_registro_tipo_tramite AS trtt
ON (trtt.tipo_registro_id = tr."id")
WHERE
tr.activo = true
AND
trtt.tipo_tramite_id = 1;
I don't know if I can use a custom repository here so my only solution is to use query_builder parameter at form level and this is what I'm doing:
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('tr')
->where('tr.activo = :activo')
->leftJoin('tr.tipoTramites', 'ttr')
->andWhere("ttr.tipo_tramite_id = :tramite")
->setParameter("tramite", 1)
->setParameter('activo', true);
}
But that solution cause this error:
[Semantical Error] line 0, col 121 near 'tipo_tramite_id': Error:
Class AppBundle\Entity\TipoTramite has no field or
association named tipo_tramite_id
I tried this one also (as suggested per user here):
->andWhere("ttr.tipoTramites = :tramite")
But the errors turns into this one:
[Semantical Error] line 0, col 120 near 'tipoTramites': Error: Invalid
PathExpression. StateFieldPathExpression or
SingleValuedAssociationField expected
Also I've tried this other solution:
->andWhere("tr.id = :tramite")
But this one doesn't return the right values. I read something about Closures on the entity field type but has no idea in how to implement on this scenario. The other idea comes to me is use a DQL and a preparement statement but I thin this can't be used at form level, so I'm out of ideas, then how I can get those values? How I should establish the relation between this ManyToMany entities?
When you create a ManyToMany relationship in your entity, Doctrine will generate the third table as mediator between those two entities (named in #ORM\JoinTable) with two fields (defined in joinColumns). You do not have direct access to the fields or columns in the third table but they all will be handled by Doctrine in your DQL.
This means in your DQL you need to just set the joint table id in your where clause not the real field name and Doctrine will produce the proper SQL:
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('tr')
->where('tr.activo = :activo')
->leftJoin('tr.tipoTramites', 'ttr')
->andWhere("ttr.id = :tramite")
->setParameter("tramite", 1)
->setParameter('activo', true);
}
This will produce the proper SQL.
The only tip is you do not have access to the generated fields of third table in you DQL, but it will be handled by Doctrine and you can use the join table id instead

Resources