Viewing Order By Array from Laravel Tidbits series.

Order By Array

Laravel Tidbits

In this video lesson we will cover how to create a new order by function; Order by Array, which allows us to control the order of our eloquent data through an array.

Video Information

Hi everyone, welcome back to Laracademy. It's Mickey again and in this lesson I am going to show you a little Eloquent sorting trick. In multiple projects that I have worked on I have always needed the ability to sort my data. Some times it is just an easy one column sort, but other times I need to be able to sort on multiple columns.

This example project I have create 500 users and I used faker to generate the user data. If we hit the page itself you can see it will just return the users in order of how they were created in the database.

Now if we wanted to sort the users by city then we could add that into our controller.

public function index()
{
  $users = User::orderBy('city')->get();

  return view('users', compact('users'));
}

Now our users are ordered by the city fields. But what if we wanted to add state, country, and name to our sort? Well we could code it like this.

public function index()
{
  $users = User::orderBy('city')
            ->orderBy('state')
            ->orderBy('country')
            ->orderBy('name')
            ->get();

  return view('users', compact('users'));
}

But to me that does not look right and does not feel right. Here is what we are going to do. We are going to move this sort into our model. We can do this by using a scope. Open up app\User.php.

public function scopeOrderByArray($query, $order)
{
  foreach($order as $column => $sort) {
    $query = $query->orderBy($column, $sort);
  }

  return $query;
}

Now we can save our scope and return to our controller. We can then either make a local variable, or if we are going to be using the same sort order in multiple methods on our controller we can define it at the top. I will just define it at the top.

protected $sortBy = [
  'city' => 'asc',
  'state' => 'asc',
  'country' => 'asc',
  'name' => 'asc',
];

And now we can replace our entire orderBy with our new scope.

$users = User::orderByArray($this->sortBy)->get();

If we return to our page, you can see everything is being sorted as expected. We have really cleaned up the controller. But what if we also had a client table and we wanted to also use this function in there? First let's look at our page so let's hit our /clients route. Now we don't want to copy the code over so we can use a trait. A trait allows us to reuse code in multiple classes. I will create a new folder called app\Traits. I think this is good for now. The folder structure is really up to you but we will work with this. Now we can create a trait and just throw our scope inside of it.

<?php
namespace App\Traits;

trait OrderByArrayTrait
{
  public function scopeOrderByArray($query, $order)
  {
    .....
  }
}

Now let's open up the Client.php model and tell it to use this trait.

use App\Trait\OrderByArray;

class Client
{
  use OrderByArray;

  // ....
}

Now we can switch to our ClientController and use the scope that we just added because of the trait.

protected $sortBy = [
  'first_name' => 'asc',
  'last_name' => 'asc',
];

public function index()
{
  $clients = Client::orderByArray($this->sortBy)->get()

  return view('clients', compact('clients'));
}

We can return to our page and refresh the view and now everything is sorted. The last thing we want to do is clean up the User model. We will remove the original orderByArray scope and bring in the trait.

use App\Traits\OrderByArray;

class User extends Authenticatable
{
  use OrderByArray;

  // .....
}

Now lets return to our user page and ensure everything is working. Now we have a clean Controller and also a clean model because of our trait. And the bonus is that this code can be reused on multiple models when needed.

Do you have a question? Go ahead and ask it below.

Please login to ask your question