Languages, APIs, Groups, Validation – Quick Admin Panel


We all use simple Route::get() and Route::post() syntax, but in bigger projects, it gets much more complicated. This article will compile various tips for different situations.

Tip 1. Route::get() BEFORE Route::resource()

For Resource Controllers, this is one of the most common mistakes, see this example:


Route::resource('photos', 'PhotoController');
Route::get('photos/popular', 'PhotoController@method');

The second route won’t be accurate, you know why? Because it will match show() method of Route::resource() which is /photos/{id}, which will assign “popular” as $id parameter.

So if you want to add any get/post route, in addition to Route::resource(), you need to put them BEFORE the resource. Like this:


Route::get('photos/popular', 'PhotoController@method');
Route::resource('photos', 'PhotoController');

Tip 2. Group in Another Group

We probably all know that we can group routes with Route::group() and assign different middlewares/prefixes and other parameters, like public routes and logged-in routes.

But what if you need a certain set of rules for sub-groups of those groups?

A typical example: you need public routes and authenticated routes, but within the authenticated group, you need to separate administrators from simple users.

So you can do this:


// public routes
Route::get('/', 'HomeController@index');

// Logged-in users - with "auth" middleware
Route::group(['middleware' => ['auth']], function () {

    // /user/XXX: In addition to "auth", this group will have middleware "simple_users"
    Route::group(['middleware' => ['simple_users'], 'prefix' => 'user'], function () {
        Route::resource('tasks', 'TaskController');
    });

    // /admin/XXX: This group won't have "simple_users", but will have "auth" and "admins"
    Route::group(['middleware' => ['admins'], 'prefix' => 'admin'], function () {
        Route::resource('users', 'UserController');
    });
});

Tip 3. Route Parameter Validation – Multi-Language Example

A pretty typical case is to prefix your routes by language locale, like fr/blog and en/article/333. How do we ensure that those two first letters are not used for some other than language?

We can validate it directly in the route, with “where” parameter:


Route::group(['prefix' => '{locale}', 'where' => ['locale' => '[a-zA-Z]{2}']], function () {
    Route::get('/', 'HomeController@index');
    Route::get('article/{id}', 'ArticleController@show');
});

The main part here is ‘where’ => [‘locale’ => ‘[a-zA-Z]{2}’] where we use a regular expression to match only two-letter combinations.


Tip 4. Dynamic Subdomain Routing

This comes directly from the official Laravel documentation, but rarely used so I think to mention it.

If you have a dynamic subdomain, like a different subdomain for every user, it needs to become a variable, right? Laravel has done that automatically for you. See example:


Route::domain('{account}.myapp.com')->group(function () {
    Route::get('user/{id}', function ($account, $id) {
        //
    });
});

Note that {account} automatically is passed as $account parameter in all inside controllers methods, so you need to accept that in all of them, don’t forget.


Tip 5. Be Careful with Non-English Route Model Binding

Sometimes, URLs should contain non-English words. For example, you have a Spanish portal for books and you want to have URL like /libros for the book list, and single book would have /libros/1, like a normal Resource Controller.

But in the database, all names should all be in English, so Laravel “magic” could work between singular and plural, right?

So if you generate a model Book with migration and Controller, you may have this command:


php artisan make:model Book -mcr

That -mcr key would generate a model and a resource controller, read more in this article. So, in that Controller, you would have this:


/**
 * Display the specified resource.
 *
 * @param  \App\Book  $book
 * @return \Illuminate\Http\Response
 */
public function show(Book $book)
{
    // ...
}

But in your routes/web.php, you would have this:


Route::resource('libros', 'BookController');

The problem is that it wouldn’t work. An even bigger problem is that it wouldn’t throw any error, just $book would be empty, and you wouldn’t understand why.

According to the official Resource Controller description, the name of the variable should be the same as a parameter in singular:


// So instead of
public function show(Book $book)
{
    // ...
}

// You should have
public function show(Book $libro)
{
    // ...
}

But, to be very honest, in projects with non-English projects, I would suggest to not use Route::resource and Route Model Binding at all. Too much “magic” is unpredictable, like how Laravel would “guess” that singular of “libros” is “libro”?


Tip 6. API Routes – from V1 to V2

Imagine you’re working with API-based project and you need to release a new version of this API. So older endpoints will stay at api/[something], and for new version you would use api/V2/[something].

The whole logic is in app/Providers/RouteServiceProvider.php:


public function map()
{
    $this->mapApiRoutes();

    $this->mapWebRoutes();

    // ...
}

protected function mapWebRoutes()
{
    Route::middleware('web')
        ->namespace($this->namespace)
        ->group(base_path('routes/web.php'));
}

protected function mapApiRoutes()
{
    Route::prefix('api')
        ->middleware('api')
        ->namespace($this->namespace)
        ->group(base_path('routes/api.php'));
}

As you can see, API routes are registered in a separate function with prefix api/.

So, if you want to create V2 route group, you can create a separate routes/api_v2.php and do this:


public function map()
{
    // ... older functions

    $this->mapApiV2Routes();
}

// And new function
protected function mapApiV2Routes()
{
    Route::prefix('api/V2')
        ->middleware('api')
        ->namespace($this->namespace)
        ->group(base_path('routes/api_v2.php'));
}

This way, old routes wouldn’t break, and you would just create a new set of routes.


Tip 7. Rate Limiting – Global and for Guests/Users

This also comes from official documentation, but with less-known details.

First, you can limit some URL to be called a maximum of 60 times per minute, with throttle:60,1.


Route::middleware('auth:api', 'throttle:60,1')->group(function () {
    Route::get('/user', function () {
        //
    });
});

But did you know you can do it separately for public and for logged-in users?


// maximum of 10 requests per minute for guests 60 for authenticated users
Route::middleware('throttle:10|60,1')->group(function () {
    //
});

Also, you can have a DB field users.rate_limit and limit the amount for specific user:


Route::middleware('auth:api', 'throttle:rate_limit,1')->group(function () {
    Route::get('/user', function () {
        //
    });
});

Tip 8. Route List and Route Caching

Final tip – how to check existing routes.

Not all of you know what routes exactly are hidden under Route::resource(), or under some more complex Route::group statement.

But at any point, you can check your actual route with Artisan command:


php artisan route:list

Keep in mind, that you are using route caching, after every change of your routes, you need to launch command:


php artisan route:clear



Cloud Software

Motivation

News
Berita Olahraga
Berita Olahraga
Anime Batch
News
Pelajaran Sekolah
Berita Terkini
Berita Terkini

Review Film

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *