Output entities with relationships in json - laravel

I have a device entity which has one-to-many relationship with picture entity. Like one device has many pictures. How can I output in JSON all device properties plus array of pictures?
When I try:
dd(Device::find(1)->pictures);
I get array of laravel objects with some additional information. I tried to do it in some ways but didn't managed to get just an array of simple picture objects (or array of arrays)
Although this works:
foreach (Device::find(1)->pictures as $picture) {
$data['pictures']['path'] = $picture->path;
}
dd($data['pictures']);
It seems weird to form array that way
Basically I need to output json with arrays of one-to-many objects, like pictures and some other. So I'll get something like:
["name": "myDevice", "price": "15", "pictures": [...], "another": [...]]
I am using laravel 3

Are you using Laravel 3? In that case this should do the trick:
return Response::eloquent(Device::with('pictures')->find(1));
If your are using Response::eloquent the content-type is also automatically set to application/json.
Edit: if you want only certain fields to return, you could use Response::json(Device::with('pictures')->lists('name', 'id')); to return an array just of that values already JSONified.

Related

Livewire and Eloquent - Sums of multiple columns in filtered collection

quite a noob in Laravel and Livewire, I guess if there is a better way to accomplish what I have here:
I have some checkboxes in a Livewire view that allow the user to dynamically choose how to filter the data being shown in a HTML table.
On the controller I have an eloquent query that retrieves a subset of the records in the db whenever a checkbox changes state.
Like this (I tried to simplify the structure):
$filtered_items = Item::select('item_color', 'item_quantity', 'item_value')
->whereIn('item_color', [ /* array controlled by checkboxes values in view */ ])
->get();
I need to also show the sums of some columns for that specific filter setup.
As I don't want to make new queries, I try to work on the already available collection with a ->sum() for each column I want to sum:
$sums['item_quantity'] = $filtered_items->sum('item_quantity');
$sums['item_value'] = $filtered_items->sum('item_value');
It works as intended, as I have an array with the sums, but I was wondering if there is a better way to do this, or even write a function to get more columns sum at once, maybe passing an array.
Thanks for any idea or link!
Here is an example of a single query for all sums (two in this example)
$filtered_items = \DB::table('items')
->selectRaw('SUM(item_quantity) as sum_quantity, SUM(item_value) as sum_value')
->whereIn('item_color', [ /* array controlled by checkboxes values in view */ ])
->first();
The advantage is that it will return one entry/row instead of a collection of all the items which is way faster.

SOLVED: Looking for a smarter way to sync and order entries in Laravel/Eloquent pivot table

