How to get partial result from doctrine query builder - php

I have a product entity in which it has an array as attributes:
/**
* #ORM\OneToMany(targetEntity="Shopious\MainBundle\Entity\ProductPicture", mappedBy="product", cascade={"persist","remove"})
*/
protected $pictures;
/**
* #Accessor(getter="getCover")
*/
private $cover;
public function getCover()
{
if($this->pictures->count() > 0) {
return $this->pictures[0];
}
return new ProductPicture();
}
Now in my query builder, I have the following code:
$query = $em->createQueryBuilder()->select('p')
->from("SiteMainBundle:Product", 'p')
->innerJoin('p.category', 'c')
->innerJoin('p.shop', 'shop')
;
The issue here is that I don't want to be selecting all of p's attribute. So I only wanted to get the first ProductPicture in the pictures array (in my case above it's similar to the getCover() method). How do I do this?
So far I can filter out the the partial attributes that I want by doing:
$query = $em->createQueryBuilder()->select('p.name, p.id')
->from("SiteMainBundle:Product", 'p')
->innerJoin('p.category', 'c')
->innerJoin('p.shop', 'shop')
->innerJoin('p.pictures', 'pictures')
;
so in the example above I have done inner joined on the pictures, but how do I get the first element from here?
In conclusion, my question is how do I select/query the first ProductPicture in the pictures array using the query builder? Because when I do:
$query = $em->createQueryBuilder()->select('p')
it returns the whole product attributes, but I don't want the whole product attributes.. I only wanted some of them, such as the id, name, etc. However one of the product attributes is actually an entity (which is the ProductPicture), so how do I return this in the select statement?
EDIT:
Here's a SQL equivalent on how the pictures should be inner joined:
SELECT *
FROM `product`
JOIN `product_picture` ON `product`.id = `product_picture`.product_id
WHERE `product`.id =100
LIMIT 1

Try something like this, if it's a one to many, the normal mySQL behaviour is returning several records with redundant product data, if the same case happens here, then only returning the first record should do the trick.
PS: assuming the ProductPicture entity has a url property that you want to get
$query = $em->createQueryBuilder()->select('p.id, p.name, pictures.url')
->from("SiteMainBundle:Product", 'p')
->innerJoin('p.category', 'c')
->innerJoin('p.shop', 'shop')
->innerJoin('p.pictures', 'pictures')
;

Add a custom repository method with the DQL for your entity, then call it from the controller
You can name the repository method whatever you want, for this example I'm using findProductWithPicture
class ProductRepository extends EntityRepository
{
/**
* #param integer $id
*/
public function findProductWithPicture($id)
{
$dql = <<<SQL
SELECT
p.id id,
p.name name,
q picture
FROM
Shopious\MainBundle\Entity\ProductPicture q,
Shopious\MainBundle\Entity\Product p
WHERE
p.id = :picture_id AND
q.product = p.id
SQL;
$query = $this->_em->createQuery($dql)->setParameter('picture_id', $id);
return $query->setMaxResults(1)->getResult();
}
}
To use this from the controller
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('ShopiousMainBundle:Product')->findProductWithPicture($id);
return $this->render('ShopiousMainBundle:Product:show.html.twig', array(
'product' => $product[0]
));
In the rendered Twig template, you can access them like so
<p>{{ product.id }}
<p>{{ product.name }}
<p>{{ product.picture.whatever_property }}

Related

Doctrine query with nullable/optional join

I got a method on my repository class to get me Article with their join (category, image ...) and not every Article have categories or image, what don't give me back all expected result, cause my current repository function return only Article who have Categories and Image not null and just ignore what having null value.
My Article entity have the following relationship.
/**
* #ORM\ManyToMany(targetEntity="App\ArticleBundle\Entity\Category", cascade={"persist"})
*/
private $categories;
/**
* #ORM\ManyToOne(targetEntity="App\ArticleBundle\Entity\Image", cascade={"persist"})
*
*/
private $image;
And this is my repository function
public function getArticle($id)
{
$qb = $this->createQueryBuilder('a')
->where('a.id = :theId')
->setParameter('theId', $id)
->join('a.author', 'auth')
->addSelect('auth')
->join('a.categories', 'cat')
->addSelect('cat')
->join('a.image', 'img')
->addSelect('img');
return $qb->getQuery()->getOneOrNullResult();
}
Now I want to know if I can get Article which have categories, image or not in one query with the join. I want to say when using the Doctrine lazy loading (by avoid the join in the query) I get the expected result.
Use ->leftJoin() to get Article which have categories, image or not in one query:
public function getArticle($id)
{
$qb = $this
->createQueryBuilder('a')
->addSelect('auth', 'cat', 'img')
->join('a.author', 'auth')
->leftJoin('a.categories', 'cat')
->leftJoin('a.image', 'img')
->where('a.id = :theId')
->setParameter('theId', $id)
;
return $qb->getQuery()->getOneOrNullResult();
}
Thus, avoids extra queries when Doctrine try to load the related properties in a lazy way.
Explanation:
Using ->join() or ->innerJoin():
This is the simplest, most understood Join and is the most common. This query will return all of the records in the left table (table A) that have a matching record in the right table (table B).
Using ->leftJoin():
This query will return all of the records in the left table (table A) regardless if any of those records have a match in the right table (table B). It will also return any matching records from the right table.
Source: Visual-Representation-of-SQL-Joins explained in detail by C.L. Moffatt

How to find all where query in Symfony 3 Doctrine

I have table named post and column string named status with data published or pending. Now I want to fetch all rows from this table with EntityRepository class custom method or just modify existing method.
I have tried this:
public function queryLatestPublished()
{
return $this->getEntityManager()
->createQuery('
SELECT p
FROM AppBundle:Post
WHERE p.staus == :st
ORDER BY p.created DESC
')
->setParameter('now', new \DateTime())
->setParameter('st', 'published')
;
}
but the query does not return anything.
There are some errors/problems in your code
number of parameters provided does not match number of expected
you don't call getResult (or any other execute like method) so you actually return Query object
I would change naming of method because it's misleading
also I would change
you probably want to limit somehow number of results
using select and from when working with querybuilder can be omitted if you are inside entity repository (in this case Post)
My proposition:
/**
* #param int $limit
* #param int $offset
*
* #return Post[]
*/
public function findNewestPublished($limit = 10, $offset = 0)
{
return $this->createQueryBuilder('p')
->where('p.status = :status')
->setParameter('status', 'published')
->addOrderBy('p.createdAt', 'desc')
->getQuery()
->setFirstResult($offset)
->setMaxResults($limit)
->getResult();
}
I hope this helps
try this
$em = $this->getDoctrine()->getManager();
$sql = "SELECT p FROM AppBundle:Post WHERE p.staus = ?1 ORDER BY p.created DESC'";
$query = $em->createQuery($sql);
$query->setParameter(1, 'published');
$result = $query->getResult();
for more info see http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#dql-select-examples

Symfony2/Doctrine2 Get data from two tables

I have an Alert Class with some data in it. I then have an Availability class. In my Availability class I have
/**
* #var \Nick\AlertBundle\Entity\Alert
*
* #ORM\ManyToOne(targetEntity="Nick\AlertBundle\Entity\Alert")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="availability_alert_id", referencedColumnName="id")
* })
*/
private $availabilityAlert;
So this is a foreign key back to my Alert class, linked to my Alerts class id.
I am now doing some work on the Availability data, so I have the DQL query
public function getAlertAvailability($id)
{
return $this->getEntityManager()
->createQuery(
'SELECT a.id, a.classLetter, a.flightNumber, a.alertPseudo, a.availability, a.lastUpdated
FROM NickAlertBundle:Availability a
WHERE a.availabilityAlert = :id
ORDER by a.classLetter, a.lastUpdated'
)
->setParameter('id', $id)
->getResult();
}
The way I call that is like this
public function getAvailabilityData(){
$alerts = $this->em->getRepository('NickAlertBundle:Alert')->getActiveAlertIds();
if (!$alerts) {
echo "No Availability";
}
foreach($alerts as $alert){
$alertId = (int)$alert['id'];
$allAvailability = $this->em->getRepository('NickAlertBundle:Availability')->getAlertAvailability($alertId);
}
}
So I essentially get all my active Alerts Ids, and then pass this to get my Availability for these individual Alerts.
Now I have a couple of problems.
Firstly, in the DQL query I make, I need to also get something from my Alert table (a field called command). How would I do a join in this query to get this piece of data?
Secondly, with the data that is returned, how do I access availabilityAlert in my Twig file?
UPDATE
Attempt at join
public function getAlertAvailability()
{
return $this->getEntityManager()
->createQuery(
'SELECT a.id, a.classLetter, a.flightNumber, a.alertPseudo, a.availability, a.lastUpdated, u.searchCommand
FROM NickAlertBundle:Availability a
JOIN a.availabilityAlert u
ORDER BY a.classLetter, a.lastUpdated'
)
->getResult();
}
Doctrine will load that entity as a proxy (for lazy loading) when the Availability entity is loaded.
You can access these via a normal getter / property access, but they will typically be lazy loaded by Doctrine. You can have them joined via a DQL query and the property will be hydrated with all the linked entities already loaded, see Improving Performance with a Join.
You can then access those associated entities in Twig as any other property.

Doctrine ManyToMany relation with join condition

I've got a Many-To-Many relation between two tables but I want to add a join condition on the join table.
/**
* #ManyToMany(targetEntity="Company", inversedBy="Accounts")
* #JoinTable(name="account_company",
* joinColumns = { #JoinColumn(name="account_id", referencedColumnName="id") },
* inverseJoinColumns = { #JoinColumn(name="company_id", referencedColumnName="id") }
* )
*/
protected $companies;
I would have a condition like "account_company.is_deleted = 0", how can I make it ?
Yes, you have the choice to hydrate your object and its ManyToMany collection using custom dql:
make a repository method that adds conditions on your join:
// repository class:
public function getAccountWithNonDeletedCompanies($id)
{
return $this->createQueyBuilder('account')
->leftJoin('account.companies', 'companies', 'WITH', 'companies.deleted = 0')
->andWhere('account.id = :id')
->setParameter('id', $account)
->getQuery()
->getOneOrNullResult()
;
}
From what I can understand, you never want to display deleted companies ? (soft delete).
In this case, you may want to use SQL Filters: http://blog.loftdigital.com/doctrine-filters-and-soft-delete
Have dealt with this before and the only solution I got was creating a custom function that will make the query.
So on your Entity create a custom function getCompanies().

Doctrine2 fetching rows that have manyToMany association by QueryBuilder

everyone.
I have 2 entities City and POI. Mapping looks like this:
class City {
/**
* #ORM\ManyToMany(targetEntity="POI", mappedBy="cities")
* #ORM\OrderBy({"position" = "ASC"})
*/
protected $pois;
and
class POI {
/**
* #ORM\ManyToMany(targetEntity="City", inversedBy="pois")
* #ORM\JoinTable(name="poi_cities")
*/
protected $cities;
I would like to fetch all POIs that have at least 1 association with some City using QueryBuilder. I should probably use exists() function but I don't quiet know how.
You'd have to Left join them and check if cities is null.
$qb->select('p', 'c')
->from('AcmeDemoBundle:POI', 'p')
->leftJoin('p.cities', 'c')
->where('c IS NOT NULL');
I haven't tested it, but I hope it gives you the general direction. You can read more about the QueryBuilder from here.
Docrine2 was changed in 2013, so the other solution displays error Error: Cannot add having condition on a non result variable. Now we cannot use joined alias just as a condition variable. We should use any of its properties like c.id
So you should fix the code to
$qb->select('p', 'c')
->from('AcmeDemoBundle:POI', 'p')
->leftJoin('p.cities', 'c')
->where('c.id IS NOT NULL');
$results = $qb->getQuery()->execute();
If you want to select entities that does not have any cities, use IS NULL.
$qb->leftJoin('p.cities', 'city')
->where('city.id IS NULL')
->getQuery()
->execute();
Description of a problem and link to the commit that responsible for that - http://www.doctrine-project.org/jira/browse/DDC-2780

Categories