Validate array keys at Laravel Request - php

I have a Laravel Request where I need to validate the keys from an array.
The keys are the productId and I am checking if the product belongs to the user.
Here is an example of products at the POST request:
[
8 => [
'quantity' => 10,
'discount' => 10
],
9 => [
'quantity' => 10,
'discount' => 10
]
]
And bellow is the Request rules. Is it possible to check on the keys?
public function rules()
{
return [
'product.*' => 'required|exists:recipes,id,user_id,' . $this->user()->id,
'product.*.quantity' => 'required|numeric|min:0',
'product.*.discount' => 'required|numeric|min:0'
];
}

I made a temporary solution...I kept the id validation at the request.
'products.*.id' => 'required|exists:recipes,id,user_id,' . $this->user()->id,
'products.*.quantity' => 'required|numeric|min:0',
'products.*.discount' => 'required|numeric|min:0',
But at the controller the data is modified to fit the sync() method where the id is removed from the object that will be modified and setted as a key.
$products = [];
for ($i = 0; $i < count($data['products']); $i++) {
$recipes[$data['products'][$i]['id']] = $data['products'][$i];
unset($products[$data['products'][$i]['id']]['id']);
}
$budget->products()->sync($products);
$budget->products = $data['products'];
I didn't mentioned that this is a manytomany polymorphic relationships.

Related

Get total number of occurrences of a data item from nested array

I have an array with a structure like:
$arr = [
'data1' => [ /* some data */],
'data2' => [
'sub-data1' => [
[
'id' => 1
'status' => 'active'
],
[
'id' => 2
'status' => 'not-active'
]
],
'sub-data2' => [
[
'id' => 3
'status' => 'active'
],
[
'id' => 4
'status' => 'active'
]
]
]
]
Is there a simple way in which I can count how many sub-dataxxx have any item with a status is active?
I have managed to do this with nested foreach loops, but I'm wondering if there is a more simple method?
The above example should show the number of active entries as 2, as there are 2 sub-data elements with active statuses. This ignores any non-active statuses.
Edit: To clarify my expected result
I am not wanting to count the number of status = active occurrences. I'm wanting to count the number of sub-dataxxx elements that contain an element with status = active.
So in this instance, both of sub-data1 and sub-data2 contain sub-elements that contain status = active, therefore my count should be 2.
you can do it quite easily with a function like this
function countActiveSubData(array $data): int
{
return array_reduce($data, function ($res, $sub) {
foreach ($sub as $d) {
if ('active' === $d['status']) {
return $res + 1;
}
}
return $res;
}, 0);
}
you can call it on a single data or if you want to get the result for entire $arr you can call it like this
$result = array_map('countActiveSubData', $arr);
// the result will be [
'data1' => 0,
'data2'=> 2
....
]

Check record if exist using updateOrCreate and do a math(SUM) if record exist in laravel 5.5

