this is the route from api.php :
Route::apiResource('friend-request', FriendRequestController::class)->except('delete');
the controller already exists and i'm calling it on a store using a post request :
FriendRequestController.php
class FriendRequestController extends ApiController
{
public function store(StoreFriendRequest $request, FriendRequestService $friendRequestService,UserService $userService)
{
//code
}
i get this error after calling this route
message: "Target class [App\Http\Controllers\App\Http\Controllers\FriendRequestController] does not exist."
i guess it's because of the form request what do you think ?
This is very probably because the $namespace property of the app/Providers/RouteServiceProvider.php file is uncommented.
It means that every controllers in your route file will have the default \App\Http\Controllers namespace prefixed.
So \App\Http\Controllers\FriendRequestController becomes \App\Http\Controllers\App\Http\Controllers\FriendRequestController and so on...
Since you are using the "new" route declaration notation (using the fully qualified namespace), you don't need a "default" namespace:
Route::apiResource('friend-request', FriendRequestController::class)->except('delete');
// FriendRequestController::class === \App\Http\Controllers\FriendRequestController
// However in the old declaration:
Route::apiResource('friend-request', 'FriendRequestController')->except('delete');
// You were only using the base class name, so the prefix was here to help Laravel find the "entry point" of your controllers
// which is \App\Http\Controllers\
All this is detailled in the migration guide: https://laravel.com/docs/8.x/upgrade#automatic-controller-namespace-prefixing
Related
Good day, and thank you for reading this problem
I have a problem where I'm using a different parameter but it doesn't work, here's the problem code
Route::get('/profiles','ProfilesController#index');
But when I'm using this code it worked perfectly fine
Route::get('/profiles',[ProfilesController::class, 'index']);
Here's the controller
class ProfilesController extends Controller
{
public function index()
{
return profiles::all();
}
}
You need to use full namespace App\Http\Controllers\ProfilesController#index
use App\Http\Controllers\ProfilesController;
// Using PHP callable syntax...
Route::get('/profiles', [ProfilesController::class, 'index']);
// Using string syntax...
Route::get('/profiles', 'App\Http\Controllers\ProfilesController#index');
If you would like to continue using the original auto-prefixed controller routing, you can simply set the value of the $namespace property within your RouteServiceProvider and update the route registrations within the boot method to use the $namespace property.
More info:
https://laravel.com/docs/8.x/upgrade#automatic-controller-namespace-prefixing
The problem
The default namespace for view components is App\View\Components with the folder being app/View/Components. I am setting up a DDD file structure and wish to do two things:
Move "shared" view components to a namespace and folder of App\ViewComponents and src/app/ViewComponents respectively
Have view components specific to individual "apps" with their own namespace and folder of App\MyApplication\ViewComponents and /src/app/MyApplication/ViewComponets respectively.
The new App namespace/folder setup is done via composer psr-4 autoload keys and works fine. But Laravel always used the App\View\Components namespace when trying to load components.
My attempt
I have solved the first part of my problem, but I am hoping that there is a better way. For instance when I want to move views, I can just set the view.paths config directive in my AppServiceProvider but I don't see a similar way of, essentially, adding namespaces to where Laravel looks for view components. So what I ended up doing was:
Create a ViewServiceProvider class, extending Illuminate\View\ViewServiceProvider::class and point to it in bootstrap/app.php instead
In there, override the registerBladeEngine method, in there pointing towards my own BladeCompiler class instead of the built-in one
public function registerBladeEngine($resolver)
{
// The Compiler engine requires an instance of the CompilerInterface, which in
// this case will be the Blade compiler, so we'll first create the compiler
// instance to pass into the engine so it can compile the views properly.
$this->app->singleton('blade.compiler', function () {
return new BladeCompiler(
$this->app['files'],
$this->app['config']['view.compiled'],
);
});
$resolver->register('blade', function () {
return new CompilerEngine(
$this->app['blade.compiler']
);
});
}
In my own BladeCompiler class, which extends Illuminate\View\Compilers\BladeCompiler, override the component() and compileComponentTags() methods - basically anywhere that referenced View\\Components - with pretty much a carbon copy but instead using ViewComponents and also made sure that where they return a Illuminate\View\Compilers\ComponentTagCompiler I instead referenced my own ComponentTagCompiler
In my own TagCompiler I override the guessClassName() method, again with essentially a carbon copy, just renaming View\\Components to ViewComponents
As you can see, that's quite a lot of work just to change the path. And I also want to add another path. Multiple "apps" run under the same Laravel codebase, so for instance we might have App\Website\, App\Admin and App\Blog and, depending on which app is currently running, load a different namespace for the running app, i.e. the blog would be App\Blog\ViewComponents pointing to src/app/Blog/ViewComponents.
Is there a way to achieve this without as much overriding as above? If not, can you suggest a way to achieve the second part of the requirement?
Note: I haven't ruled out using sub folders and continuing with everything under the main App\View\Components namespace just yet - I don't want to fight Laravel more than I have to and am willing to concede if there's no better way, but if I can achieve the folder structure I want it would feel a lot tidier.
Update; got a working implementation by using a configuration and php 8 annotations
Follow the steps below to make it possible to add more lookup folders for the blade view components feature, based on your question and details you have provided. It would have helped to have posted that code you already had. But I have added a possible solution to get it to work, using Annotations and using a config with a namespace/path map.
Depending on how you switch between one application and the other, from which the details are not provided in your question, you have to modify the way the configuration is retreived in the MyComponentTagCompiler class.
Blade compiler
In order to change the ComponentTagCompiler we need to change the BladeCompiler class:
namespace App;
class YourBladeCompiler extends \Illuminate\View\Compilers\BladeCompiler
{
protected function compileComponentTags($value)
{
if (! $this->compilesComponentTags) {
return $value;
}
return (new \App\MyComponentTagCompiler( //it is about this line
$this->classComponentAliases, $this->classComponentNamespaces, $this
))->compile($value);
}
}
Service provider
Now register the YourBladeCompiler in YourViewServiceProvider :
class YourViewServiceProvider extends \Illuminate\View\ViewServiceProvider
{
public function registerBladeEngine($resolver)
{
$this->app->singleton('blade.compiler', function () {
return new \App\YourBladeCompiler( //it is about this line
$this->app['files'],
$this->app['config']['view.compiled'],
);
});
$resolver->register('blade', function () {
return new CompilerEngine(
$this->app['blade.compiler']
);
});
}
}
MyComponentTagCompiler
This is an implemention I created that works with PHP 8 Attributes, given below:
namespace App;
#[\Attribute]
class ViewComponentName
{
public string $name;
public string $package;
public function __construct(string $name, string $package)
{
$this->name = $name;
$this->package = $package;
}
}
With this attribute, you can declare the package name and component name on the view component class (see example at the bottom). So during lookup the component can be matched on these parameters.
But you can change it to your own requirements if needed.
What it does:
It first let's Laravel lookup the View Component through it's own mechanisms, in the parent::componentClass method.
If no component is found and an exception (InvalidArgumentException) is thrown, after which my implementation will walk through the given paths and namespaces (from the getLookupPaths method) and see if an attribute matches the component name and package name. If so it returns this class and the view component is loaded accordingly.
namespace App;
use App\View\ViewComponentName;
use Illuminate\View\Compilers\ComponentTagCompiler;
class MyComponentTagCompiler extends ComponentTagCompiler
{
protected function getLookupPaths() : array
{
/*
* add some logic here to get an application specific configuration
* since you have multiple application in one, I cannot know it works in your
* application, since the details are not provided in the question
*/
return config('view_component_paths');
}
private function getFiles(string $dir) : array
{
return scandir($dir);
}
private function isPhpFile(string $file) : bool
{
return strpos($file, ".php");
}
private function getClassNamespace(string $file, string $folderNamespace) : string
{
$class = str_replace(".php", "", $file);
$classNamespace = $folderNamespace . "\\" . $class;
return $classNamespace;
}
private function getComponentName(string $file, string $namespace) : ?ViewComponentName
{
$classNamespace = $this->getClassNamespace($file, $namespace);
$reflection = new \ReflectionClass($classNamespace);
if(method_exists($reflection, 'getAttributes')) {
$attribute = $reflection->getAttributes()[0];
if ($attribute->getName() == ViewComponentName::class) {
return $attribute->newInstance();
}
}
return null;
}
public function componentClass(string $component)
{
try {
parent::componentClass($component);
} catch(\InvalidArgumentException $e) {
list($lookupComponentPackage, $lookupComponentName) = explode("-", $component);
foreach($this->getLookupPaths() as $namespace=>$dir) {
foreach ($this->getFiles($dir) as $file) {
if ($this->isPhpFile($file)) {
if($componentName = $this->getComponentName($file, $namespace)) {
if($componentName->name == $lookupComponentName && $componentName->package == $lookupComponentPackage) {
return $this->getClassNamespace($file, $namespace);
}
}
}
}
}
throw $e;
}
}
}
Where the config contains (config/view_component_paths.php):
return [
"App\\Test"=>__DIR__ . "/Test/"
];
If you wish to replace the default laravel behavior completely or do not like my implementation based on annotations, consider implementing your own version of the method:
public function componentClass(string $component)
{
//return the class name here based the component name
//without calling parent
dd($component);
}
Example view component
namespace App\Test;
use App\View\ViewComponentName;
use Illuminate\View\Component;
#[ViewComponentName('test', 'namespace')]
class MyViewComponent extends Component
{
public function render()
{
return view('components.test');
}
}
In blade:
<x-namespace-test />
It should now be working. I think this is enough information to give you an idea of how to implement this in your own application. There seems to be no other way than to extend some base classes. But looking at this answer, it is possible to create a high level implementation based on a global lookup configuration and php annotations (or some other mechanism you wish, for example converting the class name with namespace to a view component name).
Old answer
Problem 2 as defined in your question
Have view components specific to individual "apps" with their own namespace and folder of App\MyApplication\ViewComponents and /src/app/MyApplication/ViewComponets respectively.
Sadly there seems to be no way of defining multiple class paths for view components in Laravel. But you can however change the application path and namespace prefix. As far as I found out you only have to overwrite the following properties in the Application class.
bootstrap/app.php
Replace the following lines:
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
With:
class YourApplication extends \Illuminate\Foundation\Application
{
protected $namespace = "App\\MyApplication";
protected $appPath = __DIR__ . "/../app/MyApplication";
}
$app = new YourApplication(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
This is enough to change the app folder to another and gives you an idea of how to change it dynamically for having multiple apps in different namespaces. If you now run laravel commands like php artisan make:component Test1234 it is created in your new app folder: app/MyApplication/View/Components/Test1234.php.
Hardcoded paths
Some paths like View/Components are hardcoded in Laravel, and therefor not that easy to change. If you change as defined above, in this case the view components namespace becomes: App\MyApplication\View\Components and the path: app/MyApplication/View/Components.
Problem 1 as defined in your question
Move "shared" view components to a namespace and folder of App\ViewComponents and src/app/ViewComponents respectively
When you change application paths as explained above it is not possible to have a "shared" View Component folder. Laravel, as it seems, has only one default View Components path, which is based on hard coded paths and a dynamic namespace prefix as explained above. But you can of course, create a shared namespace and register the view components manually:
View component (app/ViewComponents/ folder)
namespace App\ViewComponents;
use Illuminate\View\Component;
class Test extends Component
{
public function render()
{
return view('components.test');
}
}
Don't forget the components.test blade view.
ServiceProvider
\Blade::component("shared-test",\App\ViewComponents\Test::class);
Blade
<x-shared-test />
I need help with using sub directory controllers in CodeIgniter 4.
I just can't make it work for some reason.
This is the URL for example: www.example.com/admin/dashboard
In the controllers folder, I created a folder named Admin, and a file named Dashboard.php.
I used this code in Dashboard.php:
namespace App\Controllers;
class Dashboard extends BaseController
{
public function index()
{
}
}
I tried changing the class name to AdminDashboard, Admin_Dashboard, pretty much every logical name but every time I get a 404 error, saying:
Controller or its method is not found:
App\Controllers\Admin\Dashboard::index
I know the file itself gets loaded successfully, but I think I don't declare the classname correctly and it keeps throwing me those 404 errors.
The documentation of CI4 isn't providing any information about what the classname should be called unfortunately...
UPDATE #1
I managed to make it work by changing few things:
namespace App\Controllers\Admin;
use CodeIgniter\Controller;
class Dashboard extends Controller
{
public function index()
{
}
}
But now it won't extend the BaseController which has some core functions that I built for my app.
Any ideas to how to make it extend BaseController?
I must admit that I don't have much knowledge about namespacing yet, so that might be the reason for my mistakes.
As I imagined, the problem was that I didn't learn about namespacing.
I needed to point the use line at the BaseController location.
namespace App\Controllers\Admin;
use App\Controllers\BaseController;
class Dashboard extends BaseController
{
public function index()
{
}
}
Now www.example.com/admin/dashboard/ goes directly to that index function, as intended.
php spark make:controller /Subfolder/ControllerName
$routes->add('/(.+?)_(.+?)/(.+?)$', 'subdir\\\\$1_$2::$3');
$routes->add('/(.+?)_(.+?)$', 'subdir\\\\$1_$2::index');
I was able to map with this setting.
The route mapping could be as simple as:
$routes->group('admin', static function ($routes) {
$routes->get('dashboard', 'Admin\Dashboard::index');
});
whenever we call a Facade Method it involves Facade design pattern and it called for some hidden class by using Facade. for instance for File, if we call
File::get(public_path().'test.txt');
this will call the method in class
Illuminate\Filesystem\Filesystem
and in this class we will have get($path) method.
Now my question is how Facade Abstract Class is related to File and Filesystem and where Laravel is telling them to call get in Filesystem. is there some kind of register which i am missing ?? i want to find complete link.
If you go in your config/app.php, you will notice that there's an array called aliases which looks like this
'aliases' => [
//
//
//
//
'File' => Illuminate\Support\Facades\File::class,
];
So, basically whenever you call File, the Service Container will try to resolve an instance of Illuminate\Support\Facades\File::class which is just a Facade.
If you look into Illuminate\Support\Facades\File::class, you will see that it contains only one method:
class File extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'files';
}
}
As you can see, it extends the Facade class and whenever a Facade is being resolved, Laravel will try to find a key in the Service Container that is equal to whatever is returned by getFacadeAccessor().
If you check the source of Illuminate\Filesystem\FilesystemServiceProvider, you will see this:
$this->app->singleton('files', function () {
return new Filesystem;
});
As you can see, the key files is being bounded to a FileSystem implementation. So, that's how Laravel knows how to resolve the File facade.
I'm trying to validate an email field on a Post request in Laravel 5.1.
On my Controller i have 'use App\Http\Requests;' but I get an error message 'Class App\Http\Controllers\StorePotentialUserRequest does not exist'. (For some reason it's looking for the class in the controller).
The class IS found when i have 'use App\Http\Requests\StorePotentialUserRequest;'. But I feel that this shouldn't be the case..
I also can't 'use App\Http\Requests\Request' because there is a conflict with 'use Illuminate\Http\Request'.
My code in the controller is as follows:
public function create(StorePotentialUserRequest $request)
{
...
EDIT: works if you namespace the code class as follows:
public function create(\App\Http\Requests\StorePotentialUserRequest $request)
{
...
But this is not mentioned on the documentation
Unless you are in the same namespace, you do have to specify where in the namespace are the classes you are using.
Either with use or with the full path every time you refer it.