andrews-quest's avatar

How to approach creating multiple versions of a ressource controller's index?

Hi, I have a bunch of calls, which I need to return sorted by different columns (/calls/datetime, /calls/operator etc.).

What'd be the correct approach in this scenario? Should the index() function of the CallController capture the URL parameter and base it's output based on that? Should it be split into multiple functions?

1 like
12 replies
andrews-quest's avatar

Isn't it for the sorting itself? If so, then that can also be done with vanilla Model functions, like orderBy().

What I'm asking is, what architecture for the routing should I build? Like, how to get a ressource controller to index() different contents based on the URL?

Correct me, If I have missunderstood your premise.

Tray2's avatar

You pass the sorting criteria into your controller via the url, and then look at them to set the order by.

Similar to this, but with order by instead of where

'books' => BookIndexView::query()
                    ->when($request['authors'], function ($query, $authors) {
                        $query->whereIn('author_id',
                            $this->numericStringToArray($authors));
                    })
                    ->when($request['published'], function ($query, $published) {
                        $query->where('published_year', $published);
                    })
                    ->when($request['genre'], function ($query, $genre) {
                        $query->where('genre', $genre);
                    })
                    ->when($request['format'], function ($query, $format) {
                        $query->where('format', $format);
                    })
                    ->when($request['search'], function ($query, $search) {
                        $query->where('title', 'LIKE',  "%$search%")
                        ->orWhere('author_name', 'LIKE', "%$search%")
                        ->orWhere('series', 'LIKE', "%$search%");
                    })
                    ->orderBy('author_name')
                    ->orderBy('series')
                    ->orderBy('part')
                    ->orderBy('published_year')
                    ->get(),
            ]);
1 like
andrews-quest's avatar

That seems to be exactly what I need, thanks!

But may I ask you something else related to the topic, namely how to pass query parameters (/calls&sort=datetime) to a ressource controller? This seems to be a basic question, but I'm really struggling to get hold of it.

Tray2's avatar

Simply pass it as an array on your link like so

route('books.index', ['genre' => $book->genre]
1 like
andrews-quest's avatar

Well, I've ended up pulling them out in the Controller like

class CallController extends Controller 
{
public function index (Request $request) {
	// ...
	datetime = $request->query('datetime');
	// ...
}
	// ...
}

Thank you for your advice!

jlrdw's avatar

Also @andrews-quest your search / query is what I meant here:

https://laracasts.com/discuss/channels/laravel/design-patterns-in-laravel?page=1&replyId=971681

When I said I might have a class I call for a complex search, just to keep the controller lean.

Yes it could go in the model, but for complex search criteria I like a little special class. But just a suggestion. Also there is nothing wrong with a "blump" model. But I do prefer a lean controller.

1 like
andrews-quest's avatar

It seems to be a clever approach, thanks. Maybe, if you have an example of such class or a whole architecture that uses it, it would be helpfull to take a look.

martinbean's avatar

Hi, I have a bunch of calls, which I need to return sorted by different columns (/calls/datetime, /calls/operator etc.).

What'd be the correct approach in this scenario? Should the index() function of the CallController capture the URL parameter and base its output based on that? Should it be split into multiple functions?

@andrews-quest You would use a query string parameter here to control how you want records sorting, i.e.

  • /calls?sort=datetime
  • /calls?sort=operator

You can use Spatie’s Laravel Query Builder package as mentioned by @glukinho to implement sorting (https://spatie.be/docs/laravel-query-builder/v6/features/sorting):

class CallController extends Controller
{
    public function index()
    {
        $calls = QueryBuilder::for(Call::query())
            ->allowedSorts([
                'datetime',
                'operator',
                // Any other column you want to be able to sort by...
            ])
            ->paginate();

        return view('calls.index', compact('calls'));
    }
}
1 like
andrews-quest's avatar

Oh, I see. I'll stick with the out-of-the-box solution for now, but will definetely look further into this approach on latter stages.

martinbean's avatar

@andrews-quest What “out-of-the-box” solution? There isn’t one; you’d have to parse the query string and manually add the clauses yourself:

public function index(Request $request)
{
    $query = Call::query();

    $query = match ($request->query('sort')) {
        'datetime' => $query->orderByDesc('datetime'),
        'operator' => $query->orderByDesc('operator'),
        default => $query,
    };

    $calls = $query->paginate();

    return view('calls.index', compact('calls'));
}

This also doesn’t deal with sorting directions (ascending/descending) like Spatie’s package will.

andrews-quest's avatar

Yes, that's the solution I meant. That's how it works in my app for now.

But you mentioning the sorting directions made me ponder, maybe I should really switch to Spatie as soon as possible.

Thanks a lot!

1 like

Please or to participate in this conversation.