Laravel eloquent, how to order custom sortby - php

I have the following code that works fine:
$products = Product::like($search)->whereIn('id', $request->input('product_ids'))->skip($offset)->take($limit)->get(array('products.*'))->sortBy(function($product) use ($sort_order) {
$number = (isset($sort_order[$product->id])) ? $sort_order[$product->id] : 0;
return $number;
});
This returns the items in ascending order, how do I specify whether I want sortby to return the products in ascending or descending order?

//$order contains either 'asc' or 'desc'
$products = Product::like($search)->whereIn('id', $request->input('product_ids'))->skip($offset)->take($limit)->get(array('products.*'))->sortBy(function($product) use ($sort_order, $direction) {
$number = (isset($sort_order[$product->id])) ? $sort_order[$product->id] : 0;
return ($direction == 'asc') ? $number : -$number;
});

I really don’t understand the query. There’s a lot going on there that really shouldn’t be. For example:
You’re not selecting data from any other table, so why are you specifying all rows from the products table in the get() method (->get(array('products.*')))?
Why are you applying the ordering function to the returned collection instead of just applying the order clause to the query?
With the above, you query could be simplified to something like:
$productIds = [1, 2, 3, 4];
$direction = 'asc'; // or desc
$products = Product::like($search)
->whereIn('id', $productIds)
->skip($offset)
->take($limit)
->orderBy('id', $direction)
->get();
Also, you don’t need to manually specify the offset and limit if you use the paginate() helper method.

Just use sortBy for ASC (how you used it now) and sortByDesc for DESC.

Related

Operetion on child collection

I have code like this
$tag = Tag::where('slug' = $slug)->first();
$posts = $tag->posts;
It works correctly but I want to use limit, orderBy, offset and other operation on posts. So it works
$posts = $tag->posts->where('accept', 1);
But it doesn't works
$posts-> $tag->posts->orderBy('created_at', 'desc');
//or
$posts-> $tag->posts
->offset($offset)
->limit($limit);
I must use offset and limit into query from var.
How I can do that?
When you set up your initial query Tag::where('slug' = $slug)->first(); you're using Query Builder and it's methods. But when Laravel returns the results, they're returned as a collction object -- those have very similar but slightly different methods available. https://laravel.com/docs/5.8/collections#available-methods
On a collection or its children, instead of orderBy() you would use sortBy() or sortByDesc(). Those will return an instance of the collection, sorted by your specified key. $results = $posts->sortBy($sorting);
The same idea with limit, in this case you can use the splice method. (Collections are basically php arrays on steroids) Splice accepts two parameters, a starting index and a limit. So, to get only the first 10 items, you could do this: $results = $posts->splice(0, 10);
And of course, you can also chain those togeather as $results = $tag->posts->sortBy('id')->splice(0, 10);
When you use child, Eloquent create another subquery, then result is added to parent, thats way its not sorting properly.
A solution could be join tables:
$tags = Tag::where('tags.slug' = $slug)
->join('tags', 'tag.post_id', '=', 'posts.id')
->orderBy('posts.created_at', 'desc')
->select('tags.*')
->get();

Laravel 5.4 collection sortByDesc not working

I have two queries with Eloquent which I collect and merge and after i do sortByDesc but it's not sorting collection.
$f_games = collect(Game::with('fUser', 'sUser')->where('first_user_id', Auth::user()->id)>get());
$s_games = collect(Game::with('fUser', 'sUser')->where('second_user_id', Auth::user()->id)->get());
$response = $f_games->merge($s_games)->sortByDesc('id');
You can use values() at the end of sorting, as discussed in documentation
$gameCollection = collect($game);
$sorted = $gameCollection->sortByDesc('date');
return $sorted->values()->all();
In you case it should be
$response = $f_games->merge($s_games)->sortByDesc('id')->values();
There is no need to wrap in collect(), $f_games and $s_games will be collection without additional wrapping:
$f_games = Game::with('fUser', 'sUser')->where('first_user_id', Auth::user()->id)>get();
$s_games = Game::with('fUser', 'sUser')->where('second_user_id', Auth::user()->id)->get();
$response = $f_games->merge($s_games)->sortByDesc('id');
But the best way is:
$user_id = Auth::user()->id;
$f_s_games = Game::with('fUser', 'sUser')
->where('first_user_id', $user_id)
->orWhere('second_user_id',$user_id)
->orderBy('id', 'desc')
->get();
The sortByDesc method sorts the collection by field that belongs to some eloquent relation in your model.
If you are trying to sort collection with sortByDesc for model itself ( your current object of model), please user orderBy rather than sortByDesc
Example:
For model itself
{$collection_list = $this->model_name->orderBy('field_name','DESC')->get();}
For relations that will be lazy loaded in views
{$collection_list = $this->model_name->get()->sortBy('table_name.field_name', SORT_REGULAR, true);}
Note: sortByDesc internally called sortBy() with descending order,
true means Descending Order and false means Ascending Order.

