Zend Framework Modules with common resources - php

I'm having a lot of trouble figuring out how we can have a modular directory structure, with the ability to load resources that are to be shared across modules. I.e.,
application
--- /forms
--- /models
--- /modules
------/module1/
---------/models
------/module2/
---------/models
Now, what I'm trying to do is load forms in /application/forms from within the modules. Everything I've tried results in these classes to not being loaded.
I've tried:
1) Letting Zend try and figure it out automagically.
2) Specifying all the paths in the main bootstrap for the application path as well as the modules. I.e.,
protected function _initAutoload()
{
$front = $this->bootstrap("frontController")->frontController;
$modules = $front->getControllerDirectory();
$default = $front->getDefaultModule();
$moduleloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'Application',
'basePath' => APPLICATION_PATH
));
foreach (array_keys($modules) as $module) {
$moduleloader = new Zend_Application_Module_Autoloader(array(
'namespace' => ucfirst(strtolower($module)),
'basePath' => $front->getModuleDirectory($module))
);
}
}
3) Smashing my head on my desk many times.
.. and yes, I realize I do not need that loop for modules, as I have blank bootstraps in each module directory.
Any suggestions are welcome. Thanks!

Try this:
protected function _initAutoload()
{
$autoloader = new Zend_Application_Autoloader_Resource(array(
'namespace' => 'Application',
'basePath' => APPLICATION_PATH,
'resourceTypes' => array(
'form' => array(
'path' => 'forms/',
'namespace' => 'Form'
)
)
));
return $autoloader;
}
you don't need the module part, as you already seem to know. Add other resource types as required.
Since this is very close to what you have already, there may be another issue. The above should work assuming that:
APPLICATION_PATH points at the /application directory in your app
The form classes are named Application_Form_Something
The filenames of these classes are Something.php (case sensitive)
e.g. if you have a contact form, you might call the class Application_Form_Contact and this would live at application/forms/Contact.php.
If you're still having issues, please include an example of a form class that isn't being found, along with how and where you are calling it from.

Related

Phalcon Router and Loader for subfolder structure getting bigger. How to set up?

