O framework Laravel 8 trás uma série de novos recursos, melhorias em funções já conhecidas e algumas alterações estruturais. Uma destas mudanças foi a remoção do “namespacing” de rota padrão, desta maneira tem gerado certa confusão e o erro “Target class [Controller] does not exist”.

Esta mudança é compatível com versões anteriores, o que significa que projetos mais antigos que usavam o Laravel 7.x podem facilmente migrar para o Laravel 8.x, sem alterar nada pois a estrutura básica de seus componentes é mantida, porém novos projetos criados no Laravel 8 devem levar isso em consideração.

O problema decorrente desta mudança em seus aplicativos Laravel 8 recém-criados, ao tentar carregar as rotas depara-se com uma exceção como:

Target class [MeuController] does not exist.

O problema não está relacionado necessariamente a um erro de código, mas a um ajuste necessário, ainda mais levando em conta que 99,9% dos tutoriais a respeito Laravel (ao menos até a versão 7) não dependia deste ajuste de namespace padrão para a configuração de rotas.

Corrigindo o erro “Target class XXX does not exist”

Até a versão 7 do framework Laravel, o arquivo RouteServiceProvider.php tinha o seguinte código na configuração da propriedade $namespace:

<?php

    protected $namespace = 'App\Http\Controllers';

...

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

Na leitura deste código: por padrão o Laravel configurava o Service para carregar as rotas em routes/web.php, usando o middleware web e o namespace App\Http\Controllers. Isso, por sua vez, significa que sempre que você declarar uma rota usando a sintaxe de string, o Laravel irá procurar pela classe controladora, considerando como “raiz” a pasta App\Http\Controllers. Como no exemplo da configuração de uma rota:

Route::get('artigos', 'ArtigosController@getAll');

Qual mudança ocorreu no Laravel 8? Simples porém impactante, a variável $namespace não é mais configurada por padrão no RouteServiceProvider, e a declaração/configuração do carregamento de rotas alterou para:

Route::middleware('web')
    ->group(base_path('routes/web.php'));

Isto significa que o Laravel está procurando por rotas dentro do seu arquivo web.php, como sempre. Também está aplicando o middleware da web, como sempre. Porém, observe que ele não está mais usando o namespace anterior.

Isso significa que a partir do Laravel 8, quando você declara suas rotas usando a sintaxe de string, o framework Laravel não vai procurar seu controlador dentro de App\Http\Controllers. E caso você utilize esta sintaxe, o erro de exceção Target class [Controller] does not exist será lançado.

Como corrigir este erro?

Primeiro vamos entender o problema gerado: o Laravel não sabe onde procurar seu controlador, então é necessário “informar” a ele onde a classe está.

Existem 3 maneiras de fazer isso:

  1. Adicionando o $namespace novamente na configuração para que você possa continuar usando as rotas como fazia no Laravel 7.x e versões anteriores
  2. Usar o namespace completo em seus arquivos de rota ao usar a sintaxe de string
  3. Usar a sintaxe de ação - action syntax(recomendado)

Adicionando a configuração do $namespace

Isso é bastante simples. Acesse o arquivo RoutesServiceProvider.php, primeiramente insira a linha a seguir no início da classe (ou retire o comentário se ela existir assim):

protected $namespace = 'App\Http\Controllers';

Em seguida adicione a chamada ao método namespace para cada configuração de rota na função boot. O resultado deve se parecer com o código a seguir:

public function boot()
{
    $this->configureRateLimiting();

    $this->routes(function () {
        Route::prefix('api')
            ->middleware('api')
            ->namespace($this->namespace) // <- esta linha deve ser inserida
            ->group(base_path('routes/api.php'));

        Route::middleware('web')
            ->namespace($this->namespace)  // <- esta linha deve ser inserida
            ->group(base_path('routes/web.php'));

        ...
    }

Desta maneira voltamos a configurar as rotas do Laravel para que ele considere o App\Http|Controllers a base para sua descoberta.

Usando o namespace completo

Esta alteração envolve alterar todas as suas declarações de rota. Apesar de trabalhoso é simples: prefixe os nomes dos seus controladores com seus namespaces. No exemplo a seguir para uma classe ArtigosController dentro da pasta app/Http/Controllers:

// note como adicionamos o namespace completo antes do nome da classe
Route::get('artigos', 'App\Http\Controllers\ArtigosController@getAll');

Usando a sintaxe de ação - action syntax

Esta é a alternativa mais recentemente recomendada por ser menos suscetível a erros de digitação e, em minha experiência, oferece melhor suporte ao IDE, pois informamos explicitamente ao código qual classe usar.

Portanto, ao invés de usarmos a sintaxe de string usual, podemos usar a sintaxe de ação em que especificamos a classe e o método a serem usados em uma matriz como parâmetro do método da rota sendo configurado:

// sintaxe de string
Route::get('artigos', 'App\Http\Controllers\ArtigosController@getAll');

// sintaxe ação. 
use App\Http\Controllers\ArtigosController;

Route::get('artigos', [ArtigosController::class, 'getAll']);

Note que para utilizar a sintaxe de ação é necessário importar a classe no arquivo de rotas, usando use App\Http\Controllers\ArtigosController, isto será necessário para todas as classes de controladores a serem utilizadas.

O atributo das classes PHP, ::class trará o namespace completo, juntamente com o nome da classe, desta maneira o Laravel saberá onde encontrar o controlador e irá executar o método passado na segunda da posição do array, em nosso exemplo o método getAll.

Considerações finais

Agora seu aplicativo deve funcionando corretamente. Caso ainda encontre problemas, sinta-se à vontade para pedir ajuda. Você pode me encontrar no Twitter como @nunomazer.

Se você adicionou o namespace manualmente, especificou-o completo em suas rotas, ou seguiu com a sintaxe de ação, o que você acabou de fazer foi dizer ao Laravel como encontrar seus controladores na estrutura da aplicação.