Where should I put this query to access it from the entity? - php

I have two entities with relationship One Donacion Many Pajuelas.
Now I have it in DonacionRepository as:
public function getPajuelasReservadas($idDonacion)
{
$em = $this->getEntityManager();
$consulta = $em->createQuery(
"SELECT COUNT(p)
FROM EntidadBundle:Pajuela p JOIN p.donacion d
WHERE d.id = :idDonacion AND p.reservada = TRUE"
)
->setParameter("idDonacion", $idDonacion);
return $consulta->getResult();
}
I would like to access this query from a donation entity, without having to use $idDonacion.
Something like: $donation->getPajuelasReservadas();
What is the right way to do this? Thank you

Related

Symfony - how to use an array as an parameter by using a querybuilder?

I'm about to output a list that includes several documents(called waiver). However not every user should be allowed to see all documents and therefore I've implemented an filter to check if the user has the same "airline" and "market" assigned. So every user should only see the documents that are assigned to his "airline" and "market".
This is f.e. the getter for the airline of the user entity:
/**
* Get airlines
*
* #return array
*/
public function getAirlines()
{
if($this->airlines != null)
{
$airlines = explode(",", $this->airlines);
return $airlines;
}
return Array();
}
This is the controller logic:
//Get User:
$user = $this->container->get('security.context')->getToken()->getUser();
// Gets an Array of User markets
$user_markets = $user->getMarkets();
// Gets an Array of User carriers
$user_airlines = $user->getAirlines();
if(!$this->ROLE_IS(Array( 'ROLE_XY'))){
$query = $em->createQuery(
'SELECT w
FROM WaiverBundle:Waiver w
WHERE w.carrier = :carrier
AND w.market = :market
ORDER BY w.id DESC'
)
->setFirstResult($page*10-10)
->setMaxResults(10)
// I wan't to get the whole array and not just one position here:
->setParameters(array(':carrier'=>$user_airlines[0],
':market'=>$user_markets[0],
));
}else{
$query = $em->createQuery(
'SELECT u
FROM WaiverBundle:Waiver u
ORDER BY u.id DESC'
)
->setFirstResult($page*10-10)
->setMaxResults(10);
}
Question: How do I manage to compare the DQL attributes with an array and not just a string as a parameter?
I think you want to use "IN" syntax, not "=" syntax:
'SELECT w
FROM WaiverBundle:Waiver w
WHERE w.carrier IN (:carrier)
AND w.market = :market
ORDER BY w.id DESC'
Your query is not complicated. I think you should consider QueryBuilder instead of DQL in this case. Something like this would do the trick:
$qb = $em->createQueryBuilder();
$qb->select('w')
->from('WaiverBundle:Waiver', 'w')
->where($qb->expr()->in('w.carrier', ':carrier'))
->andWhere($qb->expr()->eq('w.market', ':market'))
->orderBy('w.id', 'DESC')
->setParameters(
array(
'carrier'=>$user_airlines[0],
'market'=>$user_markets[0)
);

Doctrine get entity with item

I want to get the field of an entity that is associated with another .
My entity Offers has a last_offer field.
Offers is related to the Products entity.
Then , with a consultation in my entity Products, I want to get the latest offer associated with the entity Offer.
Controller:
public function lastAction($key)
{
$em = $this->getDoctrine()->getManager();
$last_offer = $em->getRepository('MyAppBundle:Products')->findOfferByKey($key);
$response = new JsonResponse();
return $response->setData($last_offer);
}
My repository:
public function findOfferByKey($key){
$em = $this->getEntityManager();
$dql = 'SELECT pr, of FROM MyAppBundle\AppBundle\Entity\Products pr
INNER JOIN pr.offers of
WHERE pr.key = :key';
$query = $this->getEntityManager()
->createQuery($dql)
->setParameter('key', $key)
->setHydrationMode(\Doctrine\ORM\Query::HYDRATE_ARRAY);
return $query->execute();
}
My routing:
last_offer:
path: /{key}/last_offer
defaults: { _controller: "MyAppBundle:Products:last" }
But, this return an array.
I want to return only last_offer element.
Or to return the entity offers without being in an array.
You're specifically telling doctrine to generate/return an array with this line
->setHydrationMode(\Doctrine\ORM\Query::HYDRATE_ARRAY);
I think all you need to do is remove that line and doctrine will generate/return entities instead. However, you will get a Collection back, so make sure to take that into account. Perhaps something like
public function findOfferByKey($key) {
$dql = 'SELECT pr, of FROM MyAppBundle\AppBundle\Entity\Products pr
INNER JOIN pr.offers of
WHERE pr.key = :key';
$query = $this->getEntityManager()
->createQuery($dql)
->setParameter('key', $key)
;
$results = $query->execute();
if (count($results) !== 1)
{
// It's up to you how to handle zero or multiple rows
}
return $results->current();
}
EDIT
I see what's happening - I wasn't paying attention to your SELECT caluse. You're not selecting just the last_offer columns, you're selecting the entirety of the Products and Offers entities = $results is going to be an array of all of these together. In this scenario, $query->execute() will return an array() instead of a collection.
If you want to select just the Offer entities, you need to modify the SELECT
$dql = 'SELECT of
FROM MyAppBundle\AppBundle\Entity\Products pr
INNER JOIN pr.offers of
WHERE pr.key = :key';
But be wary that this still may return more than one row.

How can I check whether an array parameter is empty?

I want to select users from a database with Doctrine and Symfony. Depending on whether I have a supplied list of user IDs I want to only select users with these IDs. If the list is empty, then all users should be selected.
Here is the code I have created so far:
class UserRepository extends EntityRepository {
public function selectUsers (array $userIds) {
$dql = "
SELECT
u
FROM
MyBundle:User
WHERE
u.id IN (:users)"; // OR (:users) does not contain any values
$query = $this
->getEntityManager()
->createQuery($dql)
->setParameter("users", $userIds);
return $query->getResult();
}
}
How can I check whether the array is empty? So far, I have tried IS EMPTY, = (), = [], SIZE(:users) = 0, COUNT(:users) = 0 but all of them give me errors. What is the correct syntax here?
You can dynamically build DQL query
public function selectUsers(array $userIds)
{
$dql = "SELECT u FROM MyBundle:User";
$params = array();
if ($users) {
$dql .= " WHERE u.id IN (:users)";
$params["users"] = $userIds;
}
return $this->getEntityManager()->createQuery($dql)->execute($params);
}
You are building two different queries - select all, select specific users. I think you cannot build such SQL query. Maybe DQL has some shortcut how you can do it, but I would prefer SQL-ish syntax.
The only solution I have come up with so far is to calculate the count in PHP and pass it in as an additional parameter.
class UserRepository extends EntityRepository {
public function selectUsers (array $userIds) {
$dql = "
SELECT
u
FROM
MyBundle:User
WHERE
u.id IN (:users) OR :userCount = 0";
$query = $this
->getEntityManager()
->createQuery($dql)
->setParameter("users", $userIds)
->setParameter("userCount", count($userIds));
return $query->getResult();
}
}
However, I have a hard time believing that this is impossible to do directly in DQL.

Second counting query for pagination

I have a couple of pretty complex queries, and for each of them I have to write a second query counting results. So for example, in the model:
$dql = "SELECT u FROM AcmeBundle:Users u LEFT JOIN AcmeBundle:Products p WITH u.id = p.id";
I would have to create a duplicate query like this:
$countingQuery = "SELECT COUNT(u.id) FROM AcmeBundle:Users u LEFT JOIN AcmeBundle:Products p WITH u.id = p.id";
The main problem with that is that with every change in the first query, I would have to change the second either.
So I came up with another idea:
$countingSelect = "SELECT COUNT(u.id)";
$noncountingSelect = "SELECT u";
$dql = " FROM AcmeBundle:Users u LEFT JOIN AcmeBundle:Products p WITH u.id = p.id";
return $this->getEntityManager()->createQuery($noncountingSelect . $dql)
->setHint('knp_paginator.count', $this->getEntityManager()->createQuery($countingSelect . $dql)->getSingleScalarResult());
It works of course, but the solution seems quite ugly with larger selects.
How can I solve this problem?
I believe the Doctrine\ORM\Tools\Pagination\Paginator will do what you're looking for, without the additional complexity.
$paginator = new Paginator($dql);
$paginator
->getQuery()
->setFirstResult($pageSize * ($currentPage - 1)) // set the offset
->setMaxResults($pageSize); // set the limit
$totalItems = count($paginator);
$pagesCount = ceil($totalItems / $paginator->getMaxResults());
Code yanked from: http://showmethecode.es/php/doctrine2/doctrine2-paginator/
You can create a customer repository as explained in the docs and add your query to that with a minor edit like..
use Doctrine\ORM\EntityRepository;
class ProductRepository extends EntityRepository
{
public function findProducts()
{
return $this->findProductsOrCountProducts();
}
public function findCountProducts()
{
return $this->findProductsOrCountProducts(true);
}
private function findProductsOrCountProducts($count = false)
{
$queryBuilder = $this->createQueryBuilder('u');
if ($count) {
$queryBuilder->select('COUNT(u.id)');
}
$query = $queryBuilder
->leftJoin('AcmeBundle:Products', 'p', 'WITH', 'u.id = p.id')
->getQuery()
;
if ($count) {
return $query->getSingleScalarResult();
} else {
return $query->getResult();
}
}
}
Then you can call your method using...
$repository = $this->getDoctrine()
->getRepository('AcmeBundle:Users');
// for products
$products = $repository->findProducts();
// for count
$countProducts = $repository->findCountProducts();
Note:
I know it's not best practice to just say look at the docs for the customer repository bit s here' the YAML mapping...
# src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml
Acme\StoreBundle\Entity\Product:
type: entity
repositoryClass: Acme\StoreBundle\Entity\ProductRepository
# ...

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() ;
}

Categories