How to sort Eloquent collection after fetching results? - laravel

In my controller I have this query
$query = Market::whereHas('cities', function($query) use ($city) {
$query->where('id', $city);
})->get();
Then I want to make a few operations with this collection and remove my subquerys from the main object
$return['highlighted'] = $markets->where('highlighted', true);
$markets = $markets->diff($return['highlighted']);
The problem is when I try to sort it by created_at
$return['latest'] = $markets->sortByDesc('created_at')->take(4);
$markets = $markets->diff($return['latest']);
It just won't work, keeps returning the first 4 objects order by id, I've tried parsing created_at inside a callback function with Carbon::parse() and strtotime with no results.
I'm avoiding at all cost to make 2 different database querys since the original $markets has all the data that I need.
Any suggestion?
Thanks

I think your problem is you try to sort and take in one-go and also missing a small thing when you try to access the values of the collection.
Try below approach:
$return['latest'] = $markets->sortByDesc('created_at');
$markets = $markets->diff($return['latest']->values()->take(4));
Or you may need to do it like:
$return['latest'] = $markets->sortByDesc('created_at');
$return['latest'] = $return['latest']->values()->take(4);
$markets = $markets->diff($return['latest']->all());

Related

Avoid overriding eloquent data

This is my collection of data I need:
$author_id = 12345;
$newsTagCollection = NewsTag::
->where('author_id', $author_id);
$postTodaycnt =$newsTagCollection->where('post_date', '>', \Carbon\Carbon::today())->get()->count();
$postWeekcnt =$newsTagCollection->where('post_date', '>', \Carbon\Carbon::now()->subDays(7))->get()->count();
dd($postWeekcnt);
$newsTags = $newsTagCollection->simplePaginate(12);
What I'm trying is to avoid calling the Model multiple times and just past post_date on postTodayCount and PostWeekCount. But $postWeekcnt seem to get value same as $postTodaycnt.
How do I not replace the value and get $postTodaycnt, $postWeekcnt and $newsTags value from $newsTagCollection as desired?
Thank you
Good question.
Answer to that is Optimization. Laravel provide Collection which is very helpful when it comes to such scenarios.
// This will return a *Collection* of NewsTag model.
$newsTagCollection = NewsTag::where('author_id', $author_id)->get(); // One query. Use ->get() here to fetch all the data
// Here no new queries will triggered.
$postTodaycnt = $newsTagCollection->where('post_date', '>', \Carbon\Carbon::today())->count(); // No need to use get() again
$postWeekcnt =$newsTagCollection->where('post_date', '>', \Carbon\Carbon::now()->subDays(7))->count();

How to use search value contain in array field using eloquent in Laravel

I'm working on laravel array serialize. Below is serialize in controller.
public function CreateSave(CreateTestTopicRequest $request){
...code..
$testtopic->class_room_id = $request->classroom;
$testtopic->roomno = serialize($request->roomno);
...code..
}
Then, roomno will be saved to database like.
a:2:{i:0;s:1:"1";i:1;s:1:"2";}
I would like to get result. For example class_room_id = 1 and roomno only contain in roomno array. I may use command to get all as below.
$testtopics = TestTopic::where('class_room_id',1)->get();
But, I do not know to get record only class_room_id = 1 and roomno contain in array. Any advice or guidance on this would be greatly appreciated, Thanks
You can use like search in json fields
TestTopic::where('class_room_id',1)->where('roomno', 'like', '%"id": 1%')->first()
When checking for an array of values the whereIn method can be used:
$roomno = 'a:2:{i:0;s:1:"1";i:1;s:1:"2";}';
$testtopics = TestTopic::where('class_room_id',1)
->whereIn('roomno', unserialize($roomno))
->get();
Multiple where statements can be combined by passing an array:
$roomno = 'a:2:{i:0;s:1:"1";i:1;s:1:"2";}';
$users = TestTopic::where([
['class_room_id', '=', '1'],
['roomno', '=', $roomno],
])->get();

Laravel Model::find() auto sort the results by id, how to stop this?

