First off, the issue/question arose with Laravel 5.6 and PHP 7.4.10. I know that 'magic strings' for controller calls have been deprecated as of recent Laravel versions, but I'm interested in the underlying infrastructure in this question.
PREREQUISITES
Suppose I have two folders: project and core.
projectcontains a freshly installed laravel project;corecontains some package-like structure of namespaces and classes (not actually a package in the laravel sense), which is being autoloaded by theproject.
For a minimal working example, you can see the last commit of the repository I published about this. I'm going to post the main files' contents here as well, but you can actually reproduce and play around with the sample projects yourself.
Basically the concept is pretty standard, much like a package: I want to have some reusable logic inside of my core with some default implementations. In the project I may or may not overwrite some of the functions, but for the sake of the question's simplicity, my core only defines a Fruit module (just a folder with a namespace) containig three files:
fruit_routes.php
<?php
Route::get('/pear', 'FruitController@pear');
Route::get('/apple', 'FruitController@apple');
FruitController(This is where the question is focused)
<?php
namespace CORE\Fruit;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class FruitController extends Controller
{
public function pear()
{
//if we call the action '\CORE\Fruit\FruitController@apple' instead, it works :)
return redirect()->action('FruitController@apple', ['message' => "No pears available! :( "]);
}
public function apple(Request $request)
{
return $request->message . "There you have an apple!";
}
}
FruitServiceProvider
<?php
namespace CORE\Fruit;
use Illuminate\Support\ServiceProvider;
class FruitServiceProvider extends ServiceProvider
{
public function register()
{
/*
* Map routes from our Fruit module to the controller's namespace
*/
$this->app->router->namespace('CORE\Fruit')->group(__DIR__."/fruit_routes.php");
}
}
And then in the project I simply include the following two lines:
composer.json
{
//..
"autoload":
//...
"psr-4": {
"App\\": "app/",
"CORE\\" : "../core"
}
},
}
config/app.phpin$providersarray
/*
* Package Service Providers...
*/
CORE\Fruit\FruitServiceProvider::class,
Again, for the full sample structure and contents, refer to the MWE repo.
THE QUESTION
Although the namespace for the controller classes has been explicitly declared from the FruitServiceProvider's register() method for the fruit_routes.php file, Laravel still fails to recognize that same namespace when using rediret()->action() from within those same controllers.
In the given FruitController you can see:
return redirect()->action('FruitController@apple');
and this will fail with Action App\Http\Controllers\FruitController@apple not defined.
As you can see, Laravel still searches for the controller inside the current project's default controller namespace (in this case: App\Http\Controllers)
Fortunately, passing the fully qualified name of the controller solves the issue:
return redirect()->action([self::class, 'apple']);
//Or alternativelly, but really clumsy:
//return redirect()->action('\Core\Fruit\FruitController@apple'); //notice the slash at the beginning
I have the suspicion that I must somehow also inform the redirect() helper (and actually, the underlying Redirect facade) that I want it to reference the controllers inside of my CORE\Fruit namespace whenever called from within that namespace... Or what would be the right approach? Is that desired behavior and am I missing something? And again, as mentioned in the beginning, I know that 'magic strings' are not the best practice and the most beloved feature of Laravel, but I really want to understand how things work in this scenario.
I also found this issue on github with a realted topic, but no answer there as well. As for people who are about to comment 'just avoid using magic strings', I'm stuck with a project I inherited and rewriting all of the core's magic string controller references is really not an option I would love to embrace.
Any insight is appreaciated! Many many thanks in advance!