I got the following collection:
$this->items = collect([$productId => [
'name' => $product->title,
'price' => $product->price,
'is_sale' => $product->is_sale,
'sale_price' => $product->sale_price,
'sale_percent' => $product->sale_percent,
'can_use_promocode' => $product->can_use_promocode,
'qty' => 1,
]);
]);
How to search an item with key? In documentation (https://laravel.com/docs/5.2/collections) I dont see any methods for it
UPD: For example, user added an item to cart ($this->items). I want to check existence of item in cart (need to do it with key). Analog for php function array_key_exists, but for collections.
use has()
if($this->items->has('key_name_to_check')){
///your task if exists
}
You can do this:
$this->items->toArray()[$key]
Or you can use the first() method:
$this->items->first(function($i, $k) use($key) {
return $key === $k;
});
Update
If you just want to know if an item with given key exists in the collection, you can use the offsetExists() method.
The offsetExists() method is full analog of array_key_exists(), because all it does is this:
return array_key_exists($key, $this->items);
Related
I need to sum the price values of all rows where the optional check element exists.
Sample data:
[
6254 => [
'check' => 'on',
'quantity' => 2,
'name' => 'Testing product_special One Size',
'total' => 15.9,
'price' => 33.0000,
'totalken' => 33075.9,
],
6255 => [
'quantity' => 1,
'name' => 'Testing card',
'total' => 113.85,
'price' => 33.0000,
'totalken' => 16537.95,
],
6256 => [
'check' => 'on',
'quantity' => 1,
'name' => 'Testing food',
'total' => 113.85,
'price' => 33.0000,
'totalken' => 16537.95,
],
]
I tried array_sum(array_column($value, 'price')) but this sums all price values regardless of the check value.
Expected result: 66
I would use array_reduce in this case.
array_reduce loops through the array and uses a callback function to reduce array to a single value.
<?php
$totalPrice = array_reduce($myArray, function ($accumulator, $item) {
// I'm checking 'check' key only here, you can test for 'on' value if needed
if (isset($item['check'])) {
$accumulator += $item['price'];
}
return $accumulator;
});
You can simply filter the array based on condition by using array_filter() function of php.
You can see the usage of array_filter() here.
Here is my solution for you if you want to use condition.
$filteredByCheck = array_filter($value, function ($val){
return isset($val['check']);
});
$total = array_sum(array_column($filteredByCheck, 'price'));
The quickest method would be just to loop over the array and maintain a sum, use ?? '' to default it to blank if not set...
$total = 0;
foreach ($value as $element ) {
if ( ($element['check'] ?? '') == "on" ) {
$total += $element['price'];
}
}
#aliirfaan's implementation of array_reduce() can be modernized and compacted as the following snippet.
Code: (Demo)
echo array_reduce(
$array,
fn($result, $row) =>
$result + isset($row['check']) * $row['price']
);
The above uses arrow function syntax which is available since PHP7.4. The mathematics in the return value of the custom function multiplies the price value by the true/false evaluation of isset() on the check column. The boolean value is coerced to an integer automatically when used with arithmetic -- false is zero and true is one. If the coercion is not to your liking, you can explicitly cast the boolean value using (int) immediately before isset().
I have used laravel 5.6 and used the updateOrCreate model to add or update some data.
But I need to get all the values which changed
$q=Userssub::updateOrCreate(
['userid' => $uid ],
['model' => $model]
);
and the result shows like in this image
How can I get the changes array?
I tried to get it with
$u->changes
and
$u->changes->toarray()
but both return null.
What can I do to get the changed values?
Eloquent models have two protected arrays, $original and $changes, which contain the attributes as they were when fetched from storage and the attrbirutes which have been modified, respectively.
So you can use getOriginal() and getChanges() and compare the differences.
$model = Model::createOrUpdate([...]);
// wasRecentlyCreated is a boolean indicating if the model was inserted during the current request lifecycle.
if (!$model->wasRecentlyCreated) {
$changes = $model->getChanges();
}
This creates an array which will contain the original attribute value and what it was changed to:
if (!$model->wasRecentlyCreated) {
$original = $model->getOriginal();
$changes = [];
foreach ($model->getChanges() as $key => $value) {
$changes[$key] = [
'original' => $original[$key],
'changes' => $value,
];
}
}
e.g.
(
[first_name] => [
[original] => Kevinn
[changes] => Kevin
]
[website] => [
[original] => google.com
[changes] => google.ca
]
)
I need help with symfony validator. It is possible validate only specific values in array? For example I have array:
'0' => [
'interestidKey' => true,
'anotherInterestedKey' => 'foo'
],
'error' => [
'errorMsg => 'not interest for me'
]
I need to validate this array with validator mainly value 0. I need know if array contains '0' key and inside if is key interestidKey with boolean value. I always use Collection for array but it not work in this case because ofc shows me an error that error does not contain interestidKey.
How can I fix this?
I'm not sure you will be able to do what you want with the shipped out of the box constraints. But you should be able to do what you want by writing your own: https://symfony.com/doc/current/validation/custom_constraint.html have a look at that and see if it can help you.
if you're validate you could do:
if(array_key_exists(0, $array)) {
if(array_key_exists("interestid", $array[0])) {
return true;
}
} else {
// do the error stuffs
}
You could just build a loop over your array, check for the key and if it is a numeric key (or not the error key) apply your validation to the children. That would look like this:
use Symfony\Component\Validator\Constraints as Assert;
...
$constraint = new Assert\Collection([
'fields' => [
// put any constraints for your objects here, keyed by field name
'interestidKey' => new Assert\Type('bool')
],
'allowExtraFields' => true // remove if you don't want to allow other fields than specified above
]);
$violations = [];
foreach($data as $key => $item) {
if ($key != 'error') {
$violations[$key] = $validator->validate($item, $constraint);
}
}
Given following collection/array:
[
"somename" => "test.test.be"
"anothername" => "test"
"yetanothername" => "testing"
"extrafield" => "extra",
"extrafield" => "extra",
]
When i retrieve this collection i always know the order of them, but i will not know the key-names. So what i want to do is transform this collection and change the keynames to my defined values.
For a non-associative array i would do something like
$trimmedCollection->transform(function ($item) {
return [
'email' => $item[0],
'first_name' => $item[1],
'surname' => $item[2],
];
});
But how would i handle this for the given collection? Also what to do with overflow items. Say i suddenly got 10 key-value pairs but only wrote a transform for 3 how would i transform all the overflow to a default key?
Edit:
For the overflow items i would like to assign all extra fields in the given array to be stored like so.
Below would be the final array:
[
"email" => "test.test.be"
"first_name" => "test"
"surname" => "testing"
"additional_fields" => ["key-name" => "extra","key-name" => "extra"]
]
Where the key-name is the original name of the key i retrieved.
You can use array_shift to remove the 1st element in the array for every known element, and add the remaining array to your additional_fields key:
$trimmedCollection->transform(function ($item) {
return [
'email' => array_shift($item), //$item[0]
'first_name' => array_shift($item), //$item[1]
'surname' => array_shift($item), //$item[2]
'additional_fields' => $item //all remaining items
];
});
You could do something like this to transform your selected keys. This retains the other values with their unchanged keys.
function replace_array_key(array &$item, $oldKey, $newKey)
{
$item[$newKey] = $item[$oldKey];
unset($item[$oldKey]);
}
$trimmedCollection->transform(function ($item) {
replace_array_key($item, 'somename', 'email');
replace_array_key($item, 'anothername', 'first_name');
replace_array_key($item, 'yetanothername', 'surname');
return $item;
});
You can even extend this to an array list of old and new key names and run it through and arrap_map.
Official Laravel documentation has this on sync() function:
$user->roles()->sync( array( 1, 2, 3 ) );
You may also associate other pivot table values with the given IDs:
$user->roles()->sync( array( 1 => array( 'expires' => true ) ) );
In the latter example only a single pivot row is being added. What I don't understand is how to associate other pivot table records if there are more than one rows to be synced?
In order to sync multiple models along with custom pivot data, you need this:
$user->roles()->sync([
1 => ['expires' => true],
2 => ['expires' => false],
...
]);
Ie.
sync([
related_id => ['pivot_field' => value],
...
]);
edit
Answering the comment:
$speakers = (array) Input::get('speakers'); // related ids
$pivotData = array_fill(0, count($speakers), ['is_speaker' => true]);
$syncData = array_combine($speakers, $pivotData);
$user->roles()->sync($syncData);
This works for me
foreach ($photos_array as $photo) {
//collect all inserted record IDs
$photo_id_array[$photo->id] = ['type' => 'Offence'];
}
//Insert into offence_photo table
$offence->photos()->sync($photo_id_array, false);//dont delete old entries = false
There is now a ->syncWithPivotValues($ids, $pivotValues) method available if you want to set the same pivot value for all synced items.
Example from the doc:
$user->roles()->syncWithPivotValues([1, 2, 3], ['active' => true]);
Attaching / Detaching
Eloquent also provides a few additional helper methods to make working with related models more convenient. For example, let's imagine a user can have many roles and a role can have many users. To attach a role to a user by inserting a record in the intermediate table that joins the models, use the attach method:
$user = App\User::find(1);
$user->roles()->attach($roleId);
When attaching a relationship to a model, you may also pass an array of additional data to be inserted into the intermediate table:
$user->roles()->attach($roleId, ['expires' => $expires]);
You can also use Sync if you want to remove old roles and only keep
the new ones you are attaching now
$user->roles()->sync([1 => ['expires' => $expires], 2 => ['expires' => $expires]);
The default behaviour can be changed by passing a 'false' as a second
argument.
This will attach the roles with ids 1,2,3 without affecting the existing
roles.
In this mode sync behaves similar to the attach method.
$user->roles()->sync([1 => ['expires' => $expires], 2 => ['expires' => $expires], false);
Reference:
https://laravel.com/docs/5.4/eloquent-relationships
Add following trait to your project and append it to your model class as a trait. This is helpful, because this adds functionality to use multiple pivots.
Probably someone can clean this up a little and improve on it ;)
namespace App\Traits;
trait AppTraits
{
/**
* Create pivot array from given values
*
* #param array $entities
* #param array $pivots
* #return array combined $pivots
*/
public function combinePivot($entities, $pivots = [])
{
// Set array
$pivotArray = [];
// Loop through all pivot attributes
foreach ($pivots as $pivot => $value) {
// Combine them to pivot array
$pivotArray += [$pivot => $value];
}
// Get the total of arrays we need to fill
$total = count($entities);
// Make filler array
$filler = array_fill(0, $total, $pivotArray);
// Combine and return filler pivot array with data
return array_combine($entities, $filler);
}
}
Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Example extends Model
{
use Traits\AppTraits;
// ...
}
Usage:
// Get id's
$entities = [1, 2, 3];
// Create pivots
$pivots = [
'price' => 634,
'name' => 'Example name',
];
// Combine the ids and pivots
$combination = $model->combinePivot($entities, $pivots);
// Sync the combination with the related model / pivot
$model->relation()->sync($combination);
Simply just append your fields and their values to the elements:
$user->roles()->sync([
1 => ['F1' => 'F1 Updated']
]);
$data = array();
foreach ($request->planes as $plan) {
$data_plan = array($plan => array('dia' => $request->dia[$plan] ) );
array_push($data,$data_plan);
}
$user->planes()->sync($data);
Putting this here in case I forget it later and Google it again.
In my case I wanted the extra column to have the same data for each row
Where $syncData is an array of IDs:
$syncData = array_map(fn($locationSysid) => ['other_column' => 'foo'], array_flip($syncData));
or without arrow
$syncData = array_map(function($locationSysid) {
return ['ENTITY' => 'dbo.Cli_Core'];
}, array_flip($syncData));
(array_flip means we're using the IDs as the index for the array)
foreach ($request->exercise_id as $key => $exercise_id) {
$data_plan[$exercise_id] = [
'serie' => $request->serie[$key],
'observation' => $request->observation[$key],
];
}