Getting Records with multiple ids in eloquent - laravel

Hello i am posting the id of several items along with their quantities to my server eg.:
[{"id": "1", "quantity": "2"}, {"id": "2", "quantity": "5"}, {"id": "3", "quanity": "10"}]
Now on my server I need to validate each of these ids and get certain values from their respective rows in my database table.
What i simply want to do is this:
foreach ($items as $item)
{
$row = Items::find($item);
$price = $row->price;
}
But here i am wondering if the foreach loop will have a huge impact on speed and performance since the items may be many.
My question is is there a way of doing this in laravel without using a foreach, loop, like a query that can fetch data based on an array of ids.

If you want to take all the price attributes of certain ids you can use query's builder whereIn method to achieve this.whereIn accepts an array of values and returns the rows corresponding to these values.It's also very cost efficient when it comes to performance
I see you are passing a json string, so if you where to use whereIn one solution might be like the following:
Example
//json string
$json = '[{"id": "1", "quantity": "2"}, {"id": "2", "quantity": "5"}, {"id": "3", "quanity": "10"}]';
//convert json to array
$json_toArray = json_decode($json,true);
//get id values from array
//If you are using php 5.5+ this will work, if you are using an older version you can use
//array_map method like this: $array_ids = array_map(function ($array) {return $array['id'];}, $json_toArray);
$array_ids = array_column($json_toArray, 'id');
//Finally execute the query
//Using Eloquent
$result = Items::whereIn('id', $array_ids)->select('price')->get();
//Using the Query Builder
$result = DB::table('items')->whereIn('id',$array_ids)->select('price')->get();

Related

The filtered result I get by running the filter method on a laravel collection returns a new collection with an undesired index

To make it easier to understand the problem, I will hardcode the data that I am using the collection on and explain the problem.
Let us assume the following data structure in JSON format,
{
"shelters_with_linear_distances": [
{
"id": 3,
"shelterName": "Third Shelter",
"latitude": "5.0034000",
"longitude": "70.1230000",
"linear_distance": 3.1352984845527
},
{
"id": 4,
"shelterName": "Fourth Shelter",
"latitude": "5.1413000",
"longitude": "70.2250000",
"linear_distance": 2.7850629146201
},
{
"id": 5,
"shelterName": "Fifth Shelter",
"latitude": "5.2220000",
"longitude": "70.1320000",
"linear_distance": 2.6042789457753
}
]
}
The following filter method is run on a collection format of 'shelters_with_linear_distance' in the above data structure and $minimum_distance_to_a_shelter is a dynamically calculated value that holds a data type of double.
$nearest_shelter = $shelters_with_linear_distances_from_user
->filter(function ($shelter, $key) use ($minimum_distance_to_a_shelter) {
return $shelter['linear_distance'] == $minimum_distance_to_a_shelter;
});
The problem here is if I send back the value returned by the filter method (which is the $nearest_shelter) as JSON to the frontend,
in the postman I see the following output,
{
"nearest_shelter": {
"2": { // <------------------------------------ I can not figure out from where this key '2' is coming from.
"id": 5,
"shelterName": "Fifth Shelter",
"latitude": "5.2220000",
"longitude": "70.1320000",
"linear_distance": 2.6042789457753
}
}
}
The problem is I can not figure out from where the key I have pointed with an arrow in the above line of code is coming from.
*) It is okay if that value '2' never changes so that in the later parts of code I can always access the $nearest_shelter as $nearest_shelter['2']. But the problem is, the value of that key changes depending on the data I am receiving from the db.
One time that value of the key was '1', then once I added some new records to the db it was '2'. Also this one other time there was no key marked as either '1' or '2' and the shelter I wanted was directly inside the collection.
Can someone please help me understand why this is happening and how to get rid of that since I want to access the value inside the $nearest_shelter in latter parts of the code and I do not want to get a key like that which I do not know the value of beforehand to access the $nearest_shelter later in the code.
(Project I am working on uses laravel 5.2)
Thanks.
When you filter a collection, the index is preserved.
The "2" is because this element was the third (so index 2) in your original collection.
To fix this, just add ->values() after the filter:
$nearest_shelter = $shelters_with_linear_distances_from_user
->filter(function ($shelter, $key) use ($minimum_distance_to_a_shelter) {
return $shelter['linear_distance'] == $minimum_distance_to_a_shelter;
})->values();
This way the index will be reset and will start from 0, as usual.
From the documentation (for Laravel 5.2 as stated in your question) documentation:
The values method returns a new collection with the keys reset to consecutive integers

How to implement a partial resource rest api?

