DQL for selecting a row in a x-to-x relationship - php

I have 3 different entities that are mapped to each other. To give a very simplified example.
ProductList:
class ProductList
{
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="productlist") */
protected $products;
}
Product:
class Product
{
/**
* #ORM\ManyToOne(targetEntity="Edition")
* #ORM\JoinColumn(name="edition_id", referencedColumnName="id")
*/
protected $edition;
/**
* #ORM\ManyToOne(targetEntity="ProductList")
* #ORM\JoinColumn(name="productlist_id", referencedColumnName="id")
*/
protected $productlist;
}
Edition
class Edition
{
protected $id;
}
How can I get all the productLists in my productListRepository that contain only 1 product with an specific edition id using DQL?
I know how to do it the 'lazy' way by retrieving all the items from the productList and check whether the related product entity contains an specific edition id but this seems highly ineffective if there is a large dataset to iterate through.
I know I have to use a join but I am stuck at the idea that I need to iterate through the products in productLists. ProductLists can contain more then one products but I only need the ones with only a single product and check wether they have an edition with the specific id.

Try this for extract all the productlist for a specified version that have only one edition:
SELECT pl
FROM ProductList pl
-- LEFT JOIN pl.products p
LEFT JOIN p.edition e
WHERE (sum (p1) from Product p1 where p1.productlist = pl.id) = 1
Hope this help

Related

Laravel Eloquent - get count of another table

I have an eloquent model "Athlete" and there is another table performances. Each athlete has 0 to many performances. And I would like get best performance of each athlete(personal best) or null if the athlete doesnt have any performances yet.
My athlete model:
class Athlete extends Model
{
// I would like to do something like
public $personalBest = max(performances) - the highest perfomance
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'athletes';
/**
* The primary key associated with the table.
*
* #var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* #var bool
*/
public $timestamps = false;
/**
* Get the performances for the Athelete post.
*
* #return HasMany
*/
public function performances()
{
return $this->hasMany('App\EloquentModels\Performance', 'athlete_id', "id");
}
}
I would like to get the highest performance of each athlete. Hope it does make sense.
I think it has to be answered somewhere but I had no luck finding it. So sorry if I just failed to find it.
Performances table
id(int) year(int) performance(float)
-------------------------------------
1 2000 257.3
2 2001 227.3
Just to wrap things up. Posting the final raw query which was generated:
select [athletes].[first_name], [athletes].[last_name], MAX(performance) AS personal_best
from [athletes]
left join [performances] on [athletes].[id] = [performances].[athlete_id]
group by [athletes].[id], [athletes].[first_name], [athletes].[last_name]
order by [personal_best] desc
Using withCount should do the job
$athletes= App\Athlete::withCount('performances')->get();
foreach ($athletes as $athlete) {
echo $athlete->performances_count;
}
If you want max performance, you can do something like
$athletes= App\Athlete::all();
foreach ($athletes as $athlete) {
echo $athlete->performances->pluck('performance')->max();
}
Something like
select e.athelete.id, max(perf.performace) as max_performace
from atheletes ath
left join performaces perf on ath.id = perf.athelete_id
group by ath.id, max_performace
may be something like
DB('athletes as ath')::leftJoin('performaces as perf', 'ath.id', 'perf.athelete_id')->select(e.athelete.id, max(perf.performace) as max_performace)->orderBy('max_performace');
You can use order by max_performace, if you need.
I think you can also use simply
echo $athlete->performances->max('performance');

Doctrine ORM: filter articles by 2 categories

