How can someone autoload every form and model for each module? Consider the following file structure:
application/
modules/
foo/
forms/
Register.php
models/
Account.php
Bootstrap.php
bar/
forms/
Publish.php
models/
Article.php
Bootstrap.php
Bootstrap.php
And for example, in foo/Bootstrap.php you have the following (non-functional) code:
class Foo_Bootstrap extends Zend_Application_Module_Bootstrap
{
protected function _initAutoLoad()
{
$loader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => APPLICATION_PATH . '/modules/foo',
'namespace' => 'Foo',
));
$loader->addResourceType('form', 'forms', 'Form')
->addResourceType('model', 'models', 'Model');
return $loader;
}
}
Basic question: How can the bootstrap be modified so that it does load every form and model from the Foo module?
Extra question: Is it possible to have a global autoloader that loads in forms and models from every module? If so, how?
Edit (most common questions about the issue):
The default Zend naming conventions are being used for classes. Such as Bar_Model_Article, Bar_Model_Mapper_Article, Bar_Model_DbTable_Article, Bar_Form_Publish, ... (And are being placed in their respective folder.)
It isn't just one module that doesn't get its classes loaded, it's all of them.
There is no problem autoloading classes using the Zend autoloader when using a plain no-module application with multiple models, mappers, dbtables and forms.
Fix
As #Tim Fountain mentioned the module bootstraps weren't being run, meaning none of the automatic loading occurred that's baked into Zend. Eventually, I found where the problem was in my case. I had to remove the following lines from my configuration:
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
Agreed, the global bootstrap won't work anymore; but it's a lot better than having module bootstraps not functioning. If anyone knows how to still have the global bootstrap, feel free to leave a comment. Hope this can be of help to others with a similar problem.
The module bootstrap class sets up the module autoloader automatically, so you can remove your example _initAutoload() function leaving just an empty class and it should all just work. See: http://framework.zend.com/manual/en/zend.loader.autoloader-resource.html#zend.loader.autoloader-resource.module
Edit: It sounds like your module bootstraps aren't being run. This is not an uncommon problem as the way it all fits together can be a bit confusing. The quickest way to verify this would be to just add an init method to one of them with an echo and an exit and see if it ever gets output.
Module bootstraps are pulled in and run by the 'modules' resource within Zend Application. You need to trigger this resource in some way because ZF won't go hunting around for module bootstraps just in case they are there. The most common way to do this is to include this line in your application.ini:
resources.modules[] = ""
alternatively you can manually setup the resource from your main Bootstrap file.
I also had it always like that. But since the release of 1.10 (wild guess), you can remove that bootstrap code and just put the following line in your application.ini:
appnamespace = "Foo"
I personally leave mine empty.
Related
I am trying to define a namespace/autoload structure for multiple projects. Each project has a library. The consists classes which will be extended for each project. Here is the content of one project
/project1
/lib
parentclass.php
autoloader.php
index.php
myclass.php
parentclass.php:
<?php
class parentclass{
}
?>
myclass.php
<?php
class pr1_myclass extends parentclass{
}
In index.php i will define a global array with settings which will be used in the autoloader:
<?php
$settings = array(
'dir' => __DIR__;
'lib_dir' => 'project1_dir',
'lib_namespace' => '??',
);
$project_prefix = 'pr1';
$GLOBALS['projectsAutoloadSettings'][$project_prefix] = $settings;
The autoloader will be something like this:
<?php
function my_autoloader($className){
$parts = explode('_',$className);
if(isset($GLOBALS['projectsAutoloadSettings'][$parts[0])){
$settings = $GLOBALS['projectsAutoloadSettings'][$parts[0]];
//I now have settings array for this project. From here i can include the right file:
include($settings['dir'].'/'.$parts[1].'.php');
}else{
//Need to include library file. This is part of my problem. I dont know how to handle this
}
}
spl_autoload_register('my_autoLoader');
This was the structure for 1 project. I have multiple projects. The /lib directory in each project is the same.
Now i need a namespace structure which will work in both 2 environments mentioned below, while only having to change the $settings.
Local developer environment. Here a single /lib should be used for all projects. So for example a class from project 3 will extends a parent class from the library from project 1.
Active environment. Here each class in a project extends the library from its project. So a class from project 3 will always extend a parent class from project 3.
Multiple projects can be active at a single web page. I figure i need namespaces for this, but my main question is that the /lib directory is always the same code (copy pasted). I want to keep it that way, as far as i know it is not possible to have a different namespace for /project3/lib than for project1/lib.
How can i still achieve my goal. I am interested in the code-structure, things like autoloader code (classname to filename) i will figure out myself.
EDIT: An edit with an example of a problem i am facing, which seems not clear if i look at the comments.
It is in live (not developer) mode. I have a second project with the same structure as project one, but instead with this class in /project2/someclass.php
<?php
class pr2_someclass extends parentclass{
}
?>
Now this is my code for running both projects:
<?php
//project 1 start:
$myClass1 = new pr1_myclass();
//This loads pr1_myclass which is an extension from the parent class in project1/lib/parentclass.php.
//So far so good
//Project 2 start
$myClass2 = new pr2_someclass();
//Here the problem occurs. This class loads pr2_someclass, but it will be an extension of project1/lib/parentclass.php instead of project2/lib/parentclass.php.
?>
Since both lib directories are identical, you might think i just should use the same library for both projects. However, there can be more than a year between two projects so it might be possible that the two libraries are not exactly identical.
I'm trying the framework ZF2 and I try to do very independant modules like bundles in SF2.
I've got ZfcTwig to have Twig to render my views. This worked until I've created a second module.
-Application (default module)
-Admin
-view
index.twig
-layout
base.twig
-Blog
-view
index.twig
-layout
base.twig
The problem is that my Blog layout extend the Admin base layout then !
I've done my structure layout based on http://blog.evan.pro/module-specific-layouts-in-zend-framework-2
So in both Module.php I've this:
public function init($moduleManager)
{
$sharedEvents = $moduleManager->getEventManager()->getSharedManager();
$sharedEvents->attach(__NAMESPACE__, 'dispatch', function($e) {
$controller = $e->getTarget();
$controller->layout('layout/base.twig');
}, 100);
}
Plus I don't understand why I've to define twice the layout, one time in the init function of Module.php, the second on extend function of twig views.
For sure its work if I've different names.
And I see for this module: https://github.com/EvanDotPro/EdpModuleLayouts
But I think it should be possible without this to have really independant module since its the philosophy of the framework.
By default, ZfcTwig works in "zf way", using the two-step view pattern.
If you want to use the original twig system (for extends), you must specify it in your config file.
It is well commented :
/**
* If set to true disables ZF's notion of parent/child layouts in favor of
* Twig's inheritance model.
*/
'disable_zf_model' => true,
This way, you will control yous layouts with extends instructions.
My Zend Framework setup is like this:
/application
/modules
/models
/configs
/library
/public
I want to access my models without needing prefixes or namespaces, like this:
new User() // Would refer to /application/models/User.php
I know this is a fairly simple problem, but I havn't been able to figure it out yet. I've also seen a lot of similar questions but none that I thing were this one exactly, but please forgive me if I am duplicating an existing question.
So far, I have tried changing appnamespace to "" in my config.ini with no success and adding
the following to my Bootstrap with no success:
protected function _initAutoload()
{
$autoloader = new Zend_Loader_Autoloader_Resource(array(
'namespace' => '',
'basePath' => APPLICATION_PATH . '/models',
));
}
Thanks!
You have to use the fallback autoloader..
In your Bootstrap file (or wherever you like), do this:
protected function _initAutoloader()
{
Zend_Loader_Autoloader::getInstance()->setFallbackAutoloader(true);
}
That will allow you to load any arbitrary class that is in your path, even if you've registered specific autoload prefixes in your application.ini
And to clarify, make sure you are pushing APPLICATION_PATH . "/models" into your include_path at some point..
The ZF Docs reference 'Subclassing the Action Controller' (bottom of the page), but don't reference a standard place to put the new Action_Controller class.
Application_Module_Autoloader sets up pats for a bunch of things, but never controllers. I guess putting it on library/APPNAMESAPCE/Action/Contoller would work. But that seems a bit odd since every other application specific file is stored under application/.
The class gets autoloaded like any other class, there isn't a 'standard' place for it as such. So the question becomes, where do you want it to live?
The convention I usually follow in modular applications is to have most stuff in the modules, but register an app namespace and use application/models for 'core' type classes. So in your case, say your app namespace was Wordpress, you'd have:
class Wordpress_Controller_Action extends Zend_Controller_Action
{
}
and the file would live in application/models/Wordpress/Controller/Action.php.
To make this work you'll need application/models on your include path, and you'll want to init the standard autoloader with something like this (in your bootstrap class):
protected function _initAutoloader()
{
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('Wordpress_');
return $autoloader;
}
alternatively you could setup the above in application.ini.
I use Module Autoloader to autoload resources (forms, Doctrine models etc.).
I do not use Zend_Db_Table at all.
When I load any Doctrine model,
e.g. MyModule_Model_Test,
it tries to load MyModule_Model_TestTable too, so I get errors that the MyModule_Model_TestTable.php is missing.
To fix this, I may create empty class MyModule_Model_TestTable class and
everything works as expected.
But I don't need this file.
Strange that, when I move MyModule_Model_TestTable to /anyDirDeeper/MyModule_Model_TestTable without changing its name or content, the class is correctly loaded too…
How to configure Module Autoloader so it would not require this …Table classes?
I have in my application.ini:
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules[] =
And Module Bootstrap:
class MyModule_Bootstrap extends Zend_Application_Module_Bootstrap {}
My app structure is similar to this:
/application/
/modules/
/mymodule/
/models/
/Db/
*Mymodule_Model_Db_Test*
*Mymodule_Model_Test*
I think this issue was produced because I used the same module name and resource type name (registered by default).
Models were named: Acl_Model_Modelname and Acl_ namespace was registered with autoloader. Changed model namespace to something else and it works.