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.
Related
I'm creating a function that should return an array of User ORM object. The function should run a query to the DB and return the users where the users' contact persons has 1 company (not more or less). The relationship is like this: every user has one or more contact person and every contact person has one or more companies.
The SQL to locate these users are like this. We are using PHP 7.1, Symfony 3.4 and Doctrine 2.7.
The problem that I have is that I cannot manage to describe this in Doctrine QueryBuilder syntax so that an array of User ORM objects are returned. Can anybody give me some advice?
SELECT users.email
FROM company
INNER JOIN contact_person ON contact_person.id = company.belongs_to_contact_person_id
INNER JOIN users ON users.id = contact_person.belongs_to_user_id
GROUP BY users.email
HAVING COUNT(company.id) = 1
Depending on how your mapping is on your entities, you have multiple solution.
It would be nice if you can show us what you tried so we can see what you miss.
The best is to use the repository of the entity you whish to have an array of:
namespace App\Repository;
use App\Entity\User;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
{
/**
* #return User[]
*/
public function findUsersHavingAtLeastOneCompany():array
{
return $this->createQueryBuilder('user')
->join('user.contact', 'contact')
->join('contact.company', 'company')
->where('contact.company = 1')
->getQuery()
->getResult();
}
}
When using the createQueryBuilder function, it will auto populate the select and the from.
The getResult will return an array of entity (if you have not defined a select)
I fixed this by using the following code using createNativeQuery. It can probably be done by using fever lines of code, but it does the job for me :)
$em = $this->getEntityManager();
$sql = <<<SQL
SELECT users.id
FROM company
INNER JOIN contact_person ON contact_person.id = company.belongs_to_contact_person_id
INNER JOIN users ON users.id = contact_person.belongs_to_user_id
GROUP BY users.id
HAVING COUNT(company.id) = 1
SQL;
$rsm = new ResultSetMapping();
$rsm->addScalarResult('id', 'text');
$query = $em->createNativeQuery($sql, $rsm);
$locatedUsers = [];
foreach ($query->getResult() as $lUser) {
foreach ($lUser as $user) {
$locatedUser = $em->find("Project\User\User", $user);
array_push($locatedUsers, $locatedUser);
}
}
return $locatedUsers;
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
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.
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
# ...
So here is my query:
public function fetchAd($adID){
$row = $this->tableGateway->select(function(Select $select) use ($adID){
$select->join('adDetails','adDetails.adID = ads.adID',array('*'),'inner');
$select->where(array('ads.adID' => $adID));
});
return $row->current();
}
So what I'm doing I'm querying the ad table and join the adDetails table in order for me to get the details for a certain AD, the problem is that the entity AD which belongs to the model that I'm doing the query, it doesn't have the columns names(variables) from the adDetails table; so it will only return the columns from the AD table, because the entity doesn't have those fields in the exchangeArray()
I have tried to extend the AD entity to use the AdDetails entity but it returns now to object array but with the fields as null, because it can;t populate them.
So, how should I do this, in order for me to have all the columns available in the model for the tables that will join?
I'm planning to join other tables as well.
Ok I solved the problem, the thing is that it will return an array, and it won't use the ORM style, but it does the job, for now relations are not supported in ZF2 like in ZF1;
use Zend\Db\Sql\Sql,
Zend\Db\Sql\Where;
$sql = new Sql($this->tableGateway->getAdapter());
$select = $sql->select();
$select->from($this->tableGateway->table)
->join('adDetails','adDetails.adID = ads.adID',array('*'),'inner');
$where = new Where();
$where->equalTo('ads.adID', $adID) ;
$select->where($where);
$statement = $sql->prepareStatementForSqlObject($select);
$result = $statement->execute();
return $result->current();
public function fetchJoin()
{
$select = new \Zend\Db\Sql\Select;
$select->from('tablea a');
$select->columns(array('*'));
$select->join('tableb b', "b.id = a.b_id", array('field1'), 'left');
// to display query string remove comment in next line
//echo $select->getSqlString();
$resultSet = $this->tableGateway->selectWith($select);
return $resultSet;
}