Dynamically select properties from PHP object - php

I have an array of PHP objects (lets say orders) which have arrays of items like so (JSON for ease of notation):
$orders = [
{
id: 87,
user_id: 6,
menu_id: 9,
items: [
{
id: 1,
name: "Item 1",
}
]
},
{
id: 88,
user_id: 3,
menu_id: 6,
items: [
{
id: 2,
name: "Item 2",
}
]
}
]
I'm trying to make a CSV row with titles of this kind of array. I'm using a keyed array like so:
$settings = [
'Order ID' => 'id',
'User ID' => 'user_id'
];
So now I can go...
$csv = [];
$csv[] = array_keys($settings);
foreach($orders as $order):
$csv_row = [];
foreach($settings as $column);
$csv_row[] = $order->{$column};
endforeach;
$csv[] = $csv_row;
endforeach;
This gives me:
[ 'Order ID', 'User ID' ]
[ '87', '6' ]
[ '88', '3' ]
Is there any way I can dynamically access the 'deeper' variables? i.e. $order->items[0]->name, in a similar way to the curly bracket notation? I need to do it dynamically as there are lots of different object and relations that I need to export as CSV files.
P.S. Its really late – sorry if this is a crazy question

You can't use the curly braces notation to get deeper than one level. What you could do is to use a helper function that would let you fetch values by multi-level key.
An example function could be:
function getByPath($value, array $path)
{
if (empty($path)) return $value;
if (!is_array($value)) return;
if (array_key_exists($path[0], $value)) return getByPath($value[$path[0]], array_slice($path, 1));
}
Example usage pattern for your case could be:
$settings = [
'Order ID' => ['id'],
'User ID' => ['user', 'id'],
'User Name' => ['user', 'name'],
'Item 0 Name' => ['item', 0, 'name'],
'Item 0 Price' => ['item', 0, 'price']
];
foreach($settings as $path);
$csv_row[] = getByPath($order, $path);
endforeach;

Related

PHP: Translation table to convert mutiple values to another value?

I have a a number of values/IDs that need to be translated to a single ID, what is the recommended method using PHP?
For example, I want IDs 38332, 84371, 37939, 1275 to all translate to ID 1234 and IDs222, 47391, 798 to all translate to ID 1235, etc. .
I'm thinking PHP has something built-in to handle this efficiently?
I'm thinking PHP has something built-in to handle this efficiently?
You can use the standard array as a map, quickly translating one ID to another:
$table[38332]; # int(1234)
depending on how you store your overall translation table, you can create a function that returns the translation from its input:
$table = $translation('I want IDs 38332, 84371, 37939, 1275 to all translate to ID 1234');
$result = $table[1275] ?? null; # int(1234)
Example:
$parseId = static fn(string $i) => (int)trim($i);
$translation = static fn(string $buffer): array
=> preg_match_all('~((?:\d+,\s*)+\d+)\s+to all translate to ID\s*(\d+)~', $buffer, $_, PREG_SET_ORDER)
? array_reduce($_, static fn (array $carry, array $item): array => [
$ids = array_map($parseId, explode(',', $item[1])),
$carry += array_fill_keys($ids, $parseId($item[2])),
$carry,][2], []) : [];
This is pretty easy to accomplish with PHP, here's one way you could do it:
Using this method, you populate the $map array, using the id you want to replace with as the key, and the value being an array of the keys you want to be replaced. It then calculates a simple key => value array based on this to make comparison a lot quicker.
Instead of creating a copy of the data, you could use foreach ($data as &$record)
$data = [
[
'id' => 1,
'foreign_id' => 38332,
'text' => 'a'
],
[
'id' => 2,
'foreign_id' => 84371,
'text' => 'b'
],
[
'id' => 3,
'foreign_id' => 37939,
'text' => 'c'
],
[
'id' => 4,
'foreign_id' => 1275,
'text' => 'd'
],
[
'id' => 5,
'foreign_id' => 222,
'text' => 'e'
],
[
'id' => 5,
'foreign_id' => 47391,
'text' => 'f'
],
[
'id' => 5,
'foreign_id' => 798,
'text' => 'g'
]
];
$map = [
123 => [
38332,
84371,
37939,
1275
],
1235 => [
222,
47391,
798
]
];
// Calculate a map to speed things up later
$map_calc = [];
foreach ($map as $destination_id => $ids) {
foreach ($ids as $id) {
$map_calc[$id] = $destination_id;
}
}
$new_data = [];
foreach ($data as $record) {
if (isset($map_calc[$record['foreign_id']]))
$record['foreign_id'] = $map_calc[$record['foreign_id']];
$new_data[] = $record;
}
var_dump($new_data);

