Laravel 5 route binding variables not accessible in query builder - php

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();

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;

can we avoid eloquent in large no of records

I am using laravel eloquent and have condition where thousands of records inside database so when using eloquent relationship my query is executing slow. Should i avoid using eloquent in these satuations or any other way?
here is mysql query
$leads=Lead::select($col)
->join("gc_od_leads_detail as ld", "gc_od_leads.leads_id", "=", "ld.ld_leads_id")
->join("gc_od_chat as c", "gc_od_leads.leads_chat_id", "=", "c.chat_id")
->join("gc_od_group as g", "c.chat_group_id", "=", "g.group_octachat_id")
->where('c.chat_tags','sales')
->whereIn('c.chat_group_id',$filter['groups']);
if(!empty($filter['keyword'])) {
$leads=$leads->where(function ($q) use ($filter) {
$q->where('ld_name','like', "%".$filter['keyword']."%")
->orWhere('ld_email','like', "%".$filter['keyword']."%")
->orWhere('ld_phoneno','like', "%".$filter['keyword']."%");
});
}
if(!empty($filter['startDate']) && !empty($filter['endDate'])){
$leads=$leads->whereBetween('leads_created_date', [$filter['startDate']." 00:00:00",$filter['endDate']." 23:59:59"]);
}
$leads=$leads->orderBy('leads_created_date','desc');
return $leads;
}
I have more than 500 000 recordings in side messages and chats table. i changed query in eloquent and debugged it
Query:
Lead::select('leads_id','leads_chat_id')->with(["detail"=>function($q){
$q->select("ld_leads_id");
}])->with(["chat"=>function($q){
$q->select("chat_id")->where(['chat_status'=>1]);
}])->where("leads_status",1)->get();
Debuging Ouput
array:3 [▼
0 => array:3 [▼
"query" => "select `leads_id`, `leads_chat_id` from `gc_od_leads` where `leads_status` = ?"
"bindings" => array:1 [▼
0 => 1
]
"time" => 14.85
]
1 => array:3 [▼
"query" => "select `ld_leads_id` from `gc_od_leads_detail` where `gc_od_leads_detail`.`ld_leads_id` in (2278918, 2278919, 2278920, 2278921, 2278922, 2278923, 2278924, 22789 ▶"
"bindings" => []
"time" => 0.59
]
2 => array:3 [▼
"query" => "select `chat_id` from `gc_od_chat` where `gc_od_chat`.`chat_id` in (3496457, 3496458, 3496459, 3496460, 3496461, 3496462, 3496463, 3496464, 3496465, 3496466, 34 ▶"
"bindings" => array:1 [▶]
"time" => 4.21
]
]
i above output you can see that it get all records of leads first then going to lead detail and chats table if i only want to find out the leads having chat status =1 it will still query all leads this is what slowing my query
where we use join it will not work in this way i think which will save time and space both that's my i post this question i think a lot of people having same problem and no one discuss this point
Let's take a look at one part of this.
if(!empty($filter['keyword'])) {
$leads=$leads->where(function ($q) use ($filter) {
$q->where('ld_name','like', "%".$filter['keyword']."%")
->orWhere('ld_email','like', "%".$filter['keyword']."%")
->orWhere('ld_phoneno','like', "%".$filter['keyword']."%");
});
}
This keyword-matching scheme is inherently, and disastrously, slow. It's slow in both Eloquent and native SQL. There's no way it can work in MySQL without doing a full table scan. That is, it must examine every row of your table looking for matches and cannot, in MySQL, exploit any indexed lookup scheme. Why?
column LIKE 'constant%'
can look at an index on column and quickly find any value starting with 'constant'. But
column LIKE '%constant%'
has to look at every value in the table. The leading % makes the index lookup useless.
In MySQL you would be wise to investigate MySQL's FULLTEXT searching as a way of handling your keyword lookup. (Recent versions of postgreSQL can handle this sort of query directly with a different kind of index, but not MySQL.)

Laravel make array and use strtolower

I have data like this:
array:1 [
0 => "No Brand,ddfg"
]
First of all this data is wrong, what I want is to have something like this:
array:2 [
0 => "No Brand"
1 => "ddfg"
]
So now its really an array :)
Then I need my array data transform to lower case like:
array:2 [
0 => "no brand"
1 => "ddfg"
]
Code
$sibarBrandsArray = SidebarManager::first()->pluck('brands')->toArray();
This return data like:
array:1 [
0 => "No Brand,ddfg"
]
And this is how my data looks like in database:
Any idea?
Solved
// get my table row
$sibarBrandsArray = SidebarManager::first();
// get my row column
$getBrandColumn = $sibarBrandsArray->brands;
// separate data in that column with comma
$separateBrands = explode(',', $getBrandColumn);
// lowercase each separated data
$brandsArray = array_map('strtolower', $separateBrands);
// dump the result
dd($brandsArray);
Result
array:2 [
0 => "no brand"
1 => "ddfg"
]
Laravel has a very efficient and easy way to work with arrays. It's called a collection. Click here to learn more. Don't convert your response to the array, use collection directly.
$sibarBrandsCollection = SidebarManager::first()->pluck('brands');
Laravel eloquent by default gives you collection instance when you get a response. so pluck in above call is nothing but calling pluck on collection instance. We can chain method to the collection and do manipulation as needed.
$sibarBrandsCollection = $sibarBrandsCollection->map(function ($name) {
return strtolower($name);
});
Above code will automatically convert all of your values to lowercase. Similarly, you can explode the value to get your intended result. At last, if you have to send data as array to the browser just add toArray() method at the end of your collection.
I would not use core PHP array function unless needed, Laravel collection is great way to work with arrays.
$yourArray = array_map('strtolower', $yourArray);
$yourArray = array_map('nestedLowercase', $yourArray);
function nestedLowercase($value) {
if (is_array($value)) {
return array_map('nestedLowercase', $value);
}
return strtolower($value);
}
or you can use:
$query->whereRaw('LOWER(`newsTitle`) LIKE ? ',[trim(strtolower($newsTitle)).'%']);

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

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.

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