Sorting array of collections - laravel

I have an array storing 3 merged collections:
public function answer() {
$interview = Interview::find(1);
$videoQuestions = $interview->VideoQuestions()->get();
foreach ($videoQuestions as $vq) {
$vq->type = 'video';
}
$textQuestions = $interview->TextQuestions()->get();
foreach ($textQuestions as $tq) {
$tq->type = 'text';
}
$uploadQuestions = $interview->UploadQuestions()->get();
foreach ($uploadQuestions as $uq) {
$uq->type = 'upload';
}
$questions = collect($videoQuestions);
$questions = $questions->merge($textQuestions);
$questions = $questions->merge($uploadQuestions);
$questions->sortBy('order_position');
dd($questions);
}
The ->sortBy is not working as I assume it doesn't work on an array of collections.
This is what I have:
But I really just want it to be a merged collection not an array inside the collection.
Each VideoQuestion or TextQuestion has an order_position field that I would like to sort by.
Any help is appreciated!

First thing collection is a class containing an array of elements with special methods to filter sort, docs:
The Illuminate\Support\Collection class provides a fluent, convenient
wrapper for working with arrays of data.
The sortBy method returns a sorted collection (it does not apply to the item it has):
$collection = collect([
['name' => 'Desk', 'price' => 200],
['name' => 'Chair', 'price' => 100],
['name' => 'Bookcase', 'price' => 150],
]);
$sorted = $collection->sortBy('price');
$sorted->values()->all();
/*
[
['name' => 'Chair', 'price' => 100],
['name' => 'Bookcase', 'price' => 150],
['name' => 'Desk', 'price' => 200],
]
*/
So you should have something like:
$questions = $questions->sortBy('order_position');
dd($questions);

This could be an alternative:
$interview = Interview::find(1)->with(['VideoQuestions',
'TextQuestions',
'UploadQuestions',
'Questions' => function($query){
$query->orderBy('order_position', 'asc');
}])->get();
Define the relationships and make a table Questions that holds an order_position that you can sort on instead of each table.

Related

How to Get specific keys of multi-dimensional array with Arr::only()

Can I use Arr::only() to get specific keys in multidimensional array ?
Dot notation doesn't work in this case:
use Illuminate\Support\Arr;
$array = ['name' => 'Desk', 'model.small.price' => 100, 'orders' => 10];
$slice = Arr::only($array, ['name', 'model.small.price']);
// ['name' => 'Desk', 'model.small.price' => 100]

add data to a Laravel collection

I'm trying to use Laravel collections for a simple case. I'm building a collection of furniture items and I would like to amend the item lines with additional information afterwards :
$collection = collect([
['name' => 'Desk', 'color' => 'Black'],
['name' => 'Chair', 'color' => 'Black'],
['name' => 'Bookcase', 'color' => 'Red'],
]);
I would like to add a 'stock_value' field for some items, based on the item key 'name' (for instance). In the end, I would like the collection to become something like :
['name' => 'Desk', 'color' => 'Black', 'stock_value' => 4],
['name' => 'Chair', 'color' => 'Black'],
['name' => 'Bookcase', 'color' => 'Red', 'stock_value' => 9]
I don't know how to achieve that. Any help would be greatly appreciated.
$stockValue = 9;
return $collection->transform(function ($array) use ($stockValue) {
if ($array['name'] === 'Desk') {
$array['stock_value'] = $stockValue;
}
return $array;
});
You may use transform to modify your collection.

Method for getting items from collection with specific key, value pair and also remove it from original collection in laravel

