Adicionar novos métodos a um controlador de recursos no Laravel

141

Quero saber se é possível adicionar novos métodos a um controlador de recursos no Laravel e como você o faz.

Eu sei que esses métodos são o padrão (índice, criação, armazenamento, edição, atualização, destruição). Agora eu quero adicionar métodos e rotas adicionais ao mesmo controlador.

Isso é possível?

user2403824
fonte

Respostas:

261

Basta adicionar uma rota a esse método separadamente, antes de registrar o recurso:

Route::get('foo/bar', 'FooController@bar');
Route::resource('foo', 'FooController');
Joseph Silber
fonte
7
Observe que seus novos métodos precisam ir acima do ::resourcepadrão, caso contrário, você receberá uma mensagem de erro "Nenhum resultado de consulta para o modelo".
MPEN
Como você passaria um parâmetro como {id}? Atualmente, eu codifiquei meu método personalizado inline em routes.php (como o exemplo aqui em laravel.com/docs/5.1/routing#route-parameters ). Idealmente, eu gostaria de passar o parâmetro para executar no FooController.
ATutorMe
1
Encontrou o problema - não estava na sintaxe da rota. Na verdade, estava em algum Middleware personalizado que estou usando! Route :: get ('foo / {id} / bar', 'FooController @ bar'); passará o método id para bar ($ id). Obrigado!
ATutorMe
Alguém pode explicar por que a rota personalizada deve ir acima da rota do recurso ??? Eu fiz alguns testes e parece não ter nenhuma diferença entre colocar acima ou abaixo ...
Ricardo Vigatti
3
@RicardoVigatti - O recurso registra essa rota: Route::get('foo/{id}', ...). Isso engole todas as rotas começando com fooe tendo um segmento adicional, incluindo foo/bar.
Joseph Silber
32

Acabei de fazer isso, para adicionar um método GET "delete".

Depois de criar seus arquivos, você só precisa adicionar

'AntonioRibeiro\Routing\ExtendedRouterServiceProvider',

para 'provedores' em seu app / config.php

Edite o alias da rota neste mesmo arquivo:

'Route'           => 'Illuminate\Support\Facades\Route',

mudando para

'Route'           => 'AntonioRibeiro\Facades\ExtendedRouteFacade',

E verifique se esses arquivos estão sendo carregados automaticamente, eles devem estar em algum diretório que você possui no seu composer.json (seção "autoload").

Então você só precisa:

Route::resource('users', 'UsersController');

E este (veja a última linha) é o resultado se você executar php artisan routes:

lista de rotas Esses são meus arquivos de origem:

ExtendedRouteFacade.pas

<?php namespace AntonioRibeiro\Facades;

use Illuminate\Support\Facades\Facade as IlluminateFacade;

class ExtendedRouteFacade extends IlluminateFacade {

    /**
     * Determine if the current route matches a given name.
     *
     * @param  string  $name
     * @return bool
     */
    public static function is($name)
    {
        return static::$app['router']->currentRouteNamed($name);
    }

    /**
     * Determine if the current route uses a given controller action.
     *
     * @param  string  $action
     * @return bool
     */
    public static function uses($action)
    {
        return static::$app['router']->currentRouteUses($action);
    }

    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor() { return 'router'; }

}

ExtendedRouter.pas

<?php namespace AntonioRibeiro\Routing;

class ExtendedRouter extends \Illuminate\Routing\Router {

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

    /**
     * Add the show method for a resourceful route.
     *
     * @param  string  $name
     * @param  string  $base
     * @param  string  $controller
     * @return void
     */
    protected function addResourceDelete($name, $base, $controller)
    {
        $uri = $this->getResourceUri($name).'/{'.$base.'}/destroy';

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

}

ExtendedRouteServiceProvider.pas

<?php namespace AntonioRibeiro\Routing;

use Illuminate\Support\ServiceProvider;

class ExtendedRouterServiceProvider extends ServiceProvider {

    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = true;

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app['router'] = $this->app->share(function() { return new ExtendedRouter($this->app); });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return array('router');
    }

}
Antonio Carlos Ribeiro
fonte
3
Você pode fornecer o mesmo para o Laravel 5 e acima? Eu tenho tentado reproduzir isso ... Mas não consigo encontrar onde eles estão registrando o Accessor 'route' no contêiner.
TipuZaynSultan
por que o arquivo tem a extensão .pas?
Thiago Dias
22

Sim, é possível ..

No meu caso, adiciono method: data para manipular a solicitação de /data.json no método HTTP POST.

Foi o que eu fiz.

Primeiro, estendemos o Illuminate \ Routing \ ResourceRegistrar para adicionar novos dados de método

<?php

namespace App\MyCustom\Routing;

use Illuminate\Routing\ResourceRegistrar as OriginalRegistrar;

class ResourceRegistrar extends OriginalRegistrar
{
    // add data to the array
    /**
     * The default actions for a resourceful controller.
     *
     * @var array
     */
    protected $resourceDefaults = ['index', 'create', 'store', 'show', 'edit', 'update', 'destroy', 'data'];


    /**
     * Add the data method for a resourceful route.
     *
     * @param  string  $name
     * @param  string  $base
     * @param  string  $controller
     * @param  array   $options
     * @return \Illuminate\Routing\Route
     */
    protected function addResourceData($name, $base, $controller, $options)
    {
        $uri = $this->getResourceUri($name).'/data.json';

        $action = $this->getResourceAction($name, $controller, 'data', $options);

        return $this->router->post($uri, $action);
    }
}

Depois disso, faça seu novo ServiceProvider ou use AppServiceProvider .

Na inicialização do método , adicione este código:

    public function boot()
    {
        $registrar = new \App\MyCustom\Routing\ResourceRegistrar($this->app['router']);

        $this->app->bind('Illuminate\Routing\ResourceRegistrar', function () use ($registrar) {
            return $registrar;
        });
    }

então :

adicione à sua rota:

Route::resource('test', 'TestController');

Verifique por php artisan route:list E você encontrará novo método 'dados'

Mokhamad Rofi'udin
fonte
Sua solução funciona para mim. Você pode me dizer se descobriu algum problema depois de alguns meses usando esse código?
Ricardo Vigatti 19/10/16
Obrigado @RicardoVigatti. Eu usei isso desde o laravel 5.2 e agora eu uso isso no meu projeto recente com o L5.3 e não tenho nenhum problema. Deixe-me saber se você tem um, pode ser que eu possa ajudá-lo ..
Mokhamad Rofi'udin
bom, estou implementando isso no meu projeto 5.0. A solução parece muito consistente, mas, se ocorrer algum problema, será após algumas semanas.
Ricardo Vigatti 20/10
@ MokhamadRofi'udin Eu implementei sua solução, a nova rota é adicionada à lista de rotas, mas parece que o método correspondente não foi adicionado. Estou esquecendo de algo ?
Siavosh 22/11
@Siavosh basta escrever o seu método no controlador ou seja, eu dados método add (): `dados da função pública (Request $ request) {}`
Mokhamad Rofi'udin
13
Route::resource('foo', 'FooController');
Route::controller('foo', 'FooController');

Tente isso. Coloque métodos extras, como getData () etc. etc. Isso funcionou para mim para manter o route.php limpo

Hassan Jamal
fonte
Yas isso funciona. A documentação do Laravel 5.1 não mencionou que o Route :: resource e o Route :: controller podem ser usados ​​juntos, mas eles mencionaram sobre a suplementação de controladores de recursos. Então isso está confuso. Mas seus ans provou que estes são podem ser usados juntos
Amir
Sinto muito Hassan jamal, esses dois não estão trabalhando juntos. Então agora como posso definir um método de postagem personalizado em um controlador se eu usar apenas Route :: resource
Amir
3

Usando o Laravel> 5 Encontre o arquivo web.php na pasta routes e adicione seus métodos

Você pode usar route :: resource para rotear todos esses métodos, indexar, mostrar, armazenar, atualizar, destruir no seu controlador em uma linha

Route::get('foo/bar', 'NameController@bar');
Route::resource('foo', 'NameController');
Mazen Embaby
fonte
1

Basta adicionar um novo método e uma rota a esse método.

No seu controlador:

public function foo($bar=“default”)
{
      //do stuff
}

E nas suas rotas da web

Route::get(“foo/{$bar}”, MyController@foo”);

Apenas certifique-se de que o método no controlador seja público.

Patrick Lumenus
fonte
-1

Isso funciona muito bem também. Não há necessidade de adicionar mais rotas, basta usar o método show do controlador de recursos como este:

public function show($name){

 switch ($name){
   case 'foo':
    $this -> foo();
    break;
   case 'bar':
    $this ->bar();
    break; 
  defautlt:
    abort(404,'bad request');
    break;
 }

}
public function foo(){}
publcc function bar(){}

Eu uso o padrão para lançar a página de erro personalizada.

mdamia
fonte
2
Parece um cheiro de código real. Eu não gostaria que meus controladores lidassem com várias ações.
RonnyKnoxville