I have a store function that saves array items into my items table and together with that I am trying to check if the product_is is already in my Warehouse1StockSummaries. if still not, I will grab the product_id and its qty, If its there already then I want to ADD the value from the 'stock_in_qty' which is inside the array to the 'qty_in' in my Warehouse1StockSummaries. I hope my explanation make sense to you :)
here's my code.
public function store(Request $request)
{
$input = $request->all();
$items = [];
for($i=0; $i<= count($input['stock_in_qty']); $i++) {
if(empty($input['stock_in_qty'][$i]) || !is_numeric($input['stock_in_qty'][$i])) continue;
$acceptItem = [
'order_id' => $input['order_id'][$i],
'product_id' => $input['product_id'][$i],
'order_item_id' => $input['order_item_id'][$i],
'delivery_date' => $input['delivery_date'][$i],
'company_id' => $input['company_id'][$i],
'stock_in_qty' => intval($input['stock_in_qty'][$i]),
'stock_out_qty' => $input['stock_out_qty'][$i],
'transfer_to' => $input['transfer_to'][$i],
'delivery_note' => $input['delivery_note'][$i],
'user_id' => $input['user_id'][$i]
];
$product_id = $input['product_id'][$i];
$qty_in = intval($input['stock_in_qty'][$i]);
// dd($qty_in);
// ADD stock_in_qty TO QTY_IN ????
$stockSummary = Warehouse1StockSummaries::updateOrCreate(
['product_id' => $product_id ],
['qty_in' => $qty_in,
'qty_out' => null
]);
// dd($stockSummary);
array_push($items, Warehouse1stocks::create($acceptItem));
}
return redirect()->route('orders.index');
}
I check and everything is ok the only missing is the part where I need to grab the value from 'stock_in_qty' and add to 'qty_in' if the product id is already found in Warehouse1StockSummaries. Thank you so much in advance!
You could use the wasRecentlyCreated property on the model to determine if the model has just been created or not. If it hasn't then it won't have used $qty_in value, this means you could then use the increment() to add to the existing value in the database:
$stockSummary = Warehouse1StockSummaries::firstOrCreate(
['product_id' => $product_id ],
['qty_in' => $qty_in, 'qty_out' => null]
);
if (!$stockSummary->wasRecentlyCreated) {
$stockSummary->increment('qty_in', $qty_in);
}

how to set selected multiple combo box based on array

I have two sets of array ,first array contains all categories called "all", and second array contains selected categories called "selected", I want to populate this concept to multiple combo box,
$all = [
0 => [
'id'=>1,
'name' => 'news'
],
1 => [
'id'=>2,
'name' => 'tips'
],
2 => [
'id'=>3,
'name' => 'trick'
],
3 => [
'id'=>4,
'name' => 'review'
]
];
$selected = [
0 => [
'id'=>2,
'name' => 'trick'
],
1 => [
'id'=>4,
'name' => 'review'
],
];
I've try to do foreach in foreach , but i have duplicated data when show in combo box, i want to have all data from "all" shown with selected data from "selected".
i just solved my problem in deferent way , first i add default pair of key and value "sel"=>0 in "all" array set, then i loop trough array "all" and array "sel" to get similar value and when it match change sel key to 1 ,this code for further explanation
public static function compare($sel,$all){
// add sel key with default value = 0
foreach($all as $k=>$v){
$all[$k]['sel'] = 0;
}
foreach($all as $k=>$v){
foreach($sel as $k2=>$v2){
// when match change sel to 1
if($v['id'] == $v2['id']){
$all[$k]['sel'] = 1;
}
}
}
return $all;
}
final result :
$all = [
0 => [
'id'=>1,
'name' => 'news',
'sel' => 0
],
1 => [
'id'=>2,
'name' => 'tips',
'sel' => 0
],
2 => [
'id'=>3,
'name' => 'trick',
'sel' => 1
],
3 => [
'id'=>4,
'name' => 'review',
'sel' => 1
]
];
just add if condition when $all['sel'] = 1 they should be selected, thanks all :D
You can get the intersection of both arrays with array_uintersect and a custom callback function (compare).
function compare($a, $b){
if($a['id'] == $b['id']){
return 0;
}
return 1;
}
$res = array_uintersect($selected, $all,"compare");
print_r($res);
>Array ( [0] => Array ( [id] => 2 [name] => trick ) [1] => Array ( [id] => 4 [name] => review ) )
After that you only need to loop through the final array and set the corresponding check boxes.
If you want to compare by name just create another callback function.
function compare2($a, $b){
if($a['name'] == $b['name']){
return 0;
}
return 1;
}
The duplicates are caused by the inner for loop continuing to create select elements even after it has found a selected element. You can avoid having an inner loop and using php's in_array() function to check if $all is in $selected like this:
$x = '';
foreach($all as $a){
if(in_array($a, $selected)){
$x .= '<option selected>'.$a['id'].'Selected </option>';
}else{
$x .= '<option>'.$a['id'].'Not selected </option>';
}
}
echo $x;
Note that in_array will check all values of the elements, so for example element with id 2 but different name will appear as not selected. You may want to change both names to tips. I hope that helps.