$projects = Project::find(collect(request()->get('projects'))->pluck('id')); // collect(...)->pluck('id') is [2, 1]
$projects->pluck('id'); // [1, 2]
I want the result to be in the original order. How do I achieve this?
Try $projects->order_by("updated_at")->pluck("id"); or "created_at" if that's the column you need them ordered by.
Referencing MySQL order by field in Eloquent and MySQL - SELECT ... WHERE id IN (..) - correct order You can pretty much get the result and have it order using the following:
$projects_ids = request()->get('projects'); //assuming this is an array
$projects = Project::orderByRaw("FIELD(id, ".implode(',', projects_ids).")")
->find(projects_ids)
->pluck('id'));
#Jonas raised my awareness to a potential sql injection vulnerability, so I suggest an alternative:
$projects_ids = request()->get('projects');
$items = collect($projects_ids);
$fields = $items->map(function ($ids){
return '?';
})->implode(',');
$projects = Project::orderbyRaw("FIELD (id, ".$fields.")", $items->prepend('id'))
->find($projects_ids);
The explanation to the above is this:
Create a comma separated placeholder '?', for the number of items in the array to serve as named binding (including the column 'id').
I solve this by querying the data one by one instead mass query.
$ids = collect(request()->get('projects'))->pluck('id');
foreach($ids as $id){
$projects[] = Project::find($id);
}
$projects = collect($projects);
$projects->pluck('id');
I have to do this manually because laravel collection maps all the element sorted by using ids.

Laravel Query Builder use multiple times

Is it possible to save a query bulider and use it multiple times?
for example, I have a model 'Tour'.
I create a long query buider and paginate it:
$tour = Tour::where(...)->orWhere(...)->orderBy(...)->paginate(10);
For example, 97 models qualify for the above query.
"Paginate" method outputs first 10 models qualifying for the query, but I also need to so some operations on all 97 models.
I don't want to 'repeat myself' writing this long query 2 times.
So I want something like:
$query = Tour::where(...)->orWhere(...)->orderBy(...);
$tour1 = $query->paginate(10);
$tour2 = $query->get();
Is that a correct way to do in Laravel? (my version is 5.4).
You need to use clone:
$query = Tour::where(...)->orWhere(...)->orderBy(...);
$query1 = clone $query;
$query2 = clone $query;
$tour1 = $query1->paginate(10);
$tour2 = $query2->get();
You can but it doesn't make any sense because every time a new query will be executed. So this code will work:
$query = Tour::where(...)->orWhere(...)->orderBy(...);
$tour1 = $query->paginate(10);
$tour2 = $query->get();
But if you want to execute just one query, you'll need to use collection methods for ordering, filtering and mapping the data. You'll also need to create Paginator instance manually:
$collection = Tour::where(...)->orWhere(...)->orderBy(...)->get();
$tour1 = // Make Paginator manually.
$tour2 = $collection;
$sortedByName = $collection->sortBy('name');
$activeTours = $collection->where('active', 1);

Laravel query optimization

I have a query in laravel:
...
$query = $model::group_by($model->table().'.'.$model::$key);
$selects = array(DB::raw($model->table().'.'.$model::$key));
...
$rows = $query->distinct()->get($selects);
this works fine and gives me the fields keys' that I need but the problem is that I need to get all the columns and not just the Key.
using this:
$selects = array(DB::raw($model->table().'.'.$model::$key), DB::raw($model->table().'.*'));
is not an option, cuz it's not working with PostgreSQL, so i used $rows to get the rest of columns:
for ($i = 0; $i<count($rows); $i++)
{
$rows[$i] = $model::find($rows[$i]->key);
}
but as you see this is it's so inefficient, so what can i do to make it faster and more efficient?
you can find the whole code here: https://gist.github.com/neo13/5390091
ps. I whould use join but I don't know how?
Just don't pass anything in to get() and it will return all the columns. Also the key is presumably unique in the table so I don't exactly understand why you need to do the group by.
$models = $model::group_by( $model->table() . '.'. $model::$key )->get();

Resources