I have a quite big project that I need to program. I used to code with CodeIgniter but since they stopped maintaining the framework, I decided to switch to another. I chose Phalcon framework. The folder structure of the application that I want to implement is next:
app/
controllers/
admin/
users/
UsersController.php
UserGroupsController.php
dashboard/
system/
another_subfolder/
AnotherSubFolderController.php
production/
settings/
SettingsController.php
dashboard/
flow/
another_subfolder/
AnotherSubFolderController.php
website1/
customers/
CustomersController.php
CompaniesController.php
another_subfolder .... /
models/
sub_folder_structure/ // The same as controllers
This is just example of the folder structure of the application. There will be quite a lot of folders and sub-folders in this project to make it manageable.
From my understanding I need to register all namespaces at the loader for each sub-folder, so that Phalcon knows where to search and load the class.
//Register an autoloader
$loader = new \Phalcon\Loader();
$loader->registerNamespaces(array(
// Main
'Controllers' =>'app/controllers/',
'Models' =>'app/models/',
// Admin Routing
'Controllers\Admin'=>'app/controllers/admin',
'Models\Admin'=>'app/models/admin',
'Controllers\Admin'=>'app/controllers/admin',
'Models\Admin'=>'app/models/admin',
'Controllers\Admin\Users'=>'app/controllers/admin/users',
'Models\Admin\Users'=>'app/models/admin/users'
))->register();
The loader will look quite big.
Then I have to set-up the router so that requests redirected to the right controller. Right now I have next:
// Setup Router
$di->set('router', function(){
$router = new \Phalcon\Mvc\Router(false);
$router->removeExtraSlashes(true);
$router->add('/', array(
'namespace' => 'Controllers',
'controller' => "login"
));
$router->add('/:controller/:action/', array(
'namespace' => 'Controllers',
'controller' => 1,
'action' =>2
));
$router->add('/admin/:controller/:action/', array(
'namespace' => 'Controllers\Admin',
'controller' => 1,
'action' =>2
));
$router->add('/admin/users/:controller/:action/', array(
'namespace' => 'Controllers\Admin\Users',
'controller' => 1,
'action' =>2
));
return $router;
});
That will be also very big if I need to manually setup router for each sub-folder and namespace.
So the questions that I have are this:
Is there any way to make a loader prettier and smaller? As it will grow bigger.
How can I setup router so that I don't need to enter all of the namespaces and combinations of subfolders? Is there any way to make it smaller? May be modify dispatcher or any other class? Or is there a way I can set a path variable in the router to the location of the controller?
I have researched Phalcon documentation but could not find the way to do that.
Any help and suggestions are very much appreciated.
Thanks
Ad. 1. You can change your namespaces to be more PSR-0 (in my opinion), so I would make:
app
controllers
Admin
Users
UsersController.php
The you can register one namespace Admin or any other. Then you need to register only the top most namespace to work (keep in mind that your UsersController must have namespace Admin\Users\UsersController; to work). Thena autoloader should have only:
$loader
->registerDirs(
array(
// It's taken from my config so path may be different
__DIR__ . '/../../app/controllers/'
// other namespaces here (like for models)
)
);
I'm using registerDirs so I only point loader to the folder in which some namespace exists.
Ad. 2.
For this you can use groups of routes so you can pass a namespace as a parameter for config array of constructor and then do the repeative task in one place. Then just create new instances with different parameters;
$router->addGroup(new MahGroup(array('namespace' => 'Mah\\Controller'));
So inside MahGroup class could be:
class MahGroup extends Phalcon\Mvc\Router\Group {
public function _construct($config = array()) {
$this->setPrefix('/' . $config['perfix']);
$router->add('/:controller/:action/', array(
'namespace' => $config['namespace'],
'controller' => 1,
'action' => 2
));
// etc...
}
}
And then configuring routes:
$router->addGroup( new MahGroup(array('prefix' => 'mah-namespace', 'namespace' => 'Mah\\Namespace' )) );
$router->addGroup( new MahGroup(array('prefix' => 'mah-other-namespace', 'namespace' => 'Mah\\Other\\Namespace' )) );
But given examples for second question are just what could be done. I usually create Group class for each namespace and then declare some routes that my app uses since I'm not using english names for routes and I need rewriting polish urls to controllers which have also some namespaces.

Phalcon router doesn't react to subfolders and namespace declaration

So I've been reading a ton of stackoverflow and phalcon forum threads.. (I'm starting to hate this framework), but nothing seem to work and it doesn't explain why like Laravel does, for example.
I'm just trying to be able to operate with this application structure:
As you can see, all I want is to use namespaced controllers in subfolders to make more order for my code.
According to all explanations, here's my loader.php:
<?php
$loader = new \Phalcon\Loader();
/**
* We're a registering a set of directories taken from the configuration file
*/
$loader->registerDirs(
array(
$config->application->controllersDir,
$config->application->modelsDir,
$config->application->pluginsDir
)
)->register();
AFAIK, Phalcon should traverse all subfolders for not found classes when used via registerDirs.
Then I define my routes to specific controller after the main route to index controllers in base directory:
<?php
$router = new Phalcon\Mvc\Router(false);
$router->add('/:controller/:action/:params', array(
'namespace' => 'App\Controllers',
'controller' => 1,
'action' => 2,
'params' => 3,
));
$router->add('/:controller', array(
'namespace' => 'App\Controllers',
'controller' => 1
));
$router->add('/soccer/soccer/:controller', array(
'namespace' => 'App\Controllers\Soccer',
'controller' => 1
));
$router->add('/soccer/:controller/:action/:params', array(
'namespace' => 'App\Controllers\Soccer',
'controller' => 1,
'action' => 2,
'params' => 3
));
return $router;
And one of my controllers look like:
<?php namespace App\Controllers\Soccer;
use App\Controllers\ControllerBase as ControllerBase;
class IndexController extends ControllerBase
{
public function indexAction()
{
}
}
What's wrong here? Default top namespace is not registered? Am I missing something?
This just doesn't work. When I try to open myserver.com/soccer which I expect to go to app/controllers/soccer/IndexController.php, but instead it tells me:
SoccerController handler class cannot be loaded
Which basically means it's looking for SoccerController.php in /controllers directory and totally ignores my subfolder definition and routes.
Phalcon 1.3.0
Stuck on this for a week. Any help - Much appreciated.
I was having a problem with loading the ControllerBase and the rest of the controllers in the controllers folder using namespaces. I was having a hard time since other example projects worked fine and I realized that I was missing a small detail in the despatcher declaration where I was supposed to setDefaultNamespace
(ref: https://github.com/phalcon/vokuro/blob/master/app/config/services.php)
$di->set('dispatcher', function () {
$dispatcher = new Dispatcher();
$dispatcher->setDefaultNamespace('App\Controllers');
return $dispatcher;
});
or you can specify it directly on the routing declaration file like this
$router->add("/some-controler", array(
'namespace' => 'App\Controllers'
'controller' => 'some',
'action' => 'index'
));
that should work as well, it might be a bit confusing at first with namespaces but once you get a hang of it you will love this extremely fast framework
It looks like your namespaces have capitals
App\Controllers\Soccer
and your folder structure does not
app\controllers\soccer
In my app I have controllers with a namespace and I have just tried changing the case of the folders so they don't match and I get the missing class error.
Of course it does raise a question, how many controllers are you planning on having that namespacing them is worthwhile? Are you grouping controllers and action by function or content? I used to have loads of controllers in Zend, but now I have grouped them by function I only have 16 and I am much happier. e.g. I suspect soccer, rugby, hockey etc will probably be able to share a sport controller, articles, league positions etc will have a lot of data in common.
ps Don't give up on Phalcon! In my experience it is sooo much faster than any other PHP framework I have used it is worth putting up with a lot :)
Are you sure Phalcon traverses subfolders when registering directories to the autoloader? Have you tried adding a line to your autoloader which explicitly loads the controllers\soccer directory?
Alternatively, if your soccer controller is namespaced, you can also register the namespace: "App\Controllers\Soccer" => "controllers/soccer/" with the autoloader.

Zend Framework create fully dynamic route while maintaining default routes

I understand how to create a simple custom route in the form of
example.com/archive/:year
However, I need to create a route in the form of
example.com/:make/:model
Both path components are variables. At the same time I want to keep the default Zend Framework routing. How would I go about this?
Thanks
Not for zend, but the technique is instructive and will probably work. I've struggled with this problem too, but was more for internationalisation.
http://darrenonthe.net/2011/05/06/dynamic-routing-from-database-in-codeigniter/
Basically, you cache your routes into a file, and the framework matches them to your controller/variable details. I tried lots of things, complex regexes, and in the end, this worked out really, really well. Might be a good solution for you too.
if you are using a module based file architecture, you can maintain the zend framework default routes, and add custom routes for your modules.
for example
class Example_Bootstrap extends Zend_Application_Module_Bootstrap
{
public function _initModuleRoutes()
{
$this->bootstrap('FrontController');
$frontController = $this->getResource('FrontController');
$router = $frontController->getRouter();
$route = new Zend_Controller_Router_Route(
'modulename/:action/*',
array(
'module' => 'modulename',
'controller' => 'modulecontroller',
'action' => 'index'
)
);
$router->addRoute('routename', $route);
return $router;
}
You need to enforce some condition i.e model is integer type or something else . Like this
$route = new Zend_Controller_Router_Route(
':make/:model',
array(
'controller' => 'car',
'action' => 'logic'
),
array('make' => '\d+')
);
If you cannot distinguish them with extra condition like these then how software gone do this for you weather its action name or make ?

Why isn't my form working with my modules?

I have an auth module, and would like to keep a Login form in the forms folder in the module. I have my class named Auth_Form_Login and the code I use to instantiate it in auth's Index controller is:
$loginForm = new Auth_Form_Login($_POST);
However, Zend is complaining that it can't find the class. What am I doing wrong?
Edit
My directory structure is like so:
application
configs
layouts
modules
auth
controllers
models
views
forms <--- This is what I want to autoload from
default
controllers
models
views
library
public
Also, modules are set up correctly:
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.frontController.params.prefixDefaultModule = false
Seems like you're missing module bootstrap for Auth module:
application
modules
auth
Bootstrap.php
Which might consist of this only line - just to make autoloading magic work:
class Auth_Bootstrap extends Zend_Application_Module_Bootstrap {}
You're doing everything right in your form initialisation. The autoloader simply doesn't know where to find it so you need to setup zend autoloader correctly.
I am not sure how you created your module based structure. I use the cli tool which configures everything.
So in my application.ini file I have:
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules = ''
resources.frontController.params.prefixDefaultModule = "1"
And in my Boostrap.php:
$modelLoader = new Zend_Application_Module_Autoloader (
array ('namespace' => '', 'basePath'
=> APPLICATION_PATH .
'/modules/default' ) );
You also can setup the autoloader manually by registering your resources:
$resourceLoader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => 'path/to/my/directory',
'namespace' => 'Auth',
'resourceType' => array(
'form' => array(
'path' => 'forms/'
'namespace' => 'Form'
)
//...your code
)
)
);