Laravel sync Relation with optional parameters

I use the sync function for syncing a belongsToMany Relation:
$model->products()->sync($productIds);
In the $productIds array there is flat array with some Id's -
something like this:
$productIds = [1,3,5,6];
What I want:
The pivot table has also additional columns like "created_by" and "updated_by".
But how can I add these fields to my array WITHOUT doing a foreach loop?
Is there a shorter way to do this?
I need an array like this:
$productIds = [1 => [
'created_by' => 1,
'updated_by' => 1
],3 => [
'created_by' => 1,
'updated_by' => 1
],5 => [
'created_by' => 1,
'updated_by' => 1
],6 => [
'created_by' => 1,
'updated_by' => 1
]];
Yes I know I can do it with foreach and add the columns while I loop through the array. But I want do it shorter.. is there a way to do it shorter (perhaps with laravel)?
It should be enough to pass what you have set in $productIds in your code example to sync().
This method works not only with array of integers. You can also pass an array where key is the synced ID and value is the array of pivot attributes that should be set for given ID.
This should do the trick:
$productIds = [
1 => [
'created_by' => 1,
'updated_by' => 1
]
//rest of array
];
$model->products()->sync($productIds);
Just make sure you have defined those fields as pivot fields in your relation definition.
In order to generate such table based on a list of IDs in $productIds you can do the following:
$productIds = array_fill_keys($productIds, array(
'created_by' => 1,
'updated_by' => 1,
));

PHP - backtrack through multi-dimensional array to check for recursion issues

I have a PHP array that outlines a parent-child relationships between objects, based on their ID. The array could potentially be infinitely deep, but the only rule is that "you may not add a child ID to a parent, where the child ID is a parent or grandparent (or great-grandparent etc etc) of said parent", in order to rule out recursive loops.
For example:
<?php
// Good relationship: 6 and 4 are children of 7, with 5 a child of 6 and so on
$good_relationship = [
'id' => 7,
'children' => [
[
'id' => 6,
'children' => [
[
'id' => 5,
'children' => [.. etc etc..]
]
]
],
[
'id' => 4,
'children' => []
]
]
];
// Badly-formed relationship: 6 is a child of 7, but someone added 7 as a child of 6.
$bad_relationship = [
'id' => 7,
'children' => [
[
'id' => 6,
'children' => [
[
'id' => 7,
'children' => [ ... 6, then 7 then 6 - feedback loop = bad ... ]
]
]
],
[
'id' => 4,
'children' => []
]
]
];
?>
I'm trying to write a function that checks for recursion issues when an ID is potentially added as a child to another ID. It would take in an ID ($candidate_id) and tries to add it as a child of another ID ($parent_id), and checks the existing array ($relationship) all the way back up the chain, and returns true if the candidate does not show up as a parent,grandparent,etc of $parent, and false if the candidate addition will cause a recursion issue by being added.
From the above $good_relationship, it would return true is I added ID 3 to ID 5, but false if I added ID 7 to ID 5.
Here's what I have so far, but I know it's way off - it's only checking for immediate grandparent of the candidate ID.
<?php
public function check_candidate($array, $candidate_id, $parent_id, &$grandparent_id = 0)
{
$reply_array = [];
foreach($array as $action)
{
// If $action['id'] is the same as the grandparent,
if($grandparent_id == $action['id'])
{
return false;
}
$grandparent_id = $action['id'];
if(isset($action['children']) && count($action['children']) >= 1)
{
$this->check_candidate($action['children'], $candidate_id, $parent_id, $grandparent_id);
}
}
return true;
}
?>
I've had a look at array_walk_recursive() in this case, but if $good_relationship has more than 1 element to it (which it always will), the callback will not know how 'deep' it is within the function, and it all becomes a bit of a nightmare.
Can anyone help me here?

Categories