In order to limit the size of my REST API answers, I want to implement the Google performance tip: using the fields query string parameter to do partial resources.
If I have a full answer GET https://myapi.com/v1/users
[
{
"id": 12,
"first_name": "Angie",
"last_name": "Smith",
"address": {
"street": "1122 Something St.",
"city": "A city"
..and so on...
}
},
... and so on
]
I will be able to filter it GET https://myapi.com/v1/users?fields=first_name
[
{
"id": 12,
"first_name": "Angie"
},
... and so on
]
The concept is pretty easy to understand, but I can't find an easy way to implement it!
My API resources are all design the same way:
use query string parameters for filtering, sorting, paging.
call a service with that parameters to do a SQL request (only the WHERE condition, the ORDER BY condition and the LIMIT are dynamic)
use a converter to format data back to JSON
But when using this new fields parameter, what do I need to do? where do I filter the data?
Do I need to filter only the JSON output? But I will make (in that example) an unwanted JOIN query on address table and fetch unwanted fields in the users table.
Do I need to make a dynamic SQL query to fetch exactly the requested fields and add the JOIN only when the end user need it? Then the converter will have to be smart to convert only the available fields in the SQL query.
In my opinion, this second solution will produce a code extremely dynamic, extremely complex and difficult to maintain.
So, how do you implement such REST API with partial resource feature? What are you best practice in that case?
(I'm a PHP developer, but I don't think it's relevant for that question)
If your backend is doing
GET https://myapi.com/v1/users
which results in SQL:
select * from users
which you then turn into JSON, can you not just do:
GET https://myapi.com/v1/users?fields=first_name,surname,email
get all the required fields (rough idea of PHP implementation):
$fields = split(",", $_GET["fields"]);
$sql = "select ";
foreach ($fields as &$field) {
// do a check to see if the field is ok first...
if (checkField($field)) {
$sql += field + "," // deal with commas
}
}
$sql += " from users";
to build SQL like:
select firstname,surname,email from users
and turn that limited dataset to JSON?

How to access nested json objects without json_decoding the collection

I have the following data structure:
[
{
"id": 1,
"customer_id": "11",
"customer_name": "Vic",
"cheque_withdraw": {
"id": 2,
"request_date", "2019-03-30"
}
}
]
I have gotten this data from multiple tables through the following code;
$paginator = Cheque::with(['chequeWithdraw' => function($query){
$query->where('withdraw_status' , 'withdrawn');
}])
->paginate(5);
How do I access the nested json object request_date without having to convert my collection to an associative array? The reason why I do not want to convert the collection to an associative array is because I am having challenges paginating the data that has been json decoded.
I am currently trying to access the request_date in my blade file this way;
#foreach($paginator as $cheque)
{{ $cheque->cheque_withdraw->request_date }}
#endforeach
However, I am getting the following error;
Trying to get property 'request_date' of non-object
Actually you have chequeWithdraw and not cheque_withdraw
#foreach($paginator as $cheque)
{{ $cheque->chequeWithdraw->request_date ?? 'NA'}}
#endforeach
You might be trying to convert laravel collection to json or array. laravel convert camel case to snake case . So you were getting it for json not for laravel template.

How to get random row in object variable in Laravel?

Sorry for my bad english, I want to get a single row in my object. And I want that in random order. Im using array_rand() and it only return errors as stated below:
ErrorException: array_rand() expects parameter 1 to be array, object given in file C:\xampp\htdocs\user\TestProject\app\Http\Controllers\TestController.php on line
Here is my object.
"my_list": [
{
"id": 1,
"name": "My Name Test",
"address": [
{
"id": 1,
"city": "Manila",
"country": "Philippines"
}
]
},
{
"id": 2,
"name": "Your Name Test",
"address": [
{
"id": 2,
"city": "Cebu",
"country": "Philippines",
}
]
}
]
The problem is I want only to get a single row to the my_list which is object and not an array.
Here is my code.
$course = Course::where('id', 1)->with('my_list')->first();
$random_list = array_rand($course->my_list);
return $random_list;
I also try adding number of row in the array_rand like this.
$random_list = array_rand($course->my_list, 1);
But still not working.
What did I missed?
Any Eloquent query returns, by default, a Collection, even for the underlying relationships. Since you are working with one, this should work:
$course->my_list->random();
This will return only one item. If you want more, you could pass an argument to the random() method specifying the count of items you want.
For more information, check the documentation.
This Object is a Laravel collection. Please refer to the collection documentation.
https://laravel.com/docs/5.7/collections#method-random
You can try $course->my_list->random()
If you still wanna do this with your approach, can you try get_object_vars function to cast object into array.
$array = get_object_vars($object);
so that you can use them as an array in array_rand.
You might get an error, hence that it's an multi-dimensional array. Let me know so i may update.
Update for multidimensional:
Please refer to this.
// The second parameter of json_decode forces parsing into an associative array
$array = json_decode(json_encode($object), true);
try this:
$course = Course::where('id', 1)
->with(['my_list' => function($query) {
$query->inRandomOrder();
}])->first();
return $course->my_list;
Try this method:
$course = Course::where('id', 1)
->with(['my_list' => function($query) {
$query->inRandomOrder()->first();
}])->first();
return $course->my_list;
this method is more efficient since you will only get 1 row from my_list not like when you use $course->my_list->random() which retrieves all data and from there select a random row.
$random_list = $course['my_list']->random(number);
ps: number = number of element you want to get ,

Create collection from input array

Lets say I have a form that is a invoice. It has line items like $product[$key], $quantity[$key]. So when the form is submitted the input looks like
{
customer_id : "214"
product_id: [ "1","5", "6" ],
quantity: ["34", "1", "54"]
}
I have a model for that details table. What I have been doing is iterating over it and creating a details object then saving it like this
foreach($product as $key=>$p)
{
if($p)
{
$t = new Details;
$t->product = $p;
$t->quantity = $quantity[$key];
$t->save();
}
}
I'm guessing there is a way to be much more efficient about this. Like creating a collection of details straight from the input but I have no idea how I would accomplish that
You can instantiate models through mass assignment.
$details = new Details(['product_id'=>'1','quantity'=>'34']);
You can also specify columns of the table that you do not want to be mass assigned using the $guarded variable in the model.
Check out mass assignment in Laravel's docs: http://laravel.com/docs/eloquent#mass-assignment
For your particular issue it looks like you would need to build your input array out of the elements of the other arrays.
Seems it isn't possible. Here is Taylor responding to a issue request. It seems the problem is it wouldn't be possible to fire events then. I just ended up doing
$d = array();
foreach ($details as $detail) {
$d[] = new OrderDetail($detail);
}
$order->details()->saveMany($d);

Resources