Loop through Categories and its Products in PHP Laravel

I have to create an array with the categories array and its related products i'm fetching category with all related products using this query
$categoryData= Category::with('product')->get();
Data is beign fetched properly, using this loop to get data in the format given below:
foreach($categoryData as $row){
$categoriesData[] = [
'id' => $row->id,
'title' => $row->title,
];
foreach($row->product as $rowproduct){
$categoriesData['product_details'][] = [
'product_name' => $rowproduct->name,
'product_price' => $rowproduct->price
];
}
}
Format (It should be something like this) :
{
"categoriesData": {
"0": {
"id": 1,
"category_name" : "Name 1",
"product_details": [
{
"id": 1,
"product_name": Product Name,
},
],
"1": {
"id": 2,
""category_name" ": "Name 2",
"product_details": [
{
"id": 1,
"product_name": product name,
},
},
but through present loop all product is getting saved in one array. It is not saving inside category.
Any help is highly appreciated
You can do this with Collection methods if you wanted to, basically just map:
$array = $categoryData->map(function ($category) {
return [
'id' => $category->id,
'title' => $category->title,
'product_details' => $category->products->map(function ($product) {
return [
'product_name' => $product->name,
'product_price' => $product->price,
];
}),
];
})->all();
Though this is going in the direction of using a transformer or API resource to format this data.
Hi I think this is what you're looking for :
foreach ($categoryData as $key => $row) {
$categoriesData[$key] = [
'id' => $row->id,
'title' => $row->title,
];
foreach ($row->product as $rowproduct) {
$categoriesData[$key]['product_details'][] = [
'product_name' => $rowproduct->name,
'product_price' => $rowproduct->price
];
}
}

Get item in array according to the value of a key within the item

Suppose I have an array like this:
$myArray = [
[ 'id' => 1, 'name' => 'Some Name' ],
[ 'id' => 2, 'name' => 'Some Other Name ]
]
Now if want to get the second item without using the index ($myArray[1]), but instead by using the value of name which is Some Other Name, how do I do that?
I don't want to loop through the entire array just for one value, so how can I write this in a way that I don't explicitly loop through the array myself? I looked into array_search(), but couldn't find examples where $myArray contains additional arrays as items.
you can use array_filter() function:
<?php
$myArray = [
['id' => 1, 'name' => 'Some Name'],
[
'id' => 2, 'name' => 'Some Other Name'
]
];
$filterData = array_filter($myArray, function ($data) {
return $data['name'] == "Some Other Name";
});
var_dump($filterData);
die;
?>
Not entirely sure what you're trying to do, but you can index the array on name:
echo array_column($myArray, null, 'name')['Some Other Name']['id']; //echos 2
To do it once and have access to all name:
$result = array_column($myArray, null, 'name');
echo $result['Some Other Name']['id']; // echos 2
echo $result['Some Name']['id']; // echos 1
This is the simpliest solution:
$requiredItem = null;
foreach ($myArray as $item) {
if ($item['name'] === 'Some Other Name') {
$requiredItem = $item;
// `break` allows you to STOP iterating over array
break;
}
}
print_r($requiredItem);
All other solutions will include full loop over your array at least once.

Combine two array using a field in PHP

I have an array in which i want to combine the common fields together.
So that the common names get grouped together and they contain the common values of that particular name.
Below is my array
[
[
count_employee: 2,
name: "Harry"
],
[
count_employee: 61,
name: "Jack"
],
[
count_employee: 11,
name: "John"
],
[
count_warehouse: 1,
name: "Harry"
],
[
count_warehouse: 77,
name: "John"
],
[
count_warehouse: 45,
name: "Jack"
]
]
I want the output to be
[
[
name: "Harry",
count_employee:2
count_warehouse:1
],
[
name: "Jack",
count_employee: 61
count_warehouse: 45
],
[
name: "John",
count_employee:11
count_warehouse:77
],
]
So far i have tried this out
foreach ($data as $key => $value) {
$group[$value['name']]['name'] = $value['name'];
$group[$value['name']][$key]['count_employee'] = $value['count_employee'];
$group[$value['name']][$key]['count_warehouse'] = $value['count_warehouse'];
}
Loop over the array, and use the name as the index of each element. If no element by that index does not exist, define it as an empty array.
Then all you need to do is add the fields (if they exist) to the proper column.
Your issue was that you were creating an array with one too many dimensions, instead of adding each value to the current count - in addition to the fact that you will not always have both the warehouse and employee count defined for each iteration.
$result = [];
foreach ($arr as $v) {
// Initialize the person
if (!isset($result[$v['name']])) {
$result[$v['name']] = ["name" => $v['name'], "count_employee" => 0, "count_warehouse" => 0];
}
// Add values
if (isset($v['count_employee']))
$result[$v['name']]['count_employee'] += $v['count_employee'];
if (isset($v['count_warehouse']))
$result[$v['name']]['count_warehouse'] += $v['count_warehouse'];
}
// print_r($result);
Live demo at https://3v4l.org/TeNKF
You can use array_walk with array_key_exists
$res=[];
array_walk($arr, function($v,$k) use (&$res){
if(array_key_exists($v['name'], $res))
$res[$v['name']]['count_warehouse'] = $v['count_warehouse'];
else
$res[$v['name']] = $v;
});
print_r(array_values($res));
Live Demo
Assuming all the array has a name key for each sub array contained within, we can use the name as a key for an array and merge arrays with corresponding name values.
<?php
$data=
[
[
'count_employee'=> 2,
'name'=> 'Harry'
],
[
'count_employee'=> 61,
'name'=> 'Jack'
],
[
'count_employee'=> 11,
'name'=> 'John'
],
[
'count_warehouse'=> 1,
'name'=> 'Harry'
],
[
'count_warehouse'=> 77,
'name'=> 'John'
],
[
'count_warehouse'=> 45,
'name'=> 'Jack'
]
];
$output = [];
foreach($data as $item) {
$output[$item['name']] = array_merge($item, $output[$item['name']] ?? []);
}
var_export($output);
Output:
array (
'Harry' =>
array (
'count_warehouse' => 1,
'name' => 'Harry',
'count_employee' => 2,
),
'Jack' =>
array (
'count_warehouse' => 45,
'name' => 'Jack',
'count_employee' => 61,
),
'John' =>
array (
'count_warehouse' => 77,
'name' => 'John',
'count_employee' => 11,
),
)

PHP array group by category

I have a list of stores and they can have multiple categories.
Today i display each store multiple times, one line with only one category.
How can i group the stores, creating an array of their categories, so the store is displayed only one time and have their multiple categories ?
This is my attempt:
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
array_push(
$stores,
[
"id"=> $row["id"],
"name"=> $row["nome"],
"categories"=> array([
"name"=> $row["categories"]
//how can i have an array of categories
])
]
);
}
This is how i display the json result:
{
id: 74,
name: "First Store",
categories: [
{
name: "Clothes"
}
]
},
{
id: 1,
name: "Second Store",
categories: [
{
name: "Food"
}
]
},
{
id: 1,
name: "Second Store",
categories: [
{
name: "Toys"
}
]
},
This is how i need to show the Json:
{
id: 74,
name: "First Store",
categories: [
{
name: "Clothes"
}
]
},
{
id: 1,
name: "Second Store",
categories: [
{
name: "Food"
},
{
name: "Toys"
}
]
},
In my attempt i try inside the while to create the categories but have a php warning: end() expects parameter 1 to be array, null given
if( isset($stores) ) {
if( end($stores['id']) != $row["id"] ) {
array_push($category_array, $row["categories"]);
}
}
I believe you are looking for something like this when processing your returned MySQL rows to json_encode: https://3v4l.org/d4Wf2
This will place the stores in 1 temp $storeTracker array with the store id as the array key (this assumes store id is a constant in each record you're getting back and is always the same). From here you can then check if the array key (store id) already exists, and if so, continue to add categories to the store if the category doesn't already exist. After doing this, you can then parse to json_encode to create a valid JSON object to return. Granted there may be a more eloquent way to achieve this, but this showcases the array manipulation and whatnot to try and group data for your case.
<?php
// Data coming from MySQL Database
$rowData = [];
$rowData[] = ['id' => 74, 'name' => 'First Store', 'categories' => 'Food'];
$rowData[] = ['id' => 74, 'name' => 'First Store', 'categories' => 'DVDs'];
$rowData[] = ['id' => 1, 'name' => 'Second Store', 'categories' => 'Food'];
$rowData[] = ['id' => 1, 'name' => 'Second Store', 'categories' => 'Toys'];
$rowData[] = ['id' => 1, 'name' => 'Second Store', 'categories' => 'Toys'];
$rowData[] = ['id' => 1, 'name' => 'Second Store', 'categories' => 'Clothing'];
$rowData[] = ['id' => 3, 'name' => 'Third Store', 'categories' => 'Toys'];
$rowData[] = ['id' => 3, 'name' => 'Third Store', 'categories' => 'Clothing'];
$rowData[] = ['id' => 3, 'name' => 'Third Store', 'categories' => 'Clothing'];
/**
* Check if store category name already added to store record
*/
function categoryExistsAlready($categories, $category) {
foreach($categories as $key => $catArr) {
if(strtolower($catArr['name']) === strtolower($category)) {
return true;
}
}
return false;
}
$storeTracker = [];
foreach($rowData as $key => $storeData) {
$storeCategory = $storeData['categories'];
$storeId = $storeData['id'];
// If store exists, add category to categories array
if (array_key_exists($storeId, $storeTracker)) {
if (!categoryExistsAlready($storeTracker[$storeId]['categories'], $storeCategory)) {
$storeTracker[$storeId]['categories'][] = ['name' => $storeCategory];
}
continue;
}
// Update store categories to be array with category
$storeData['categories'] = [];
$storeData['categories'][] = ['name' => $storeCategory];
// Add store data to overall tracking array
$storeTracker[$storeId] = $storeData;
}
// Format JSON response
$jsonReturn = '[';
$i = count($storeTracker);
foreach($storeTracker as $storeId => $storeData) {
$i--;
$jsonReturn .= json_encode($storeData);
// Determine last comma separating objects
if ($i > 0) {
$jsonReturn .= ',';
}
}
$jsonReturn .= ']';
echo $jsonReturn;
Will give you this valid JSON https://jsonlint.com/:
[{
"id": 74,
"name": "First Store",
"categories": [{
"name": "Food"
}, {
"name": "DVDs"
}]
}, {
"id": 1,
"name": "Second Store",
"categories": [{
"name": "Food"
}, {
"name": "Toys"
}, {
"name": "Clothing"
}]
}, {
"id": 3,
"name": "Third Store",
"categories": [{
"name": "Toys"
}, {
"name": "Clothing"
}]
}]

Categories