i was finding method for fetching item in collection by specific key and value, and i have found one which is where(),
But i also want to delete that item after fetching it from original collection, but i couldn't find any method which can do both.
i have seen pull() method but it fetches by key and removes that column from collection but i want to remove particular item only.
I have looked in collection doc, but couldn't find any native method so i am using 2 method to achieve that need, the code is as below:
$collection = collect([
['product' => 'Desk', 'price' => 200],
['product' => 'Chair', 'price' => 100],
['product' => 'Bookcase', 'price' => 150],
['product' => 'Door', 'price' => 100],
]);
$filtered_items = $collection->where('price', 100);
$filtered_collection = $collection->whereNotIn('price', [100]);
i am thinking that above 2 method will be little overhead if i can accomplish it in one method, so i am looking for other solutions guys, thanks beforehand your answers.
You need this one https://laravel.com/docs/5.5/collections#method-partition
$collection = collect([
['product' => 'Desk', 'price' => 200],
['product' => 'Chair', 'price' => 100],
['product' => 'Bookcase', 'price' => 150],
['product' => 'Door', 'price' => 100],
]);
$filtered_items = $collection->where('price', 100);
list($neededCollection, $filteredCollection) = $collection->partition(function ($i) {
return $i['price'] == 100;
});
dd($neededCollection, $filteredCollection);
result like this

Using isDirty in Laravel

I'm using the isDirty() method in my controller to check if any field is changed. Then I am saving the old data of a field and the new data in a table. The code is working fine; however, how can I optimize this code?
By using the below code, I will have to write each field name again and again. If request->all() has 20 fields, but I want to check six fields if they are modified, how can I pass only 6 fields in the below code, without repeating?
Controller
if ($teacher->isDirty('field1')) {
$new_data = $teacher->field1;
$old_data = $teacher->getOriginal('field1');
DB::table('teacher_logs')->insert(
[
'user_id' => $user->id,
'teacher_id' => $teacher->id,
'old_value' => $old_data,
'new_value' => $new_data,
'column_changed' => "First Name",
]);
}
You can set a list of what fields you want to be checking for then you can loop through the dirty fields and build your insert records.
use Illuminate\Support\Arr;
...
$fields = [
'field1' => 'First Name',
'field2' => '...',
...
];
$dirtied = Arr::only($teacher->getDirty(), array_keys($fields));
$inserts = [];
foreach ($dirtied as $key => $value) {
$inserts[] = [
'user_id' => $user->id,
'teacher_id' => $teacher->id,
'old_value' => $teacher->getOriginal($key),
'new_value' => $value,
'column_changed' => $fields[$key];
];
}
DB::table(...)->insert($inserts);
i tried following code after getting idea by lagbox in comments, and i have found solution to my problem.
$dirty = $teacher->getDirty('field1','field2','field3');
foreach ($dirty as $field => $newdata)
{
$olddata = $teacher->getOriginal($field);
if ($olddata != $newdata)
{
DB::table('teacher_logs')->insert(
['user_id' => $user->id,
'teacher_id' => $teacher->id,
'old_value' => $olddata,
'new_value' => $newdata,
'column_changed' => "changed",
]);
}
}

Session::instance() and push array data to session array

for example i have Session::instance()->get('orders') which is an array of some arrays:
$first = array('id' = 1, 'name' => 'first', 'price' => 100);
$second = array('id' => 2, 'name' => 'second', 'price' => 200);
$_SESSION['orders'] = array($first, $second);
but if i use this
Session::instance()->set('orders', array(array('id' => 3, 'name' => 'third', 'price' => 300)));
this will erase first orders (id 1, id 2).
so how can i ADD but not ERASE data arrays to session array named 'orders'? array_push or something else?
Edit, didn't see your comment, it's perfect.
Self explanatory.
$session = Session::instance();
// Find existing data
$data = $session->get('orders');
// Add new array
$data[] = array('id' => 3, 'name' => 'new data', 'price' => 300);
// Resave it
$session->set('orders', $data);
As for me, I think that best way:
public function before() {
...
$this->variable = Session::instance()->get('key', array());
...
}
some code...
public function after() {
...
Session::instance()->set('key', $this->variable, Date::MINUTE);
...
}

Resources