Order pagination result in Symfony 2

I have a question about the order in the pagination in Symfony 2.7.
Before we used pagination we used the PHP function usort to sort some things. But my question now is how could we implement the usort in the doctrine query with the same order like the usort. Which needs to be working with the Paginator. Since when we use now the query (here under) we don't get the proper results.
usort function:
usort($result, function ($a, $b) {
$aBegin = $a->getStartDate() ?: $a->getCreatedDate();
$bBegin = $b->getStartDate() ?: $b->getCreatedDate();
if ($aBegin < $bBegin) {
return -1;
}
if ($aBegin == $bBegin) {
return 0;
}
if ($aBegin > $bBegin) {
return 1;
}
return 0;
});
How could we implemented the usort in the following query:
$build = $this->createQueryBuilder('building');
$build
->addSelect('users', 'furniture')
->join('building.users', 'users')
->leftJoin('building.furniture', 'furniture')
->where('building.id = :id')
->setParameter('id', $id)
->orderBy('building.getStartDate', 'ASC')
->addOrderBy('building.getCreatedDate', 'DESC');
$paginator = new Paginator($build->getQuery(), $fetchJoinCollection = true);
$result = $paginator->getQuery()
->setFirstResult($offset)
->setMaxResults($limit)
->getResult();
Thanks!
Doctrine orm: 2.2.3,
Symfony version: 2.7
To add such a condition, you can use a CASE expression in your select clause. You can write something like CASE WHEN b.startDate IS NULL THEN b.createdDate ELSE b.startDate END to have the behaviour described in your usort function.
That being said, you can't simply add this to your order by clause. You will need to select this value, give it an alias and then add an order by based on the newly selected value. Since you probably don't want to get a mixed result (where your entities would be mixed with scalar values), you can use the HIDDEN keyword to remove the computed field from the result set.
All put together, it could look like this:
// $qb your query builder with all your other parameters
$qb->addSelect('CASE
WHEN building.startDate IS NULL
THEN building.createdDate
ELSE building.startDate
END
AS HIDDEN beginDate');
$qb->orderBy('beginDate', 'DESC');
Note that while this works, you might encounter performance issues if you have a lot of entries in your table as the whole table is very likely to be scanned entirely for this query to be executed.

Laravel 5.4 Eloquent get x random records from collection where property is null

Learning eloquent/laravel. I have a collection:
$regions = Region::with('neighbors')
->join('cards', 'cards.id', '=', 'regions.risk_card_id')
->get();
I have a value or rows:
$regionsPerUser = 8;
I am doing this to pull random records:
$regions = $regions->random($regionsPerUser);
But I need to filter this selection where $regions->user_id is not null.
Is there a way to filter the random call as part of chaining?
I tried this which does not work:
$regions = $regions->whereNotNull('user_id')->random($regionsPerUser);
And I am wondering if there is a way to neatly do this in one chained statement as opposed to going down the path of filter / map.
Why not just add your conditions to a sql-query:
$regions = Region::with('neighbors')
->join('cards', 'cards.id', '=', 'regions.risk_card_id')
->whereNotNull('user_id')
->inRandomOrder()
->limit($regionsPerUser)
->get();
If you already have a collection then you can do something like:
$regions = $regions
->filter(function ($value) {
return !is_null($value['user_id']);
})
->random($regionsPerUser);

Get all() data and order by Ascending order and select

i want to get all user data and sort by ascending order then select required columns
$drivers = Driver::all()
->select('id','first_name','last_name','phone_number','registration_id')
->get();
now i'm getting all the data
thank you
In this case, remove all() and add an orderBy():
$drivers = Driver::select('id','first_name','last_name','phone_number','registration_id')
->orderBy('the-order-column', 'asc or desc')
->get();
The methods all() and get() do the same thing, except from that you can't modify the query using all() (like adding orderBy()).
Laravels documentation on orderBy(): https://laravel.com/docs/5.3/queries#ordering-grouping-limit-and-offset
To sort results, just use OrderBy.
For example, if you want to sort by first_name, use :
$drivers = Driver::select('id','first_name','last_name','phone_number','registration_id')
->orderBy('first_name', 'asc')
->get();
change 'asc' with 'desc' if you want descending order.
And don't use All() if you don't want everything.

Categories