pass additional data to laravel resource - php

i need to pass My custom data to the same level of array of resource but it goes out of array
return CategoryProductsResource::collection(Category::whereNull('parent_id')->get())
->additional([
'data' => [
'id' => 9999,
'name' => 'How Offers',
'image' => 'http://businessdotkom.com/storage/categories/January2020/1o6nDi1kjVuwje5FiFXv.png',
'products' => ProductIndexResource::collection(Product::whereNotNull('sale_price')->get()),
]
]);
json output

Because additional is for top-level data, use concat instaed.
return CategoryProductsResource::collection(Category::whereNull('parent_id')->get())
->concat([
'data' => [
'id' => 9999,
'name' => 'How Offers',
'image' => 'http://businessdotkom.com/storage/categories/January2020/1o6nDi1kjVuwje5FiFXv.png',
'products' => ProductIndexResource::collection(Product::whereNotNull('sale_price')->get()),
]
]);

You may use PHP array_merge() function
$categories = Category::whereNull('parent_id')->get()->toArray();
$merged = array_merge($categories, [
[
'id' => 9999,
'name' => 'How Offers',
'image' => 'http://businessdotkom.com/storage/categories/January2020/1o6nDi1kjVuwje5FiFXv.png',
'products' => ProductIndexResource::collection(Product::whereNotNull('sale_price')->get()),
]
]);
return CategoryProductsResource::collection(collect($merged));

Related

How to validate an array inside an array payload in laravel

How to validate a request like for this example i want to create a custom validation for qualities based on a group type. I know how to create a custom validation for laravel but for the example below i want to create a validation for quality type based on its group type.
The payload below it just for demonstration.
$payload = [
'groups' => [
[
'type' => 'human',
'qualities' => [
[
'type' => 'hair',
'value' => 'blue'
],
[
'type' => 'height',
'value' => '188cm'
],
]
],
[
'type' => 'cat',
'qualities' => [
[
'type' => 'hair',
'value' => 'yellow'
]
]
]
]
];
You can use the wildcard, for example:
$request->validate([
'payload.*' => 'required|array',
'payload.*.type' => 'required',
'payload.*.qualities' => 'required|array',
'payload.*.qualities.*' => 'required'
]);

Looping off of MySQL result and building custom array

The following code is building the proper structure of array that I want, but it's only doing it for one result even though the MYSQL result is about 65 items.
I need the 'type' and 'features' levels to be static, and then start the loop within the features level (so every mysql result would build an item in within the features level)
The following is only loading the last result from the query:
$db->setQuery($query);
$item=$db->loadObjectList();
echo $db->getErrorMsg();
//dump($item);
$stores = [
'type' => 'FeatureCollection',
'features' => [
foreach ($item as $i) {
[
'type' => 'Feature',
'geometry' => [
'type' => 'Point',
'coordinates' => [
$i->lat,
$i->lng
]
],
'properties' => [
'storeImage' => $i->logo, // you need to convert this into an actual URL
'storeName' => $i->name,
'phoneFormatted' => $i->phone, // this needs formatting aswell
'address' => $i->address,
'city' => $i->city,
'country' => $i->country,
'postalCode' => $i->zip,
'state' => $i->state
]
]
}
]
];
How can I properly loop the mysql query to build my array in the correct way?
Use array_map() to generate a new array from another array.
$item=$db->loadObjectList();
$stores = [
'type' => 'FeatureCollection',
'features' => array_map(function($i) {
return [
'type' => 'Feature',
'geometry' => [
'type' => 'Point',
'coordinates' => [
$i->lat,
$i->lng
]
],
'properties' => [
'storeImage' => $i->logo, // you need to convert this into an actual URL
'storeName' => $i->name,
'phoneFormatted' => $i->phone, // this needs formatting aswell
'address' => $i->address,
'city' => $i->city,
'country' => $i->country,
'postalCode' => $i->zip,
'state' => $i->state
]
];
}, $item)
];

Multidimensional array with polymorphic relation (tree structure)

