Luís Henrique Faria
Luís Henrique Faria

Reputation: 1689

Extend Resource Controllers

I'm doing what I want in a certain way and I'm looking for alternatives or better ways of doing. I'm using Resource Controllers in my application. Also I'm using softdelete in several models, so my routes are as follows:

Route::get('users/deleted', array('uses' => 'UserController@trash'));
Route::put('users/{id}/restore', array('uses' => 'UserController@restore'));
Route::resource('users', 'UserController');

The first route is to display objects that have been deleted. The second allows me to restore these deleted elements. The third maps the traditional methods (create, edit, update, etc.).

I have several controllers that work exactly the same way, I wonder if have any way to tell laravel to work with this two methods (trash and delete) by default without the two extra lines.

Is it possible? Or to give a better way that I'm doing? (sorry for bad english)

Upvotes: 5

Views: 4398

Answers (3)

Pablo Papalardo
Pablo Papalardo

Reputation: 1312

Im using route model bind.

I do this way.

<?php

namespace App\Http\Router;

use Illuminate\Support\Facades\Route;
use Illuminate\Routing\Router;

class CustomRouter extends Router
{
    public function customResource($routeName, $controllerPath, $routeBindName = null, $routeBindModel = null) {
        $routeBindName = $routeBindName ?? $routeName;

        $routeBindNameTrashed = "{$routeBindName}_trashed";

        if($routeBindModel && $routeBindName) {
            Route::model($routeBindName, $routeBindModel);
            Route::bind($routeBindNameTrashed, function($modelId) use ($routeBindModel) {
                return app($routeBindModel)->newQuery()->onlyTrashed()->findOrFail($modelId);
            });
        }

        Route::put("$routeName/{{$routeBindNameTrashed}}/restore", "{$controllerPath}@restore")->name("{$routeName}.restore");
        Route::delete("$routeName/{{$routeBindNameTrashed}}/force", "{$controllerPath}@forceDelete")->name("{$routeName}.force-delete");
        Route::resource($routeName, $controllerPath);
    }
}

Alter bootstrap/app.php

$app->singleton(
    'router', 
    App\Http\Router\CustomRouter::class
);

Then usage:

Route::customResource('projects', 'ProjectsController', 'project', \App\Models\Project::class);

Route:list router:list laravel extends resource controllers

Upvotes: 1

Lasithds
Lasithds

Reputation: 2291

Works like a charm

$routes = [
    'posts' => 'PostController',
    'posts.comments' => 'PostCommentController',
];

foreach ($routes as $key => $controller)
{
    Route::get("$key/deleted", array('uses' => "$controller@trash"));

    Route::put("$key/{id}/restore", array('uses' => "$controller@restore"));

    Route::resource($key, $controller);
}

Upvotes: 0

Antonio Carlos Ribeiro
Antonio Carlos Ribeiro

Reputation: 87719

Keeping things simple and DRY.

You can extend the Router and replace the Route Facade in your app/config/app.php file but seems like a lot of work for not much gain, but don't forget that your routes file is a PHP script and you can do things like:

$routes = [
    ['users' => 'UserController'],
    ['posts' => 'PostController'],
];

foreach ($routes as $key => $controller)
{
    Route::get("$key/deleted", array('uses' => "$controller@trash"));

    Route::put("$key/{id}/restore", array('uses' => "$controller@restore"));

    Route::resource($key, $controller);
}

Extending the router

To extend the router you need to create 3 classes:

The Router extended, where you'll add your new methods:

<?php namespace App\Routing;

class ExtendedRouter extends \Illuminate\Routing\Router {

    protected $resourceDefaults = array(
                'index', 
                'create', 
                'store', 
                'show', 
                'edit', 
                'update', 
                'destroy', 
                'deleted',
                'restore',
    );

    protected function addResourceDeleted($name, $base, $controller)
    {
        $uri = $this->getResourceUri($name).'/deleted';

        return $this->get($uri, $this->getResourceAction($name, $controller, 'deleted'));
    }

    protected function addResourceRestore($name, $base, $controller)
    {
        $uri = $this->getResourceUri($name).'/{resource}/restore';

        return $this->get($uri, $this->getResourceAction($name, $controller, 'restore'));
    }

}

A Service Provider, to boot your new router, using the same IoC identifier Laravel uses ('router'):

<?php namespace App\Routing;

use Illuminate\Support\ServiceProvider;

class ExtendedRouterServiceProvider extends ServiceProvider {

    protected $defer = true;

    public function register()
    {
        $this->app['router'] = $this->app->share(function() { return new ExtendedRouter($this->app); });
    }

    public function provides()
    {
        return array('router');
    }

}

And a Facade, to replace Laravel's one

<?php namespace App\Facades;

use Illuminate\Support\Facades\Facade as IlluminateFacade;

class ExtendedRouteFacade extends IlluminateFacade {

    public static function is($name)
    {
        return static::$app['router']->currentRouteNamed($name);
    }

    public static function uses($action)
    {
        return static::$app['router']->currentRouteUses($action);
    }

    protected static function getFacadeAccessor() { return 'router'; }

}

Then you need to add your Service Provider and Facade to your app/config/app.php file, commenting the Laravel original ones.

Upvotes: 14

Related Questions