In my Laravel 5.1 app, I have classes Page (models a webpage) and Media (models an image). A Page contains a collection of Media objects and this relationship is maintained in a "media_page" pivot table. The pivot table has columns for page_id, media_id and sort_order.
A utility form on the site allows an Admin to manually associate one or more Media items to a Page and specify the order in which the Media items render in the view. When the form submits, the Controller receives a sorted list of media ids. The association is saved in the Controller store() and update() methods as follows:
[STORE] $page->media()->attach($mediaIds);
[UPDATE] $page->media()->sync($mediaIds);
This works fine but doesn't allow me to save the sort_order specified in the mediaIds request param. As such, Media items are always returned to the view in the order in which they appear in the database, regardless of how the Admin manually ordered them. I know how to attach extra data for the pivot table when saving a single record, but don't know how to do this (or if it's even possible) when passing an array to attach() or sync(), as shown above.
The only ways I can see to do it are:
loop over the array, calling attach() once for each entry and passing along the current counter index as sort_order.
first detach() all associations and then pass mediaIds array to attach() or sync(). A side benefit would be that it eliminates the need for a sort_order column at all.
I'm hoping there is an easier solution that requires fewer trips to the database. Or am I just overthinking it and, in reality, doing the loop myself is really no different than letting Laravel do it further down the line when it receives the array?
[SOLUTION] I got it working by reshaping the array as follows. It explodes the comma-delimited 'mediaIds' request param and loops over the resulting array, assigning each media id as the key in the $mediaIds array, setting the sort_order value equal to the key's position within the array.
$rawMediaIds = explode(',', request('mediaIds'));
foreach($rawMediaIds as $mediaId) {
$mediaIds[$mediaId] = ['sort_order' => array_search($mediaId, $rawMediaIds)];
}
And then sorted by sort_order when retrieving the Page's associated media:
public function media() {
return $this->belongsToMany(Media::class)->orderBy('sort_order', 'asc');
}
You can add data to the pivot table while attaching or syncing, like so:
$mediaIds = [
1 => ['sort_order' => 'order_for_1'],
3 => ['sort_order' => 'order_for_3']
];
//[STORE]
$page->media()->attach($mediaIds;
//[UPDATE]
$page->media()->sync($mediaIds);

ManyToMany with and whereIn

I have a ManyToMany relationship between AdInterest and AdInterestGroup models, with a belongsToMany() method in each model so I can use dynamic properties:
AdInterest->groups
AdInterestGroup->interests
I can find all the "interests" in a single group like this:
$interests = AdInterestGroup::find(1)->interests->pluck('foo');
What I need is a merged, deduplicated array of the related 'foo' field from multiple groups.
I imagine I can deduplicate with ->unique(), but first, as you'd expect, this:
AdInterestGroup::whereIn('id',[1,2])->interests->get();
throws:
Property [interests] does not exist on the Eloquent builder instance.
The advice seems to be to use eager loading via with():
AdInterestGroup::with('interests')->whereIn('id',[1,2])->get();
Firstly, as you'd expect that's giving me an array of two values though (one for each ID).
Also, if I try and pluck('foo') again, it's looking in the wrong database table: from the AdInterestGroup table, rather than the relationship (AdInterest).
Is there a nice, neat Collection method / pipeline I can use to combine the data and get access to the relationship fields?
Use pluck() and flatten():
$groups = AdInterestGroup::with('interests')->whereIn('id', [1, 2])->get();
$interests = $groups->pluck('interests')->flatten();
$foos = $interests->pluck('foo')->unique();

select certain columns from eloquent collection after the query has executed

Using Laravel 5.3, I have a model with the following function
public function myData() {
return $this->hasMany(MyData::class);
}
and in my collection I have the following
$my_data = MyModel->myData()->get();
All good so far. If I return $my_data I get an eloquent collection with three items.
What I need now though is to create a duplicate of that collection but containing only three of the fields.
I have tried several different things, each of which return an error. The following is the closest I have got, but this returns an empty array - I assume because the fields are located one level deeper than the collection object.
$new_collection = $my_data->only(['field_1', 'field_2', 'field_3']);
What would be the correct way to create a new collection containing all three items, each with only the three selected fields?
Thanks for your help
You could use map:
$slimmed_down = $collection->map(function ($item, $key) {
return [
'field_1' => $item->field_1,
'field_2' => $item->field_2,
'field_3' => $item->field_3
];
});
This will return a new Collection with just the values you want. As far as I know there isn't any other method that does what you want, so iterating over every item and selecting the fields this way is one of the few solutions.
The advantage of using map instead of a standard foreach loop is that when you use map it returns a new instance of Collection.
Edit:
After some thoughts and research about this, the problem you'll have created is that the all the values in the Collection aren't instances of anything anymore. If you don't mind this effect, an even prettier and faster way would be to do this:
$slimmed_down = $collection->toArray()->only(['field_1', 'field_2', 'field_3']);
This basically has the same result.
Using Laravel 9, I just had the same issue :
$my_data->only(['field_1', 'field_2', 'field_3']);
returning an empty array.
I solved it with :
$my_data->map->only(['field_1', 'field_2', 'field_3']);

How to use one to many relation query in parse.com?

I have the 2 classes one is post and other is comments
=> One post has many comments
And i want to result like this
results:[ {
post_title:'post1',
date:'..',
postcomments:[
{comment1},
{comment2},...]
},
{
post_title:'post2',
date:'..',
postcomments:[
{comment1},
{comment2},...]
}
]
The easiest would be to use Lists: The Post object has a property postcomments that is a list of Comment objects. You could query then as follows (javascript):
new Parse.Query("Comment")
.include("postcomments")
This will give you a list of Post objects. Each Post object will have a list of associated Comment objects which you can access like this: post.get("postcomments")
If you expect to usually have more than 100 or so comments for one post you can also use Parse relations. The advantage here is that the comments are not always fetched together with the post thus saving data.

Resources