To understand what I want to do, here a simple expain : I want to create templates.
One "Template" has a "Panel"
This "Panel" has some "Item" and another "Panel"
We have to do this step by step until the last "Item"
I want something like this :
- Template
- Panel "Section"
- Panel "Slider"
- Panel "Slide 1"
- Item "Text 1"
- Item "Text 2"
- Item "Button"
- Panel "Slide 2"
- Panel "Buttons"
- Item "Button 1"
- Item "Button 2"
- Item "Text"
With the polymorphic relations from Laravel, I have 3 Models which are "Panel", "Item" and "PanelContent" (this one has some attributesn "parent_id", "contentable_id and "contentable_type" usefull to know which Panel is the parent, and which Item or Panel will be the content.
Obvisouly, my Models contains some function usefull to get contents and parent.
What I did
Currently, I use a recursive function to get Panel's children to build step by step an array with the structure I shown at the beginning.
private function getChildren(PanelContent $panelContent, string $parentName, string $key): void
{
$this->result['template']['global_panel'][$parentName][$key] = $panelContent->contentable->toArray();
if ($panelContent->contentable_type === 'panel') {
foreach($panelContent->contentable->contents as $key => $container) {
$this->getChildren($container, $container->contentable->name, $key);
}
}
}
What I want
I want to have something like this, like a tree structure.
$this->result = [
'template' => [
'id' => 1,
'name' => 'MySlider'
'contents' => [
'id' => 745,
'name' => 'slider',
'type' => 'panel',
'slides' => [
0 => [
'id' => 500,
'name' => 'slide',
'type' => 'panel'
'buttons' => [
0 => [
'id' => 877,
'name' => 'button',
'type' => 'item'
]
]
],
1 => [
'id' => 500,
'name' => 'slide',
'type' => 'panel'
'buttons' => [
0 => [
'id' => 877,
'name' => 'button',
'type' => 'item'
]
]
]
]
]
]
];
What I have for the moment
Currently, I have something like this :
$this->result = [
'template' => [
'id' => 1,
'name' => 'MySlider'
'contents' => [
'id' => 745,
'name' => 'slider',
'type' => 'panel'
],
'slides' => [
0 => [
'id' => 500,
'name' => 'slide',
'type' => 'panel'
],
1 => [
'id' => 500,
'name' => 'slide',
'type' => 'panel'
]
],
'buttons' => [
0 => [
'id' => 877,
'name' => 'button',
'type' => 'item'
],
1 => [
'id' => 877,
'name' => 'button',
'type' => 'item'
]
]
]
];
With this code, the result is "normal", because the key at this line is wrong :
$this->result['template']['global_panel'][$parentName][$key] = $panelContent->contentable->toArray();
But I really don't know how to do what I want with my data.
I hope my explains are fine. Do you have an idea about this ? How can I build properly my array (or json of course) ?
Thank you and have a nice day !
I've found a solution. Laravel has many helpers and there is one for array.
private function formatTemplateData(PanelContent $panelContent, string $path): void
{
Arr::set(
$this->result,
'template.data.' . $path,
"Je suis une data"
);
if ($panelContent->contentable_type === 'panel') {
foreach($panelContent->contentable->contents as $container) {
$newPath = $path . '.' . $container->contentable->name . $container->contentable->order;
$this->formatTemplateData($container, $newPath);
}
}
}
With the "set" function, I can use string to create dynamic path for my array.
For example :
"template.data.slider.slide.0.test"
Will be :
$array['template']['data']['slider']['slide'][0]['test']
Thanks for your help.

Merge multiple rows from a function's return data into the middle of a 2d array [duplicate]

This question already has answers here:
PHP is there a way to add elements calling a function from inside of array
(3 answers)
Closed 4 months ago.
I want to merge the rows returned from a function call directly into (the middle of) the declaration of a larger array.
The challenge is that the function is returning multiple rows. Is it possible to inject multiple rows into the expected 2d structure?
Sample input/code:
function some_function() {
return [
['id' => 'thing2', 'type' => 'sample'],
['id' => 'thing3', 'type' => 'sample']
];
}
$ztest = [
['id' => 'thing1', 'type' => 'sample'],
some_function(),
['id' => 'thing4', 'type' => 'sample'],
['id' => 'thing5', 'type' => 'sample']
];
print_r($ztest);
Desired result:
[
['id' => 'thing1', 'type' => 'sample'],
['id' => 'thing2', 'type' => 'sample'],
['id' => 'thing3', 'type' => 'sample'],
['id' => 'thing4', 'type' => 'sample'],
['id' => 'thing5', 'type' => 'sample']
]
But no matter what I try, I can only ever end up with:
[
['id' => 'thing1', 'type' => 'sample'],
[
['id' => 'thing2', 'type' => 'sample'],
['id' => 'thing3', 'type' => 'sample']
],
['id' => 'thing4', 'type' => 'sample'],
['id' => 'thing5', 'type' => 'sample']
]
I've tried array_map, array_filter, array_values, array_merge... nothing seems to work.
This seems simple enough and I feel like it should be possible to do, but I can't figure it out.
Note: I'm aware that I could do an array_merge with these two arrays after the fact, or insert it after the fact, but for the sake of code cleanliness in the project I'm working on, I'd prefer to do it inline.
Try this function:
array_merge_recursive
here is sample code:
<?php
function some_function(){
$items = array();
$items[] = array(
'id' => 'thing2',
'type' => 'sample',
);
$items[] = array(
'id' => 'thing3',
'type' => 'sample',
);
return $items;
}
$ztest = array(
array(
'id' => 'thing1',
'type' => 'sample',
),
array(
'id' => 'thing4',
'type' => 'sample',
),
array(
'id' => 'thing5',
'type' => 'sample',
),
);
$result = array_merge_recursive($ztest, some_function());
echo "<pre>";
print_r($result);
?>
function some_function(){
$items = array();
$items[] = array(
'id' => 'thing2',
'type' => 'sample',
);
$items[] = array(
'id' => 'thing3',
'type' => 'sample',
);
return $items;
}
$ztest = array_merge(some_function(), array(
array(
'id' => 'thing1',
'type' => 'sample',
),
array(
'id' => 'thing4',
'type' => 'sample',
),
array(
'id' => 'thing5',
'type' => 'sample',
),
));
Then, if you need to sort items of array by id field - use usort function:
usort($ztest, function($a, $b) { return strcmp($a['id'], $b['id']); });

Recursive Array Map with Laravel Collection map() Helper

I have a collection of data.
$array = [
[
'id' => 1,
'name' => 'some1',
'type' => 'type1',
'color' => 'color1',
'quantity' => 1
],
[
'id' => 2,
'name' => 'some1',
'type' => 'type1',
'color' => 'color1',
'quantity' => 1
],
[
'id' => 3,
'name' => 'some1',
'type' => 'type1',
'color' => 'color2',
'quantity' => 1
],
[
'id' => 4,
'name' => 'some2',
'type' => 'color1',
'color' => 'type1',
'quantity' => 1
],
......
];
that have different name, type, and color.
I want to group the data by name, type, and color and the result is array data with summary of the same group data.
First, I use this way :
function groupedData($array)
{
$collection = [];
collect($array)->groupBy('name')->map(
function ($item) use (&$collection) {
return $item->groupBy('type')->map(
function ($item) use (&$collection) {
return $item->groupBy('color')->map(
function ($item) use (&$collection) {
$quantity = $item->sum('quantity');
$collection[] = collect($item[0])->merge(compact('quantity'));
}
);
}
);
}
);
return $collection;
}
I expect the output should be like this :
$grouped = [
[
'id' => 1,
'name' => 'some1',
'type' => 'type1',
'color' => 'color1',
'quantity' => 2
],
[
'id' => 2,
'name' => 'some1',
'type' => 'type1',
'color' => 'color2',
'quantity' => 1
],
[
'id' => 3,
'name' => 'some2',
'type' => 'type1',
'color' => 'color1',
'quantity' => 2
],
[
'id' => 4,
'name' => 'some2',
'type' => 'type2',
'color' => 'color1',
'quantity' => 2
],
];
where quantity represent of the number of group items.
But, I the problem is when the required is changed. Just in case :
when user want add other category for grouping, example :
user want to group by name, type, color and size maybe.
Question : How to make a function that can make it more simple and flexible, so doesn't need to change the code when the require changes?
Thanks for the answer.
What you're looking for it sorting, not grouping.
Here's an easy way to do it:
function sort($array, $keys) {
return collect($array)->sortBy(function ($item) use ($keys) {
return array_reduce($keys, function ($carry, $key) use ($item) {
return $carry + $item[$key];
}, '');
})->all();
}
Here's a short explanation:
We're using the collection's sortBy method, which let's us use a callback function that'll return a string to determine the sort order.
We're using array_reduce on the keys to build a single string of all the values in the keys we want to sort by.
Laravel's collection object will use that resulting string to order the collection by.
Finally, we call the all method to get the underlying array from the collection. If you want to actually return a collection, you can remove that last all call.

Categories