Laravel upgrade broke model paths - php

I've performed a long-overdue update on a Laravel project from v5.7 to v9 and ran into a Target class does not exist error. I found this guide and used the first method to resolve the error (adding the namespace into the RoutesServiceProvider.php boot function). This resolved that error but now, everything is giving me Class "App\Whatever" not found.
I did notice that models are now stored in a Models directory within the app directory rather than directly in app, so have moved them to Models. I figured that might be breaking my use App\Whatever; lines at the top of my controllers, so I've tried use App\Models\Whatever and also use app\Models\Whatever (since the "a" is lowercase in the directory name) but no effect.
I should note I don't really have a good grasp of namespaces, MVC frameworks etc. so ELI5 etc :-)
Some of my controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Thing;
use App\AnotherThing;
...
public function thing_summary($id) // show the summary view of the thing
{
if(Auth::check()) {
$thing = Thing::find($id);
...

Laravel 7/8/9 sticks with strict namespacing. When you move the models to a new directory, you need to update the namespace in the models themselves (if specified at all) and any file that has a use for the model. If the models move from app/ to app/models, the namespace must be changed to App/Models

Related

Omit default controller namespace for package controllers' `redirect()->action()` method

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.
project contains a freshly installed laravel project;
core contains some package-like structure of namespaces and classes (not actually a package in the laravel sense), which is being autoloaded by the project.
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.php in $providers array
/*
* 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!

How can I correctly use/import an interface in a php class?

I can't make interfaces work in my php/laravel project. I have a series of Controllers, which I want to implement an interface. And PhpStorm seems happy with my syntax. But I get a runtime error that the interface is not found, when I make a check if the current object indeed has the interface:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use iTeamEntryController;
class TeamController extends Controller implements iTeamEntryController {...}
and then the Interface:
<?php
use Illuminate\Http\Request;
interface iTeamEntryController
{
public function saveTeam(Request $request);
}
Here I get an error on the line defining the class, saying that interface 'iTeamEntryController' is not found. Both the class and the interface exists in the same folder.
I've been trying to find an example online, but everyone either has both the interface and the class declaration in the same file, or uses 'include_once' or 'require_once', which to me seems to be referring to files, and not OOP. So what am I doing wrong here? Why can't my interface be found?
Remember that the use clause wants you to specify an absolute class/interface/trait name, not relative to your current namespace.
The below is wrong, unless your controller is in a global namespace (which I'm sure isn't the case):
use iTeamEntryController; // wrong
And this - below - is correct:
use App\Http\Controllers\iTeamEntryController;
If you keep your interface in app/Http/Controllers directory, don't forget to specify a namespace:
namespace App\Http\Controllers;
If you want, on the other hand, your interface to be in a root directory, make sure it is there. Then, the namespace is not needed and your use clause is correct. Except, I'm pretty sure you don't want your interfaces in the root directory of your Laravel app.

Putting Laravel and Lumen controllers inside packages

I'm developing a package which has controllers in it and I want this package to be compatible with (or useable by) both Laravel and Lumen projects. My problem is Laravel controllers extend Illuminate\Routing\Controller and Lumen controllers extend Laravel\Lumen\Routing\Controller. The controller inside my package can't extend them both.
The only solution I've come up with is to have the controllers inside the package extend App\Http\Controllers\Controller.
But I see some problems:
App\Http\Controllers\Controller should exist; which means it wouldn't work if the App namespace is named differently.
The package is now "aware" that it is being included in something.
Testability: I can't test the controller independently because of the reference to App\Http\Controllers\Controller.
Is there a better way of doing this?
Edit 1
I'm finding other classes which are affected in a similar way. For example, the namespace of the trait Authorizable is Illuminate\Foundation\Auth\Access in Laravel while it is Laravel\Lumen\Auth in Lumen. I am using a model which uses that trait. How do I make my model compatible with both Lumen and Laravel?
Well, you could simply have two different files and classes wrapped in if statements and check for the corresponding classes to extend. So:
LaravelClass.php:
if(class_exists(Illuminate\Routing\Controller:class)){
class LaravelClass extends Illuminate\Routing\Controller {
use YourCodeTrait;
// any more code that is not in your trait
}
}
LumenClass.php
if(class_exists(Laravel\Lumen\Routing\Controller:class)){
class LaravelClass extends Laravel\Lumen\Routing\Controller {
use YourCodeTrait;
// any more code that is not in your trait
}
}
Loading both files will only load one of the classes. In the code above I use a trait to load the contents of your controller, assuming the contents is the same, you could use the same trait and not have to repeat yourself.
trait YourCodeTrait{
// whatever would normally go in your controllers
}

Laravel 5: organised models in subdirectory not found

I am trying to organise my app a bit better by putting models and controllers in subdirectories. I thought it didn't matter if they were in subdirectories as long as the namespace is correct, but now that I've moved them I'm getting a class not found error.
I have tried running composer dumpautoload several times, but it's still not working.
Here is my directory structure:
App
Models
Managers
EntryStructure.php
FieldManager.php
Controllers
EntryControllers.php
Since I have made the new directory Managers and moved those two models in there, When I reference the FieldManager class from EntryStructure, I get the not found error.
Code in EntryStructure.php:
namespace Pascall\ICMS\Models;
use Pascall\ICMS\Models\FieldManager;
class EntryStructure
{
function index(){
new FieldManager(); // class not found
}
}
Code in FieldManager.php:
namespace Pascall\ICMS\Models;
class FieldManager {
//
}
Why is it not finding the FieldManager class when it is explicitly referenced in the use statement and they share the same namespace?
Your use should be
use Pascall\ICMS\Models\Managers\FieldManager;
instead
use Pascall\ICMS\Models\FieldManager;
If your Models directory follow the PSR-4 specifications, the namespace in both of your classes should follow the class file path, so it should be:
namespace Pascall\ICMS\Models\Managers;
Then, in EntryStructure class you should use:
use Pascall\ICMS\Models\Managers\FieldManager;

Autoload Classmap in Laravel 4

I have a:
// administration
app/controller/admin/ProjectsController.php
And I want use too:
// public in website
app/controller/ProjectsController.php
But, in autoload_classmap.php, it's registred like that:
'ProjectsController' => 'app/controller/admin/ProjectsController'
So, If I want one more 'ProjectsController' for public views, how I need to do?
What is better? 2 controllers (admin and public), or one (hybrid).
Thanks.
You should namespace your Admin controllers.
That way it'll match up to PSR and autoloader will treat them differently.
namespace Admin;
At the top of your admin files.
Edit:
It may even be worth namespacing all your controllers and models.
So for your ProjectController in app\controllers you could put
namespace ProjectName
Then for everything in subfolders, e.g. app\controllers\admin
namespace ProjectName\Admin
and so on for other folders, and files.
This'll reduce the likely hood of your code clashing with anything else.
Edit: Edit:
After namespacing classes you'll need to reference classes and functions that are outside your namespace. For example Controller belongs to the global namespace so you put \ at the start of Controller.
The documentation here should help out a lot. PHP Namespaces

Categories