I am trying to do a query where I have an entity Job and an entity JobProperty where 1 Job can have many JobProperties.
Query on a many-to-many relationship using Doctrine with Symfony2 explains how to retrieve matches on one entity based on values of it's subEntities. From this I have built the query:
$qb = $this->getDoctrine()->getRepository('AppBundle:Job')->createQueryBuilder('job')
->innerJoin('job.properties','property');
foreach($filters as $label => $value)
{
$qb->andWhere('property.label = :label AND property.value = :value')
->setParameter('label',$label)
->setParameter('value',$value);
}
The query above works to an extent, but it provides results where a property matches against ANY of the filters, and does not only provide results where ALL filters are matched. I need it to return only results where ALL filters are matched.
I might have to go about this in a different way, but I'm not sure I would implement.
You are not doing it correctly you are matching labels and values with the last filter values because the placeholders :label , :value used in query are not unique for each loop iteration, so all the clauses generated by loop will match with the last label and value.
To get the jobs whose each property match with the provided filters you can write somewhat like below doctrine query.
First it will collect all labels and values in separate array and then it will match with properties of job by using IN() operation, in last to get the jobs whose properties matches all the filters you need to build aggregation to count the matching results and should be equal to the count of filters
$qb = $this->getDoctrine()
->getRepository('AppBundle:Job')
->createQueryBuilder('job')
->innerJoin('job.properties','p');
$labels = array();
$values = array();
foreach($filters as $label => $value)
{
$labels[] = $label;
$values[] = $value;
}
$qb->addSelect('COUNT(DISTINCT p.id) AS total_properties')
->andWhere('p.label IN (:labels)')
->andWhere('p.value IN (:values)')
->addGroupBy('job.id')
->having('total_properties = '.count($filters))
->setParameter('labels',$labels)
->setParameter('values',$values)
->getQuery()
->getResult();
Here is another answer as a reference here which is similar to your question
Symfony2 - Doctrine2 QueryBuilder WHERE IN ManyToMany field
Related
I'm trying to figure out how to correctly iterate on an array in a symfony 5 repository custom query.
I explain myself. App user may check several values on a page. I want to display objects that matches all (but not necessarily only) the checked values.
I easily catch an array of the values that I send to a controller with ajax but I encounter issues with my custom query.
Values matches the "label" parameter of a table (Tag) which is related (ManyToMany) to the table (Flash) I select with my query.
So far, I tried a lot of things, like :
public function getFlashesByTagsList($tags)
{
$qb = $this->createQueryBuilder('f');
$andX = $qb->expr()->andX();
$qb->join('f.tags', 't');
$qb->addSelect('t');
foreach($tags as $tag) {
$andX->add(
$qb->expr()->like('t.label', ':tag'),
$qb->setParameter(':tag', '%'.$tag.'%')
);
}
$qb->add('where', $andX);
$qb->orderBy('f.id', 'DESC');
$result = $qb->getQuery()->getResult();
return $result;
}
But nothing I tried worked. If only one value is sent, I get all the objects that has this value in their tags but as soon as there are more, it only get me the ones that matches the last one in the $tags array.
I am using Laravel and I have two different collections that contain ID of products
First one is colorProduct and second is tagProduct
so I want to compare these two and get only same ID of products so how can I do this?
$colorProducts = Color::where('code', $request->color)->get()->first()->products;
$tagProducts = $tag->products->where('shop_id', $shop->id);
$colorAndTagProducts = collect();
foreach ($colorProducts->toBase()->merge($tagProducts)->unique('id')->groupBy('id') as $allProducts) {
if ($allProducts->count() >= 1) {
$colorAndTagProducts[] = $allProducts->first();
}
}
here
$colorAndTagProducts
gives me all records form both collection but I only want same record
I dont know, if I understand correctly, but maybe like this?
I suppose Color and Product are in many to many relationship. And Product and Shop/tag in one to many.
$colorId = Color::where('code', $request->color)->get()->first()->id;
$shopId = $shop->id;
$products = Product::whereHas('colors', function ($query) use ($colorId) {
$query->where('id', $colorId); //id or color_id
})->where('shop_id', $shopId)->get();
intersect()
The intersect method removes any values from the original collection
that are not present in the given array or collection. The resulting
collection will preserve the original collection's keys:
I did it with this method
I have related items in my database. I selected all of items from database by related id:
$next_stock = $this->model->get()->where('part_id', $in_data['part_id'])->all();
and I collection of rows grouped by one specific id, like on the picture. All of them selected by "part_id":
Selection Of Items
Grouped By Same Id
Also with this line of code i can select one of the items from this collection:
$next_stock = $this->model->get()->where('id', $old_stock['id'])->where('part_id', $in_data['part_id'])->first();
But how can I select the following items after this one?
Or, how can I select second or third item from this collect?
I cannot just increase id number by one from first, because sometimes this item ids not following each other.
Having a collection, you can take a specific element in the position with a combination of take() and last().
$collection = $this->model->get()->where('part_id', $in_data['part_id'])->all();
$second = $collection->take(2)->last(); //if this doesnt work, do it in 2 steps
$third = $collection->take(3)->last(); //if this doesnt work, do it in 2 steps
If you don't have a collection, take directly from database like this
$second = $this->model
->where('part_id', $in_data['part_id'])
->skip(1)
->first();
If it doesn't work with first()
$collect = $this->model
->where('part_id', $in_data['part_id'])
->skip(1)
->take(1)
->get();
$second = $collect->first();
Edit
skip() and take() are actually part of the query builder, not eloquent model. So it won't work with Eloquent in Laravel 5.4
Try with
$collect = $this->model
->where('part_id', $in_data['part_id'])
->get(1); //For the second record, 0 being the first
If you aren't doing it yet, you should set your model's relationships.
E.g. If you use "one-to-many", Eloquent will automatically determine the proper foreign key column on the model for you.
$parts = App\Stock::find(1)->partId;
foreach ($parts as $part) {
//
}
I have in my table "Artiste" one column "valideAdmin" who takes value 1 or 0.
I try to make a simple count to return the number of entries in my table where "valideAdmin" is to 1:
$repo = $this ->getDoctrine()
->getManager()
->getRepository('ProjectMainBundle:Artiste');
$qb = $repo->createQueryBuilder('valideAdmin');
$qb->select('COUNT(valideAdmin)');
$qb->where('valideAdmin=1');
$count = $qb->getQuery()->getSingleScalarResult();
return array(
'count' => $count
);
But it always "1" who's return...
Without where clause, I have the total count of the entries of the table, but valideAdmin can be 0 or 1. I only want the count number where valideAdmin=1
Thanks for help
createQueryBuilder()'s first parameter is the alias that you want your entity to take (ie.: a short name to be used to refer to your entity in the query).
What you need to do is set a proper alias for your entity (for example a for Artiste) and then COUNT() the instances of your entity where the property (not the column) valideAdmin is set to one:
$repo = $this ->getDoctrine()
->getManager()
->getRepository('ProjectMainBundle:Artiste');
$qb = $repo->createQueryBuilder('a');
$qb->select('COUNT(a)');
$qb->where('a.valideAdmin = :valideAdmin');
$qb->setParameter('valideAdmin', 1);
$count = $qb->getQuery()->getSingleScalarResult();
Remember that DQL runs queries on entities. The DQL your write is then translated into SQL to query the underlying data source after.
Also you can fetch all date then use of COUNT function in PHP
This method has an advantage.You do not have to use a complex query.
You have all the information with count columns
$repositoryArtiste = $this->getDoctrine()
->getRepository('ProjectMainBundle:Artiste');
$queryArtiste= $repositoryArtiste->createQueryBuilder('a')
->Where('a.valideAdmin = :valideAdmin')
->setParameter('valideAdmin',1)
->getQuery();
$Artiste = $queryArtiste->getResult();
var_dump(count($Artiste));
I am new to Doctrine and I am trying to figure out how to add a having clause on my statement. Basically I want to be able to filter down on items returned based on how many attributes the user selects. The code is as follows:
// create query builder
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('p')
->from($this->_entityName, 'p')
->leftJoin('p.options', 'o')
->where('p.active = :active')
->setParameter('active', 1);
// add filters
$qb->leftJoin('o.attributes', 'a');
$ands = array();
foreach ($value as $id => $values)
{ echo count($values);
$ands[] = $qb->expr()->andX(
$qb->expr()->eq('a.attribute_id', intval($id)),
$qb->expr()->in('a.attribute_value_id', array_map('intval', $values))
$qb->having('COUNT(*)=3) // THIS DOESN'T WORK
//$qb->expr()->having('COUNT(*)=3) // THIS DOESN'T WORK EITHER
);
}
$where = $qb->expr()->andX();
foreach ($ands as $and)
{
$where->add($and);
}
$qb->andWhere($where);
$result = $qb->getQuery()->getResult();
return $result;
When I try to execute the statement with the having() clause I get this error:
Expression of type 'Doctrine\ORM\QueryBuilder' not allowed in this context.
Without the having() clause everything works perfectly.
I have no idea how to solve this.
HAVING clause requires a GROUP BY. In doctrine it would be something like that:
$qb->groupBy('p.id'); // or use an appropriate field
$qb->having('COUNT(*) = :some_count');
$qb->setParameter('some_count', 3);
Assuming you're using mysql, here is a having clause tutorial: http://www.mysqltutorial.org/mysql-having.aspx
Perhaps you should bind number 3 to a parameter:
$qb->having('COUNT(*)=:some_count')
$qb->setParameter('some_count',3)
Goal: filter down The one side where we have some known summable conditions we want to filter by (e.g., the count of Original Parts in a Bundle) on the many side of a O2M relationship wehere want to limit the One side along with some other criteria to select on.
We are then adding in a few conditions for the LEFT_JOIN operation:
Condition #1 - the bundle.id == the original_part.bundle ID.
Condition #2 - The original_part's partStatusType == 3 (some hard-coded value).
Then we filter down to the COUNT(op) >= 1 which gives our more limited response that works just fine with pagination.
$qb->leftJoin(OriginalPart::class, 'op', Expr\Join::WITH,
$qb->expr()->andX(
$qb->expr()->eq('op.bundle', 'row.id'),
$qb->expr()->eq('op.partStatusType', 3)));
$qb->groupBy('row.id');
$qb->having($qb->expr()->gte('COUNT(op)', 1));
row is the alias for the One (bundle entity).