In our online shop we are selling spare parts for industrial computer systems. Therefore we have 2 main categories: spare parts and computer systems. Users can either search their needed spare parts via the spare parts category or via the computer system. If a user selected his computer system on the homepage, he should get a list of spare parts categories matching with his computer system.
Therefore i need to query all spare parts categories and filter them by all articles, that are also in the category of the preselected computer system. Otherwise the user could see an empty category.
I have 2 doctrine entites: article and category - each of them related many to many to the other:
Category Entity
/**
* #ORM\Table(name="categories")
* #ORM\Entity(repositoryClass="Repository")
*/
class Category extends ModelEntity
{
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Models\Article\Article")
* #ORM\JoinTable(name="articles_categories",
* joinColumns={
* #ORM\JoinColumn(name="categoryID", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="articleID", referencedColumnName="id")
* }
* )
*/
private $articles;
Article Entity:
/**
* #ORM\Entity(repositoryClass="Repository")
* #ORM\Table(name="articles")
*/
class Article extends ModelEntity
{
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Models\Category\Category")
* #ORM\JoinTable(name="articles_categories",
* joinColumns={
* #ORM\JoinColumn(name="articleID", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="categoryID", referencedColumnName="id")
* }
* )
*/
protected $categories;
I'm trying to query all categories with articles in 2 categories. For example: get all categories, that has articles in "this" category, but only where same articles are also in "that" category.
Unfortunately i have no idea, how to do this. Can anyone help me out?
To find categories who belongs to given list of articles (each category must has an association with each article from given list) then you can make use of some aggregation
$articleIds = [1,2,3,4,5];
$qb = $this->createQueryBuilder('c');
$qb->addSelect('COUNT(DISTINCT a.id) AS HIDDEN total_articles')
->innerJoin('c.articles', 'a')
->add('where', $qb->expr()->in('a', $articleIds))
->groupBy('c.id')
->having('total_articles = '.count($articleIds))
->getQuery()
->getResult();
Symfony2 - Doctrine2 QueryBuilder WHERE IN ManyToMany field
Doctrine QueryBuilder: ManyToOne Relationship where more than one subEntity must match
Sql/Doctrine query to find data with multiple condition with Many to Many associations
Convert SQL with subquery to Doctrine Query Builder
Thanks to M Khalid Junaid i finally figured it out.
This is my final query:
// select all active root categories from the online shop
$builder = Shopware()->Models()->createQueryBuilder();
$builder->from('Models\Category\Category', 'c')
->select('c as category')
->andWhere('c.active = 1')
->andWhere('c.parentId = 0');
// if the user already selected his computer system,
// only get articles, that are in both categories: the
// category of this particular computer system and the
// category of spare parts
if ($this->computerSystemCategoryId) {
$builder->addSelect('COUNT(DISTINCT a2.id) AS total_system_articles')
->leftJoin('c.articles', 'a2')
->leftJoin('a2.categories', 'c2')
->groupBy('c.id')
->having('total_system_articles > 0')
->andWhere($builder->expr()->in('c2', [$this->computerSystemCategoryId]));
}
$query = $builder->getQuery();
$categories = $query->getResult();
With this query i can get only the spare parts, that are associated with the particular spare parts category, but also with the particular computer system category id.

Symfony subquery for entity

Have a problem with subquery with symfony.
What I try to do - I have a table with users and a table with posts.
Posts Users
id|author|content id|username
I want create subquery to get user name by id.
/**
* #return array
*/
public function findAll()
{
return $this->getEntityManager()->createQuery(
'SELECT a, (SELECT u.username
FROM BackendBundle:User u WHERE u.id = a.author) as authorName
FROM BackendBundle:Article a'
)->getResult();
}
Result:
What am I doing wrong? What is the best way to join column from other table by id? Maybe i can use annotations?
Thx for any help.
You don't need a subquery here, what you need is a simple (INNER) JOIN to join Users with their Articles.
$em->createQuery("SELECT a FROM Article JOIN a.author'");
You don't even need an on clause in your join, because Doctrine should already know (through annotations on your entities or a separate yaml file), that the article.author field relates to user.id.
Edit:
I assume you have a User entity that is One-To-Many related to the Article entity.
class User
{
// ...
/**
* #OneToMany(targetEntity="Article", mappedBy="author")
*/
private $articles;
// ...
}
class Article
{
// ...
/**
* #var User
* #ManyToOne(targetEntity="User", inversedBy="articles")
*/
private $author;
// ...
}
Please refer to doctrines association mapping documentation: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html

Fetch entity without related objects doctrine

I have the following classes:
class Category {
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
private $products;
...
}
class Product {
...
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
...
}
when i try to fetch one product from my db like this:
$query = $doctrineManager->createQuery(
"
SELECT p FROM AppBundle:Product p
WHERE p.id = :id
"
)->setParameter('id', $id);
$result = $query->getSingleResult();
i get not only my product, but also get category with all products (except the one i found). So, how can i fetch only model what i want without any related model?
They are just stubs, you don't actually fetch any related entity information unless you are using fetch=EAGER.
This answer explains it pretty well.
What is the difference between fetch="EAGER" and fetch="LAZY" in doctrine
In summary, you can't get rid of the associations, but they don't load the other entities until you call the data unless you specifically request otherwise. So don't worry about it.

Jms serializer #JMS\Inline() annotation overrides an actual id

I have product and product_detail tables in database.
Every product has a product detail.
class Product
{
/**
* #var ProductDetail
* #JMS\ReadOnly()
* #ORM\OneToOne(targetEntity="ApiBundle\Entity\ProductDetail", mappedBy="product", cascade={"persist"})
* #JMS\Inline()
*/
private $detail;
}
I use #JMS\Inline() annotation to show only detail information of product_detail table.
But when I serialize a product I get wrong id. It must be product id but id returns an product detail id. Is it bug or I am doing something wrong?
You should not expose the id from the detail when using inline.
Source: https://github.com/schmittjoh/JMSSerializerBundle/issues/460#issuecomment-113440743

Categories