I have a laravel 4.1 application, and I've created a folder in my app folder to store most of the logic.
/app/Acme/Models/
/app/Acme/Repositories/
these are the two main folders.
In my composer.json I have this in the auto load, and done a dump run.
"psr-4" : {
"Acme\\" : "app/Acme"
}
However I am getting, what I think are silly issues. For example my Acme/Models/Task.php has the following
<?php
namespace Acme\Models;
class Task extends \Eloquent {
public function job()
{
return $this->belongsTo('Job');
}
}
however when I run this, I get an error
Fatal error: Class 'Task' not found in vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php on line 780
In my Job.php I have the same namespace at the top of the file....
Must I manually import/use object which are in the same name space?
use Acme/Models/Job as Job; ? it seems like such a duplicate...
And in my Repositories folder when I set a namespace of namespace Acme/Repositories;, must I use items like
use Acme\Models\Job as Job;
I is, a bit lost!
Namespaces are relative so you do not need to add use to directly reference classes within the same namespace.
The error you are getting is because you need to fully qualify relationships to namespaced models so eloquent knows where to find them eg
$this->belongsTo('\Acme\Models\Jobs');
In the case of your repository namespace, you will need to add a use statement in your file as you suggested, or reference the fully qualified namespace eg new \Acme\Models\Job();
On a side note, I know PHPStorm (and I'm sure other IDEs) will inject the namespaces for you which is super useful and saves you having to write use or the full namespace out every time you reference a class - worth checking out.
Edit: Sorry, I didn't read the question properly first time around - updated my answer.
Related
I am having a problem with my namespace fallbacks and using PSR-4 loader in Composer.
What I am trying to do is this:
Have a core which can overwritten / extended.
The core is based off an interface.
The directory structure is like so:
site/app/View/Example.php
site/src/ACME/app/View/Example.php
site/src/ACME/app/Interface/View.php
I am not set on this configuration so if you have a better suggestion then go for it.
My composer json is like so for psr-4:
"autoload": {
"psr-4": {
"ACME\\App\\Site\\" : "app/",
"ACME\\App\\" : "src/AMCE/app/"
}
}
I thought this would make ACME\App\Site\View fallback to ACME\App\View if the site one was not found (Note I haven't done the interface part yet...).
My code for site/app/View/Example.php is like so:
namespace ACME\App\Site\View;
class ViewExample extends View {
Which works, when I have site/app/View/View.php as well. That looks like:
namespace ACME\App\Site\View;
class View extends \ACME\App\View\View {
The site/src/app/View/View.php look like this:
namespace ACME\APP\View;
class View {
This one should use the interface (I haven't tried yet).
So what I really want to do is make it so I don't have to have site/app/View/View.php, and I don't have to have site/app/View/Example.php - it can use site/src/ACME/app/View/Example.php.
Sorry I'm new to namespaces so I may not of phrased it very well.
What I am getting at is I thought ACME\App\Site would fallback to ACME\App - it doesn't? Or I am doing it wrong? At the moment it needs all the files in place.
Edit: Turns out I was originally wrong, it is possible to get your example working with PSR-4! You just need to specify an array of directories for namespaces that can be loaded from different places.
Easy solution
{
"autoload": {
"psr-4": {
"ACME\\App\\Site\\": ["app/", "src/ACME/app"],
"ACME\\App\\": "src/ACME/app/"
}
}
}
Personally, I would rather name my namespaces more explicitly, see below.
Original Answer
The composer PSR-4 loader does not fall back when trying to load files that do not exist. It just fails immediately. Its flow looks like:
\ACME\App\Site\View is not loaded
Scan PSR-4 entries for matching namespaces
Class name matches the namespace \ACME\App\Site (your first PSR-4 entry).
Load file app/View.php
File does not exist. Error.
It never goes back to step 3 and tries the next namespace.
So how do I fix it?
It looks like you want to separate your reusable library code from your site code. If that's the case, I would use separate namespaces. For example, use the ACME\Site namespace to hold your reusable code, and use ACME\MySiteName for your site-specific code. Then there will be no ambiguity, and composer will have no trouble loading your classes.
But I don't want to rearrange my namespaces!
Ok, that's fine, but you'll have to use a hack to get around your problem. Composer has a classmap loader, and you'll have to use that instead of the preferred PSR-4 loader.
{
"autoload": {
"classmap": ["app/", "src/"]
}
}
Let's separate the things a bit, because they are all mixed up for now.
What I am trying to do is this:
Have a core which can overwritten / extended.
The core is based off an interface.
This sounds like basic object oriented inheritance. An interface defines the proposed public behaviour, the core implements the needed basics, and the detail implementation changes some parts, and reuses the others.
Let's write your example code in a way PHP sees it with absolute namespace names:
class \ACME\App\Site\View\ViewExample extends \ACME\App\Site\View\View {}
class \ACME\App\Site\View\View extends \ACME\App\View\View {}
class \ACME\App\View\View {}
You have three classes explicitly named. You'd need three files that match the namespace and class name. The autoloading does not need to do any detection whether or not a class is present - because you cannot optionally inherit from a class that isn't there, or omit it otherwise.
On the other hand, implementing three levels of inheritance by default very likely is too much. It looks like bad design to me, and will make maintaining the code harder than necessary. Depending on what you want to achieve, there are plenty of alternatives to get what you want easier. For example, to change some details of behavior, there are the decorator pattern or the strategy pattern.
So what I really want to do is make it so I don't have to have site/app/View/View.php, and I don't have to have site/app/View/Example.php - it can use site/src/ACME/app/View/Example.php.
You cannot have this. Your code explicitly states that it inherits from \ACME\App\Site\View\View, so this class MUST be present somewhere.
This is independent of any autoloading. To experiment, you can add all your code into one single file and then run it. This will make all classes known to PHP immediately, and the problem will become obvious: You cannot remove a class when at the same time other classes inherit it.
Sorry I'm new to namespaces so I may not of phrased it very well.
Namespaces are nothing really fancy, the same problem would arise if you would use the PSR-0 style classnames with underscores:
class ACME_App_Site_View_ViewExample extends ACME_App_Site_View_View {}
// This class MUST be present for the above class to work
class ACME_App_Site_View_View extends ACME_App_View_View {}
class ACME_App_View_View {}
The main new feature with namespaces is that you can import one class under a second name within a file with use OtherNamespace\Classname. But this is only an alias within the scope of this file (i.e. it does not affect other files or the global scope).
Namespaces and autoloading are not the right tool for this job. A namespace is just a way of making sure two people (or parts of your code) don't use the same name to mean different things. Autoloading is just a way to avoid having to list every source file you want to load code from.
When you override the behaviour of one class in another, these are not the same class; often, you'll want to inherit the default actions and reuse parts of them.
You might want to create several sub-classes for different purposes, so you need somewhere to hold the logic of which to use. The component which deals with this is called a "service locator" or sometimes a "DI container".
Namespaces let you map short names to longer, unique class names; autoloading let's you map a specific unique class name to a source file; service location is how you choose which unique class you want to use in a specific circumstance.
I am a novice laravel developer and I am trying to understand how namespacing works in here. So I was learning about the Repository pattern and decided to go ahead and implement it in my project.
So I created a directory called archive and then loaded it using PSR4 in my composer.json file like so:
"Archive\\": "archive/"
Now I created a Repositories folder in my archive folder where I will be creating all the repositories. And I am namespacing files in it like so:
namespace Archive\Repositories;
Which seems to working fine. Then I created a Contracts folder inside the Repositories folder which will hold all the interfaces to the implementations I am going to use like UserRepositoryInterface for example. And I am namespacing files in the Contracts folder like so:
namespace Archive\Repositories\Contracts;
Which is working fine too.
Now my doubt lies in the concrete implementations I am trying to make in the Repositories folder. Like for example there is a DbUserRepository which implements The UserRepositoryInterface in the Contracts folder.
Now since I am new to this I tried:
class DbUserRepository implements Contacts\UserRepositoryInterface
And it works just fine but then I thought I should use it on top of the file like so:
use Contacts\UserRepositoryInterface;
And I could just do:
class DbUserRepository implements UserRepositoryInterface
And it should work fine according to me but it gives me a class not found exception but when I do something like:
use Archive\Repositories\Contacts\UserRepositoryInterface;
It works fine now. But this is where I am blurry. Inside the DbUserRepository I am in the nampspace of Archive\Repositories already so why doesn't it just go on to look into the Contracts folder from there? Why do I need to specify the full this as use Archive\Repositories\Contacts\UserRepositoryInterface;
Why can't I just say:
use Contacts\UserRepositoryInterface;
I hope my question is not too confusing. Although my code is working now but I am blurry how namespacing works.
The rules are pretty simple:
All namespace and use statements always use fully qualified names (FQN), meaning they always start from the global namespace and are not relative to anything else. use Foo\Bar always means \Foo\Bar, no matter what namespace you're in.
All literal mentions of names inside the rest of the code are resolved relative to the current namespace and/or aliases established with use statements. new Foo, extends Foo and such either mean __NAMESPACE__\Foo, or whatever Foo you might have aliased in some use statement.
If you want to shorten names, you need to use use statements which use the FQN of the class, not relative to the current namespace.
On a web project there are two classes that have the same name. This was never an issue until now, because the two classes where never used at the same time / in the same script.
Now we require to use both classes in one script, and therefor got ourselves an "Cannot redeclare class" fatal error.
My question is: What options are there to resolve this issue?
The one possible solution would be to rename one of the classes in question, but it is something I would very much like to avoid - one of the classes is part of a third-party software that should not be modified at this level, to remain updateable.
I know there are namespaces - are they a valid option to this problem? I have never used namespaces until now.
Assuming we would put one of the classes into a namespace: Would this resolve the issue? Also, what measures would we need to take to access the now-namespaced class?
Namespaces would surely solve your problem.
For example I got multiple classes named 'core', but because they're all classes of a different namespace it doesn't give any conflict at all.
This does mean you have to go over all your code and refer to the namespaced class with the correct path.
$item = new doubleClass();
would become
$item = new \my_namespace\doubleClass();
Also make sure that your other scripts don't get namespaced otherwise it wouldn't be able to find the non namespaced class anymore.
How Namespace work:
consider we have 2 classes named User in file structure
/Package1/User.php
with content
<?php
namespace Package1;
class user {...}
and
/Package2/User.php
with content
<?php
namespace Package2;
class user {...}
and now for some reason we decide to use both in some class UserManager in:
/Package3/UserManager.php
with content:
<?php
namespace /Package3;
use package1/User as User1;
use package2/User as User2;
class UserManager {
public function __construct(User1 $user1, User2 $user2) {...}
...
}
I have create my own MVC framework which is PSR-0 compliant atm. and uses php-ActiveRecord as ORM.
I have found an issue where I from a controller called User, which exists in the namespace TapMVC\Application, tries to call an ActiveRecord model ALSO called User but exists in the namespace TapMVC\Db like so:
namespace TapMVC\Application;
use TapMVC\Db;
class User extends Controller {
function index() {
print_r(Db\User::find('all'));
}
}
This gives the following error:
Cannot redeclare class TapMVC\Application\User in
/path/to/project/app/Controllers/user.php on line 12
Where line 12 is the prototype/declaration of the User-controller.
Why can't i do this? I thought that if your classes where i different namespaces and had a namespace prefix on instantiation it would be ok to have the same name? Also it looks like the data-model is declared before the controller through autoloading (since its the user-controller declaration which triggers the error), so why is there a conflict when the data-model is in namespace TapMVC\Db and not TapMVC\Application where the controllers are?
What i can see, that even though you define different namespaces, PHP will declare the object under the same namespace as the one in the active file, but i am not sure.
Hope someone can help so i dont need to name my data models like so: (ProjectName-prefix)User and edit the database tables in order to have a User-model and a User-controller.
I found the error! :-D
The issue lied in my autoloading functions for controllers, models etc. since i didn't take care of namespaces. So when I tried to load a controller or model named the same, the spl_autoload_register function would start off by checking for controllers, and since my functions ignored the classes namespaces it would i both cases of: TapMVC\Application\User and TapMVC\Db\User find the User-controller class, stop the autoload list, and try to declare it.
This all resolved when i took care of the namespaces and made sure that controllers/models only get loaded as long as they are in the correct namespace. If not the function dose nothing and the spl_autoload_register function continues down the list of autoload functions.
The question was more framework specific than imagined and boiled down to autoloading which i didn't think was the problem... for some reason, and for that i am sorry. Just happy know that I found the error.
I have a couple functions that are used in multiple controllers in my Symfony project. Instead of copy and pasting them constantly, I'd like to have a separate php file (say functions.php) to store the functions.
I've tried the following:
1) include and require the php file with the functions, but it won't let me use $this->getDoctrine().
I've looked at the following posts for help, but it didn't solve the issue:
Symfony2: get Doctrine in a generic PHP class
Symfony2 Use Doctrine in Service Container
2) I've tried making it a class and having the functions as methods then doing:
$f = new Functions();
$variable = $f->myMethod();
But it returns:
The file was found but the class was not in it, the class name or namespace probably has a typo.
Thanks for any help. I really appreciate it.
UPDATE:
The generic class is Symfony/src/WikiRoster/MainBundle/Controller/DBFunctions.php
I just need to be able to use $this->getDoctrine() or the like somehow in there now. I've tried using the services.yml as suggested in the link above, but no cigar. Thanks!
The file was found but the class was not in it, the class name or
namespace probably has a typo.
This issue happens quite often actually. As the error mentions, there are 2 cases explaining why this error is returned:
If the namespace of your file is not the same as the path of your file
If the name of your file is not the same as the name you are using in your class
Example
Let's say you would like to use a GeneralClass in a different class like that:
use Acme\YourBundle\General\GenericClass
For this to work, GeneralClass needs to be in Acme/YourBundle/General/, and you need to:
use the correct namespace: namespace Acme\YourBundle\General
name your class with the same name: class GenericClass
You would have:
namespace Acme\YourBundle\General;
class GenericClass
{
public function __construct()
{
// some cool stuff
}
}