I am working on implementing Zend Framework within an existing project that has a public marketing area, a private members area, an administration site, and a marketing campaign management site. Currently these are poorly organized with the controller scripts for the marketing area and the members area all being under the root of the site and then a separate folder for admin and another folder for the marketing campaign site.
In implementing the Zend Framework, I would like to create be able to split the controllers and views into modules (one for the members area, one for the public marketing area, one for the admin site, and one for the marketing campaign admin site) but I need to be able to point each module to the same model's since all three components work on the same database and on the same business objects.
However, I haven't been able to find any information on how to do this in the documentation. Can anyone help with either a link on how to do this or some simple instructions on how to accomplish it?
What I do is keep common classes in a "library" directory outside of the modules hierarchy. Then set my INCLUDE_PATH to use the "models" directory of the respective module, plus the common "library" directory.
docroot/
index.php
application/
library/ <-- common classes go here
default/
controllers/
models/
views/
members/
controllers/
models/
views/
admin/
controllers/
models/
views/
. . .
In my bootstrap script, I'd add "application/library/" to the INCLUDE_PATH. Then in each controller's init() function, I'd add that module's "models/" directory to the INCLUDE_PATH.
edit: Functions like setControllerDirectory() and setModuleDirectory() don't add the respective models directories to the INCLUDE_PATH. You have to do this yourself in any case. Here's one example of how to do it:
$app = APPLICATION_HOME; // you should define this in your bootstrap
$d = DIRECTORY_SEPARATOR;
$module = $this->_request->getModuleName(); // available after routing
set_include_path(
join(PATH_SEPARATOR,
array(
"$app{$d}library",
"$app{$d}$module{$d}models",
get_include_path()
)
)
);
You could add the "library" to your path in the bootstrap, but you can't add the "models" directory for the correct module in the bootstrap, because the module depends on routing. Some people do this in the init() method of their controllers, and some people write a plugin for the ActionController's preDispatch hook to set the INCLUDE_PATH.
This can also be accomplished through a naming convention to follow Zend_Loader. Keep your model files in the models folder under their module folder. Name them as Module_Models_ModelName and save them in a file name ModelName.php in the models folder for that module. Make sure the application folder is in your include path and assuming you are using Zend_Loader for auto loading, you can then just reference the models by their class name.
This has the advantage of keeping your model code grouped in with the actual module it is for. This keeps the module contained within a single folder structure which helps encourage encapsulation. This will also help in the future if you need to port the module to another project.
I just built this custom Action Helper for the problem you describe:
<?php
class My_Controller_Action_Helper_GetModel extends Zend_Controller_Action_Helper_Abstract
{
/**
* #var Zend_Loader_PluginLoader
*/
protected $_loader;
/**
* Initialize plugin loader for models
*
* #return void
*/
public function __construct()
{
// Get all models across all modules
$front = Zend_Controller_Front::getInstance();
$curModule = $front->getRequest()->getModuleName();
// Get all module names, move default and current module to
// back of the list so their models get precedence
$modules = array_diff(
array_keys($front->getDispatcher()->getControllerDirectory()),
array('default', $curModule)
);
$modules[] = 'default';
if ($curModule != 'default') {
$modules[] = $curModule;
}
// Generate namespaces and paths for plugin loader
$pluginPaths = array();
foreach($modules as $module) {
$pluginPaths[ucwords($module)] = $front->getModuleDirectory($module) . '/models';
}
// Load paths
$this->_loader = new Zend_Loader_PluginLoader($pluginPaths);
}
/**
* Load a model class and return an object instance
*
* #param string $model
* #return object
*/
public function getModel($model)
{
$class = $this->_loader->load($model);
return new $class;
}
/**
* Proxy to getModel()
*
* #param string $model
* #return object
*/
public function direct($model)
{
return $this->getModel($model);
}
}
So in your Bootstrap.php:
Zend_Controller_Action_HelperBroker::addPrefix('My_Controller_Action_Helper');
And in any of your controllers:
<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$model = $this->_helper->getModel('SomeModel');
}
}
And this will allow your access to models in any controller across all modules.
I'm having the same problem.
Bill's answer doesn't fit for me - cos i tend to divide my modules, not by 'who is seeing them', but by 'what they do'. E.g a 'forum module' might be managed by both admin and public.
I'm trying to have front end modules, like admin, members , public - but these then use other modules like 'forum/validatepost', 'forum/show users personal info'.
If anyone could shed light on how they protect a back-end module from the public , then that would be handy. I guess ACL may be the key but it still makes me nervous having access controlled by objects as opposed 'file system/.htaccess' etc.
To answer PHPoet's question :
(i) Paths to module's controller directories can be specified by calls to front controller:
e.g see : "12.11.2. Specifying Module Controller Directories" (Zend Framework Docs)
(ii) Paths to views can be set using ViewRenderer (Controller Action Helper)
e.g. see: 'Example 12.12. Choosing a Different View Script' (Zend Framework Docs)
By playing around its possible to alter the default paths to views and controllers, thus freeing up your autoloader to run as normal.
(I have not looked into the way autoloader works, but it would make sense for it to have some mapper system to solve this kind of issue.)
<?php
return array(
'modules' => array(
'Application',
'DoctrineModule',
'DoctrineORMModule',
'Merchant',
),
'module_listener_options' => array(
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
'module_paths' => array(
'./module',
'../vendor',
// 'here we can load module'
'comomonmodule'
),
),
);
Related
I have a Class object which is used in many modules in my zend structure :
/module/
--|Aplication
--|MyClassModule
----|config
----|src
------|Factory
------|Model
---------|> MyObjectClass.php
----Module.php
--|AnotherModule
So my idea is to use this MyObjectClass.php in other modules so I can avoid duplication and have its own configuration. So far, for this is ok, however I want to get the variables set from my config/autoload files injected in this class but I don't know how.
How can I load this config data into my class model? Which is the best approach ? I can load it by accessing this directly but I don't think this is very elegant
e.g: $configArray = require './config/autoload/config.local.php';
I am not very experienced with zend so I dont know where to start with. I have seen many tutorials of how to do this via controllers, views.. etc but not in specific classes.
Thank you.
All config files are merged into one config, when your ZF2 application is bootstrapped. That includes local.php, global.php from config/autoload and all used modules' module.config.php. With a bit of more research, you can overwrite the standard loading, e.g. loading custom configs.
After bootstrapping, your are able to access the config from the ServiceManager. There are preserved keys for some ZF2-specific configs, service_manager, etc.
$serviceManager->get('config');
There is a "standard" service pattern in ZF2: Factory. This can be applied for Controllers, Services. What ever you want.
namespace Application\Factory;
use Application\Model\MyObjectClass;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyObjectFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
// get some config parameter, inject it into model
$config = $serviceLocator->get('config');
$myObjectClass = new MyObjectClass();
// ... e.g. $myObjectClass->setConfig($config);
return $myObjectClass;
}
}
It should be clear, what this factory is made for: create and return an instance of your custom object ;) You may configure your instance with some config params. With ServiceLocator as method param, you are able to access the config, other services etc.
Further, you have to register your own service/factory in the factories section of service_manager config in your module's module.config.php:
return array(
'service_manager' => array(
'factories' => array(
'MyObjectFactory' => 'Application\Factory\MyObjectFactory',
),
),
);
Now you should be able to access your factory, e.g. in an ActionController or wherever you have access to ServiceManager. That means, you can also access this factory from different modules.
public function someCustomAction() {
$myObjectClass = $this->getServiceLocator()->get('MyObjectFactory');
$myObjectClass2 = $this->getServiceLocator()->get('MyObjectFactory');
var_dump($myObjectClass);
var_dump($myObjectClass2);
if ($myObjectClass === $myObjectClass2) {
echo '<br />equal';
}
$myObjectClass = new MyObjectClass();
$myObjectClass2 = new MyObjectClass();
var_dump($myObjectClass);
var_dump($myObjectClass2);
}
Note:
Be aware, that ServiceManager returns the same instance of your object. So, that seems like what you ask for? In contrast, creating a new instance will create different objects.
Note 2:
Tested with ZF2 v2.4.9
What is the best way to separate admin and front-end for a website in codeigniter where as I was to use all libraries, models, helpers etc. in common, but only controllers and Views will be separate.
I want a more proper way, up for performance, simplicity, and sharing models and libraries etc.
I highly suggest reading the methods outlined in this article by CI dev Phil Sturgeon:
http://philsturgeon.co.uk/blog/2009/07/Create-an-Admin-panel-with-CodeIgniter
My advice: Use modules for organizing your project.
https://bitbucket.org/wiredesignz/codeigniter-modular-extensions-hmvc/wiki/Home
Create a base controller for the front and/or backend. Something like this:
// core/MY_Controller.php
/**
* Base Controller
*
*/
class MY_Controller extends CI_Controller {
// or MX_Controller if you use HMVC, linked above
function __construct()
{
parent::__construct();
// Load shared resources here or in autoload.php
}
}
/**
* Back end Controller
*
*/
class Admin_Controller extends MY_Controller {
function __construct()
{
parent::__construct();
// Check login, load back end dependencies
}
}
/**
* Default Front-end Controller
*
*/
class Public_Controller extends MY_Controller {
function __construct()
{
parent::__construct();
// Load any front-end only dependencies
}
}
Back end controllers will extend Admin_Controller, and front end controllers will extend Public_Controller. The front end base controller is not really necessary, but there as an example, and can be useful. You can extend MY_Controller instead if you want.
Use URI routing where needed, and create separate controllers for your front end and back end. All helpers, classes, models etc. can be shared if both the front and back end controllers live in the same application.
I use a very simple approach: file folders. Check out the CI User Guide section, Organizing Your Controllers into Sub-folders.
I have my public-facing website built as any other would be built with CodeIgniter. Then I have two additional folders, controllers/admin and views/admin.
The admin controllers are accessed via http://[hostname]/admin/controller, and behave just as any other controller except they have specific authentication checks. Likewise, the views are simply called with the folder name included: $this->load->view('admin/theview');.
I haven't found a reason to do anything more complicated than that.
You all can find complete solution over here, https://github.com/bhuban/modular
Module separation for admin and front-end using HMVC and template separation using template libraries
I am using two third party libraries, you can find it in zip file.
HMVC for modular developed by wiredesignz
Template engine for templating by Phil Sturgeon
Just unzip it into your webserver root directory and run
localhost/modular for front-end
and
localhost/modular/admin for back-end
application/back-modules, it is for the back-end modules
application/front-modules, it is for the front-end modules
similarly
templates/admin for the back-end templates
templates/front for the front-end templates
themes/admin for the back-end themes
themes/front for the front-end themes
Nothing hacked in original code just configured using config.php and index.php
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.
I'm working on an Application, and most of the code will be returning in future project. So my idea was to create a structure such as this:
App/
- controllers
- models
- ...
Cake/
My_Custom_Folder/
- controllers
- models
- ..
Basically I want to add another folder next to the normal App folder and use App::build() to set the paths of the extra folder controllers, models, etc.
Now the goal is to only use the App layer when my project requires custom work, I have succeeded in that and CakePHP takes the controllers in the App over the ones in My_Custom_Folder if they are there.
For example: If i have a pages_controller.php in My_Custom_Folder AND one in my App folder, it will use the one in the App folder. If there is none in the App folder, then it uses the one in My_Custom_Folder.
However that is not how I want it to be working, what I want to do is extend the controllers from My_Custom_Folder so I can override methods and/or add new ones.
So far I have tried the following:
/My_Custom_Folder/pages_controller.php
Class PagesController Extends AppController {
Public $name = 'Pages';
Public Function home(){
echo 'default home';
}
}
/App/pages_controller.php
require_once(ROOT . DS . 'My_Custom_Folder' . DS . 'controllers' . DS . 'pages_controller.php');
Class AppPagesController Extends PagesController {
Public Function home(){
echo 'Override default home';
}
}
Unfortunately this doesn't work, it still loads the default home. Basically what i want is for it to load all methods from the My_Custom_Folder and allow the App folder to override methods and/or add methods the same way as you would by extending the Appcontroller. It does load both files, but it's not overriding any functions.
A structure like this would be great to maintain the same code over many projects and allow me to easily add new functionality for all projects by just updating the My_Custom_Folder and it would not break projects that have some customized code.
Any help on this would be greatly appreciated.
Take a look at plugin or behaviors or components or helpers. With plugins you can put them in one common folder for all apps (just like your My_Custom_Folder).
i was wondering how to organize and name my classes
The goal:
classes
books
controller
model
view
authors
controller
model
view
// thanks for helping
I think you'll need to use the modules folder.
A module is like a mini Kohana application, for example, a simple module might have
/modules/first_module/...
config/
classes/controller/
classes/model/
views/
However if that isn't what you want, just follow the same directory structure above under the application folder.
Update
Okay, so if you want a book set of controllers, you may lay it out like so (if you wanted them in sub folders)... (example only...)
controllers/books/base.php
class Controller_Books_Base extends Controller {
}
controllers/books/fiction.php
class Controller_Books_Fiction extends Controller_Books_Base {
}
And a route like so in bootstrap.php
Route::set('books_fiction', 'fiction-books/<action>/<id>', array('action' => '(create|read|update|delete)', 'id' => '\d+')
->defaults(array(
'controller' => 'Books_Fiction'
));
Some example classes for Books and Authors.
class Model_Book {
// crud functions
}
class Model_Author {
// crud functions
}
class Controller_Book {
public function view($id) {
$book = Model::factory('Model_Book')->get($id);
$this->template->bookDetails = $book;
}
}
That will hopefully get you started.