How to get an entity and a count in the same query? - php

I've created the following repository and method. The method does what you would expect it to do except for one thing. It cannot return both ->select('ol') and ->select('count(ol.product) as totalProducts'). As soon as I inclode the ->select('ol') it will ignore the count.
class OrderLineRepository
{
// ...
public function getOpenOrders()
{
$qb = $this->createQueryBuilder('ol');
$orders = $qb
->select('count(ol.product) as totalProducts')
->select('ol')
->where('BIT_AND(ol.flags, 3) = 2')
->groupBy('ol.orderId')
->setMaxResults(100)
->getQuery()
->getResult()
;
return $orders;
}
// ...
}
I am still in the early stages of mastering symfony and this could possibly quite a stupid question. But it's still a question I'm currently facing. Can someone help me out?
Update
Witht the help of RiggsFolly I now get the following result by using ->addSelect(...) instead of ->select(...).
array:10 [
// ...
array:2 [
0 => OrderLine {
id: 8068005
product: Product {#1503 ▶}
supplier: Supplier {#1552 ▶}
reference: Reference {#1528 ▶}
}
"products" => "3"
]
// ...
]
Ideally, I would like to get it like this:
array:10 [
// ...
array:2 [
id: 8068005
product: Product {#1503 ▶}
supplier: Supplier {#1552 ▶}
reference: Reference {#1528 ▶}
"products" => "3"
]
// ...
]

I don't know how it's related OrderLine and Products if it is; but maybe you can create a DB relation between it, so you would get an array of products related to your OrderLine.
Finally, to get the number of products:
$productsNumber = count($orderLine->getProducts());
UPDATE
In this case, total Products will be the same as total OrderLines, is that correct?
So... total products is not an OrderLine attribute, in my opinion; and it should not be part of your OrderLine object, instead, you probably can count the OrderLines objects retrieved by Doctrine, and it will be your totalProduct.
Looking a little further, you can also create an Order entity, that is related to your OrderLine object (One to Many). Then, you could query for your Order entity using Doctrine, and count the OrderLines attribute of your Order entity, which will be the same as the total products.

Related

Laravel Getting id's from a table and inserting them into another table

Trying to get matching id's from a table and inserting them again in the same table under differnet relationship.
$contentPack = ContentPack::find($id);
$cloned_pack_goals = DB::table('content_pack_goal')->where('content_pack_id' , $contentPack->id)->get();
$cloned_pack_goal_ids = $cloned_pack_goals->goal_id;
Produces Exception
Exception
Property [goal_id] does not exist on this collection instance.
dd($cloned_pack_goals); outputs:
Illuminate\Support\Collection {#2466 ▼
#items: array:2 [▼
0 => {#3129 ▼
+"goal_id": 4
+"content_pack_id": 2
}
1 => {#2467 ▼
+"goal_id": 9
+"content_pack_id": 2
}
]
}
How to get goal_ids from the output to insert them into the same table again but with a different relation?
$newPack = $contentPack->replicate();
DB::table('content_pack_goal')->insert(['content_pack_id' => $newPack->id,'goal_id' => $cloned_pack_goal_ids]);
Am doing something wrong when getting the ID's and when inserting them. tried using ->first(); it works but only one id gets inserted
$cloned_pack_goals is a collection, so you need to exclude goal_ids from all collection records separately.
This snippet may help you:
$cloned_pack_goal_ids = DB::table('content_pack_goal')->where('content_pack_id' , $contentPack->id)->pluck('goal_id')->toArray();
foreach($cloned_pack_goal_ids as $key => $goal_id) {
DB::table('content_pack_goal')->insert(['content_pack_id' => $newPack->id,'goal_id' => $goal_id]);
}
To get an array of only the Ids, use pluck() and toArray()
$cloned_pack_goal_ids = DB::table('content_pack_goal')
->where('content_pack_id' , $contentPack->id)
->pluck('goal_id') // returns a collection of only Ids.
->toArray(); // returns an array from the collection.
Write your query in this format this will give you the require output:
$cloned_pack_goals = DB::table('content_pack_goal')->where('content_pack_id' , $contentPack->id)->get()->toArray();
$cloned_pack_goal_ids = $cloned_pack_goals[0]->goal_id;

How to get all users with all their roles in a many to many relation based sql query?

Goal
Get all users with all their roles.
Problem
If one user has more than one role, i get an instance of this user for each role he has.
Description
I have a setup with three tables, like it is described in this stackoverflow post for example.
A user table, a role table and a role_user table.
Now i want to query all users with their roles with this query:
$allUsers = DB::table('users')
->join('role_user', 'role_user.user_id', '=', 'users' . '.id')
->join('roles', 'roles.id', '=', 'role_user.role_id')
->select(['users.uuid', 'users.username', 'users.last_online_at', 'roles.role', 'roles.id as role_id'])
->get();
To me it looks exactly like the plain MYSQL query proposal which made Lizesh Shakya in the second post of the stackoverflow post i already mentioned above.
Except for the fact that i use Laravel. And i know, that i could solve it with the user model with the appropriate belongsToManymethods are set in it and the roles model. But i want to understand it a bit deeper and therefore i try it that way.
If i have three users and if one user has the roles "editor" and "author" for example, the query returns four users. For each role one.
Illuminate\Support\Collection {#1267 ▼
#items: array:4 [▼
0 => {#1273 ▼
+"uuid": "fc8bbd29-9005-4f05-9706-a437933d3bc2"
+"username": "admin"
+"last_online_at": "2021-01-16 19:01:37"
+"role": "admin"
+"role_id": 1
}
1 => {#1272 ▼
+"uuid": "d957e5af-f8f3-4a07-a2c3-eabecbdda618"
+"username": "author1"
+"last_online_at": "2021-01-16 19:01:37"
+"role": "author"
+"role_id": 3
}
2 => {#1282 ▼
+"uuid": "dc94d900-bbaf-4605-b8d8-6bab5fb43b70"
+"username": "editor"
+"last_online_at": "2021-01-16 19:01:37"
+"role": "editor"
+"role_id": 2
}
3 => {#1281 ▼
+"uuid": "d957e5af-f8f3-4a07-a2c3-eabecbdda618"
+"username": "author1"
+"last_online_at": "2021-01-16 19:01:37"
+"role": "editor"
+"role_id": 2
}
]
}
So where is my fault? :)
So if I understand correctly yo want a list of users with their roles right?
On your UserController:
public function getUsersWithRoles(){
$all_users = User::all()->load('roles');
$body = compact('all_users');
return response()->json(compact('body'));
}
OR
public function getUsersWithRoles(){
$all_users = User::with('roles')->get();
$body = compact('all_users');
return response()->json(compact('body'));
}
Don't forget to declare your relations in the models.

Doctrine2 query orderBy with specific values first

I am trying to apply several filters to one dql in Symfony using Doctrine. I want order it by several columns (although for the moment I am having problems just with one column) and I want show first values that matches with specific values. I will write a simple example to illustrate it about the result that I am searching:
col1 col1
---- -----
A B
B => A
C C
W W
I was searching information about how to make it but I am a bit confused due to that some people says that I can't do it directly, other people says that it is possible using case when or if/else. I tried to use case when but without success. The code that I am using is the following:
Updated code and error
$query = $this->createQueryBuilder('article')
->where('article.title LIKE :article')
->setParameter('title', '%'.$term.'%')
->addSelect('(CASE WHEN article.to = \'WOR\' THEN 1 ELSE 0 END) AS to')
->addOrderBy('article.to', 'ASC')
->getQuery();
And if I want set the "B" value as parameter, should I use setParameter after the addSelect?
Right now with the abovecode I am getting the following error:
Key "title" for array with keys "0, to" does not exist in result.html.twig at line 177.
Information related about how I call this method into my controller and pass it to twig template:
$prodMan = $this->get('app.article.manager');
$articles = $prodMan->getResults((string)"t", $page);
$limit = 50;
$maxPages = ceil($articles->count() / $limit);
$thisPage = $page;
return $this->render('result.html.twig', compact('categories', 'maxPages', 'thisPage', 'articles', 'images'));
And the twig template where I have the error:
<td><p>{{articles.title}}></p></td>
Result of {{ dump(articles) }}
Paginator {#475 ▼
-query: Query {#484 ▼
-_state: 2
-_dql: "SELECT article, (CASE WHEN article.to = 'WOR' THEN 1 ELSE 0 END) AS to FROM AppBundle\Entity\Article article WHERE article.title LIKE :title ORDER BY article.to ASC"
-_parserResult: null
-_firstResult: 0
-_maxResults: 50
-_queryCache: null
-_expireQueryCache: false
-_queryCacheTTL: null
-_useQueryCache: true
#parameters: ArrayCollection {#483 ▼
-elements: array:1 [▼
0 => Parameter {#480 ▼
-name: "title"
-value: "%t%"
-type: 2
}
]
}
#_resultSetMapping: null
#_em: EntityManager {#105 …10}
#_hints: []
#_hydrationMode: 1
#_queryCacheProfile: null
#_expireResultCache: false
#_hydrationCacheProfile: null
}
-fetchJoinCollection: true
-useOutputWalkers: null
-count: 143
}
I executed the same dql query without the case when (with that dql I haven't any problem), and I compare the dumps in Twig and the only difference that I see is that in the other dql I am getting #483 and #482 indexes instead of #484 and #480 respectly
And I can't var_dump any position of the array, but the array has the right number of results, although I can't check if results are sorted in the right way
I am stuck with this problem and if someone could lead me to the right answer I'd be very grateful!
I'm not sure if your query will work, but what if you change this line like so:
->addSelect('(CASE WHEN article.to = \'B\' THEN 1 ELSE 0 END) AS HIDDEN to')
Escaping the quote, or:
->addSelect("(CASE WHEN article.to = 'B' THEN 1 ELSE 0 END) AS HIDDEN to")
That way the B is in quote. Again, not sure about the query itself.
EDIT #2 - Based on dump of articles
Looks like the dump is still a query.
You need to get the results like so:
$articles = $query->getResult();
Then pass the articles to you twig and render it.
Normally done like so:
return $this->render('result.html.twig', array(
'articles' => $articles,
));
See if that works.
You might need changes, but the above code gives you some idea of what to do.

Laravel 5 route binding variables not accessible in query builder

I'm using route binding to determine if each part of the URL is related to the previous. I'm trying to access the route parameter/variable ($stage_number) in my query builder but no luck. To confuse things, if I substitute the variable with a hard value it works, e.g. 4
How do I use the $stage_number variable in my query?
/*
* Route
*/
Route::get('/application/{application_id}/stage/{stage_number}', [
'as' => 'application.stage',
'uses' => 'ApplicationController#stage'
]);
/*
* Route Service Provider
*/
// Application
$router->bind('application_id', function($application_id)
{
$application = Application::find($application_id);
if (! $application)
{
abort(404);
}
return $application;
});
// Stage
$router->bind('stage_number', function($stage_number)
{
$application = $this->getCurrentRoute()->application_id;
$stage = collect($application->stages)->where('order', $stage_number)->all();
if (! $stage)
{
abort(404);
}
return $stage;
});
Update in response to patricus:
Thanks for the information about calling where() on collections; I did not realise it handled differently to the query builder. Updating my where() for the collection works perfectly - thanks. However, I am still having trouble when using the query builder:
// Route with data
/application/1/stage/4
// Actual data returned
array:4 [▼
0 => array:2 [▼
"name" => "Information about the University or Education Course"
"order" => 3
]
1 => array:2 [▼
"name" => "Information about your education to date"
"order" => 4
]
2 => array:2 [▼
"name" => "Other Information"
"order" => 5
]
3 => array:2 [▼
"name" => "Declaration"
"order" => 6
]
]
// Desired data to be returned
array:1 [▼
0 => array:2 [▼
"name" => "Information about your education to date"
"order" => 4
]
]
Regardless of what order I specify in my route I seem to get everything returned that is not null unless I choose 1 or 2 (which are rows with no order number and excluded from the array example above) and then I get that row returned with all of the other rows that is not null (as shown in the example above) but any other null rows are excluded. Is this a problem caused by the relationship on my Application object?
public function stages()
{
return $this->hasMany('App\Entities\Application\Stage', 'type_id')->orWhereNull('type_id')->orderBy('order', 'asc');
}
Update:
Setting the foreign_key and local_key on my relationship seemed to resolve the other issues:
public function stages()
{
return $this->hasMany('App\Entities\Application\Stage', 'type_id', 'type_id')->orWhereNull('type_id')->orderBy('order', 'asc');
}
You're actually calling the where() method on a Collection object, not on a Builder object.
The where() method on the Collection works a little differently. First, it can only do an equals comparison. Second, by default, it does a strict equals (===) comparison, and this is your issue. Since your $stage_number is a string, and your order field is most likely an integer, the strict comparison doesn't return any results.
You can change the comparison to a loose equals (==) by passing false as the third parameter. You can also use the whereLoose() method, which does the same thing.
Also note, assuming that stages is a hasMany or belongsToMany relationship on the Application object, there is no need for the call to collect(). $application->stages will already return a Collection.
// pass false as third parameter
$stage = $application->stages->where('order', $stage_number, false)->all();
// or use whereLoose
$stage = $application->stages->whereLoose('order', $stage_number)->all();
Now, having said all that, you probably want to be calling where() on the query builder. While $application->stages will give a Collection of related stage objects, $application->stages() returns the Relation object for the relationship, which gives you access to the query builder. Since you have access to the builder, you can add your where clause to the query being run and will provide a performance boost (and also doesn't care about variable type).
$stage = $application->stages()->where('order', $stage_number)->get();
Note that get() will return a Collection object that contains the matching stage objects. If order is unique and you want to get that one stage object, you can use first() instead of get():
$stage = $application->stages()->where('order', $stage_number)->first();

Select an unpersisted entity

I'm using a twig for loop to display a list of elements. These elements come from a decoded json array, from an API.
I have a OneToMany relation between my user and these elements.
User needs to chose one of these elements, which will be added to the user with the addElement() function.
I tried to do so using a Symfony2 form in the loop, but it is only displayed on the first element. I also tried using a link to a controller function, but since none of these elements are persisted in my DB, I got this error:
"Unable to guess how to get a Doctrine instance from the request information."
Here's how I display my elements:
{% block itinerary %}
{% for element in elements %}
<aside class="flights-results__by-price col-md-3">
<span class="flights-results__price">{{ element.price ? element.price : 'Unknown' }}</span>
Delete
</aside>
{% endfor %}
{% endblock itinerary %}
Here is the function where I create and fill my elements :
public function getAvailabilities($availabilities, $planes, $airports)
{
$reservations = array();
foreach ($availabilities as $ar)
{
$leg = new Leg();
$leg->getId();
foreach($ar as $a)
{
$leg = $this->fillLeg($leg, $a);
foreach($a->availabilities as $aleg)
{
$leg->setAirplaneType($this->findPlane($planes, $aleg->airplane_type_id));
$leg->setAirportStart($this->findAirport($airports, $a->lfi_from));
$leg->setAirportEnd($this->findAirport($airports, $a->lfi_to));
$leg->setDurationLeg($aleg->duration);
$leg->setEndHour($aleg->datetime_to);
}
$startdate = $a->datetime;
}
$reservations[] = $leg;
}
return $reservations;
}
and here is the result when I dump($elements) :
FlightController.php on line 55:
array:4 [▼
0 => {#953 ▼
+"3e1f975601f59090decc8f2d5ced72010162e48e": {#954 ▼
+"lfi_from": "FR58957"
+"lfi_to": "FR45300"
+"datetime": "2015-09-10 20:00:00"
+"nb_pax": "4"
+"availabilities": array:1 [▼
0 => {#955 ▶}
]
}
}
1 => {#956 ▼
+"3e1f975601f59090decc8f2d5ced72010162e48e": {#957 ▼
+"lfi_from": "FR45300"
+"lfi_to": "AG00060"
+"datetime": "2015-09-10 23:00:00"
+"nb_pax": "4"
+"availabilities": array:1 [▼
0 => {#958 ▶}
]
}
}
2 => {#959 ▼
+"3e1f975601f59090decc8f2d5ced72010162e48e": {#960 ▼
+"lfi_from": "FR45300"
+"lfi_to": "AG00060"
+"datetime": "2015-11-30 23:00:00"
+"nb_pax": "4"
+"availabilities": array:1 [▼
0 => {#961 ▶}
]
}
}
3 => {#962 ▼
+"3e1f975601f59090decc8f2d5ced72010162e48e": {#963 ▼
+"lfi_from": "FR45300"
+"lfi_to": "OLOLOL"
+"datetime": "2015-09-18 23:00:00"
+"nb_pax": "2"
+"availabilities": array:1 [▼
0 => {#964 ▶}
]
}
}
]
The main problem is that the API returns several thousands results. For obvious reasons, I cannot persist them all.
I guess the easiest way to ask would be "What is the best way to send datas on an entity to another function in my controller, without persisting this entity?". So far, I've always worked with persisted elements, with an id as identifier, but I realize it gets trickier when we deal with non-persisted entities.
If you have a OneToMany relation between your User and these Elements, it means the Elements are persisted somehow. So why can't you use the id of the element ?
In case you persist it, you may need to add a ParamConverter in your controller code somewhere along those lines:
/**
* #Route("/selectLeg/{element}")
* #ParamConverter("element", class="YourBundle:Element", options={"mapping": {"name": "element.whatever_param"}})
* #Template()
*/
public function selectLegAction(Element $element)
The fact is, if Symfony2 doesn't know about your Element entity, you won't be able to do addElement() to your User.
What I guess is that you get the elements' list in the frontend and then try to update your User object. In this case I would json_encode your element in Twig (it's a simple array after all if I understand) :
Select this leg
and create a new Element in your controller :
/**
* #Route("/selectLeg/{legAsJSONString}")
*/
public function selectLegAction($legAsJSONString) {
$e = json_decode($legAsJSONString);
$leg = new Leg();
$leg->setWhateverParameter($e->parameter_in_the_array);
// more parameters here
$em->persist($leg)->flush();
/// Now here you have $leg->getId(); if ever you need it
}
EDIT : Adapted to your comment. If you don't need to persist the element (leg) before the user has chosen a particular one, then send the element in string form in a GET parameter, in the route parameters, or in the data of a POST request (cleaner solution). You don't need the id since you can pass the full object in the request, as a JSON string.
Remove your useless $leg->getId(); from getAvailabilities() as well, it doesn't actually do anything, and the id doesn't exist anyway.
If I miss the point and that the $leg object is too complicated and therefore not serializable in JSON, then you will need to persist it since two consequent requests will need to have access to it.

Categories