10

I'm working with Laravel 8 and when I write the route to the __invoke controller like this:

use App\Http\Controllers\PortfolioController;

Route::get('/portfolio', 'PortfolioController')->name('portfolio');

It shows this error:

Invalid route action: [PortfolioController]. PortfolioController is not invokable

So it only works like this:

Route::get('/portfolio', [PortfolioController::class, '__invoke'])->name('portfolio');;

Which doesn't make sense to me because it should find the __invoke which is the only one in PortfolioController.php:

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PortfolioController extends Controller
{
    /**
     * Handle the incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function __invoke(Request $request)
    {

      $portfolio = [

      ['title' => 'Project #1'],
      ['title' => 'Project #2'],
      ['title' => 'Project #3'],
      ['title' => 'Project #4'],

      ];

      return view('portfolio',compact('portfolio'));
    }
}

Is Laravel 8 ignoring the __invoke attribute???

Kenny Horna
  • 13,485
  • 4
  • 44
  • 71
Jota
  • 117
  • 1
  • 1
  • 7

2 Answers2

25

TL;DR

Do it like so:

use App\Http\Controllers\PortfolioController;

Route::get('/portfolio', PortfolioController::class)->name('portfolio');
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^

Explanation

Before Laravel 8, routes were namespaced in RouteServiceProvider.php:

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

// ...

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

So, when you defined routes, like in your example:

Route::get('/portfolio', 'PortfolioController')->name('portfolio');
                         ^^^^^^^^^^^^^^^^^^^^^

The PortfolioController string was namespaced with App\Http\Controllers.

However, since Laravel 8 this behaviour has been modified. From the v8 release note:

In Laravel 8.x, this property is null by default. This means that no automatic namespace prefixing will be done by Laravel. Therefore, in new Laravel 8.x applications, controller route definitions should be defined using standard PHP callable syntax:

use App\Http\Controllers\UserController;

Route::get('/users', [UserController::class, 'index']);

Now, for the particular case you mentioned, __invoke() methods, this is how you should handle them according to the docs:

When registering routes for single action controllers, you do not need to specify a method:

use App\Http\Controllers\ShowProfile;

Route::get('user/{id}', ShowProfile::class);
Kenny Horna
  • 13,485
  • 4
  • 44
  • 71
  • 1
    Thank you so much for this amazing explanation – Jota Oct 04 '20 at 21:13
  • 1
    Or you can use like this as well if you don't want to import all controller in the route file `$router->group([ 'namespace' => '\App\Http\Controllers'], function ($router) { $router->get('portfolio', [ 'as' => 'portfolio', 'uses' => 'PortfolioController::class', ]); ` }); – svikramjeet Oct 24 '20 at 06:10
0

another use , you can use the full path


Route::get('/portfolio', 'App\Http\Controllers\PortfolioController')->name('portfolio');

or call a specific method

Route::get('/portfolio', 'App\Http\Controllers\PortfolioController@foo')->name('portfolio')
dılo sürücü
  • 3,821
  • 1
  • 26
  • 28