What directory is used for Zend plugins?

Let's say I have the following in my ini file:
resources.frontController.plugins.auth = AuthPlugin
Where should the AuthPlugin class be placed? Let's say I would like it under controllers/plugins.
UPDATE:
Based on the suggestions below I am still having trouble. Let me be exact in what I currently have:
1) main part of application.ini
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
resources.view[] =
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.plugins.authplugin.class = "AuthPlugin"
2) my Bootstrap.php has nothing (I had lots of things in there, but still get the error with nothing):
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
}
3) I have an AuthPlugin.php class in application/plugins directory
class AuthPlugin extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
// code here
}
}
I get the following error:
Fatal error: Class 'AuthPlugin' not found in C:\[my dir structure here]\Application\Resource\Frontcontroller.php on line 111
I assume I'm missing something obvious here. Thanks in advance. Zend Framework 1.10
This is how I register a plugin named Foo_Plugin_SuperDuperPlugin in my application config:
resources.frontController.plugins.superduperplugin.class = "Foo_Plugin_SuperDuperPlugin"
The plugin is located at
APPLICATION_PATH/plugins/Foo_Plugin_SuperDuperPlugin.php and is autoloaded from there because the Resource Module Autoloader automatically looks in that (recommended) location for plugin type resources. If I wanted to load the plugin from, say,
APPLICATION_PATH/controllers/plugins/Foo_Plugin_SuperDuperPlugin.php then I would register a new resource loader with the autoloader and define a type of resource named 'plugin' and the path to those plugin resources. So in my bootstrap.php
protected function _initAutoloader()
{
$autoloader = new Zend_Loader_Autoloader_Resource(
array(
'basePath' => APPLICATION_PATH,
'namespace' => 'Foo',
'resourceTypes' => array(
'plugin' => array(
'path' => 'controllers/plugins',
'namespace' => 'Plugin',
)
)
)
);
}
and then I need to ensure that this method is bootstrapped before the SuperDuperPlugin is registered (which, in this example, happens when the application config is read resources.frontcontroller.plugins.superduperplugin.class = ...). This can be achieved by placing the _initAutoloader method at the top of the bootstrap.php or by calling $this->bootstrap('autoLoader'); from any other _init method, before the frontController resource is initialised.
UPDATED: Try adding this to your bootstrap:
protected function _initAutoloader()
{
$autoloader = new Zend_Loader_Autoloader_Resource(
array(
'basePath' => APPLICATION_PATH,
'resourceTypes' => array(
'plugin' => array(
'path' => 'controllers/plugins',
'namespace' => '',
)
)
)
);
}
and maybe even leave off the namespace. Or: add appnamespace = "Foo" to your config and rename the class to Foo_Plugin_AuthPlugin.
hmm am having the exact same issue in zf 1.11. It seems the autoloader does not exists before the plugins get loaded :s
I think in /application/plugins/
But you could also set another Directory for it.
Since the application will bootstrap itself from the config file after registering the Autoloader, you should put AuthPlugin.php (which should contain the AuthPlugin class) in the include path.
I realise this is an old question, but I've just been searching around for ages for a solution to autoloading plugins outside of the library and have finally figured it out. Generally, I have been writing generic plugins to use across many different projects and keeping them in the library made sense, providing you follow the standard ZF naming conventions these will autoload anyway. Recently, I was writing a project specific plugin and I wanted to keep it somewhere within my application directory, but how to autoload? I've never found that using the "application/plugins" directory worked. Hopefully this might benefit someone else:
If you are using a modular directory structure then rather than using the "application/plugins" directory you can use "application/module_name/plugins". You can then take advantage of the Module Resource Autoloader, which is a massively useful and underused part of ZF. If you set it up in your bootstrap, by default it is set up to autoload a whole bunch of stuff including forms, models and (I discovered today) plugins. You can also define your own custom resource types. For example below is an _initAutoloader function from a bootstrap in a project which has a default and admin module and a custom 'vo' resource type contained in the admin module:
public function _initAutoLoader() {
$autoloader = Zend_Loader_Autoloader::getInstance();
$defaultLoader = new Zend_Application_Module_Autoloader(
array(
'namespace' => '',
'basePath' => APPLICATION_PATH . '/modules/default'
)
);
$adminLoader = new Zend_Application_Module_Autoloader(
array(
'namespace' => 'Admin',
'basePath' => APPLICATION_PATH . '/modules/admin',
)
);
$adminLoader->addResourceTypes(
array(
'vo' => array(
'path' => 'models/vo',
'namespace' => 'Vo',
)
)
);
$autoloader->pushAutoloader($defaultLoader);
$autoloader->pushAutoloader($adminLoader);
}
Here I have two modules, default and admin. Assuming the following directory structure:
application/default/forms/FooForm.php
application/default/models/FooModel.php
application/default/plugins/FooPlugin.php
application/admin/forms/FooForm.php
application/admin/models/FooModel.php
application/admin/models/vo/FooVo.php
application/admin/plugins/FooPlugin.php
I can autoload across any module just by instantiating an object of each class:
new Form_FooForm();
new Model_FooModel();
new Plugin_FooPlugin(); // or add plugin to application.ini
new Admin_Form_FooForm();
new Admin_Model_FooModel();
new Admin_Vo_FooVo(); // custom resource type
new Admin_Plugin_FooPlugin(); // or add plugin to application.ini

Categories