I want to put models outside module directory in Zend Framework. To be precise, in /library folder
library/
models/
actors/
ActorsMapper.php
Actor.php
books/
BooksMapper.php
Book.php
INSTEAD OF
application/
modules/
models/
actors/
ActorsMapper.php
Actor.php
books/
BooksMapper.php
Book.php
This is done so that I don't have to create separate model for each of the module I create.
What configurations will I have to change?
If you need more details, please ask.
Thank you :)
First answer works, but I will give you another answer in case if you want to register autoload in bootstrap.
1) put 'models' folder into library with all Table.php files.
Every model/class should have:
class Model_Table extends Zend_Db_Table_Abstract{ ... }
2) In bootstrap.php put:
protected function _initAutoLoad() {
// Add autoloader empty namespace
$autoLoader = Zend_Loader_Autoloader::getInstance();
$resourceLoader = new Zend_Loader_Autoloader_Resource(
array(
'basePath' => APPLICATION_PATH,
'namespace' => '',
'resourceTypes' => array(
'model' => array(
'path' => '../library/models/',
'namespace' => 'Model_'
),
),
)
);
return $resourceLoader;
}
That's it. Now you can use your models in controllers like this:
$model = new Model_Table();
If you want to use same models for all modules you can put it in application folder applications/models
and that works fine just like when you have web without modules.
But If you want to have models in library, You can put 'models' folder in your library path and autoload it.
Create folder 'Models' into library with all Table.php files.
In configs/application.ini Put:
autoloaderNamespaces.models = "Models_"
Then you can use namespace 'Models_' in your web application
In controller:
$model = new Models_Table();
in any case, I recommend that you keep the folder models in the path application/models
zend autoloader loading class which name beginning with prefix, that is registred on it.
If you have library in your include path, you can simple register "Models" like default namespace on autoloader and name your class for example Models_Actors_Actor.
Related
I'm trying to create modules in Codeigniter 4 to work with HMVC. I tried following this user guide https://codeigniter4.github.io/userguide/general/modules.html, but cannot get it working.
I created a 'modules' folder, alongside the app, public, etc. folders.
Added to app/config/autoload.php
'Modules' => ROOTPATH.'modules'
Inside the modules folder, I created a 'Proef' folder, containing a Controllers folder and 'Proef.php' file.
The file contains the following;
namespace App\Modules\Proef\Controllers;
class Proef extends \CodeIgniter\Controller
{
public function index() {
echo 'hello!';
}
}
In the app/config.routes.php file I added
$routes->group('proef', ['namespace' => 'Modules\Proef\Controllers'], function($routes)
{
$routes->get('/', 'Proef::index');
});
Yet, the following error persists:
Controller or its method is not found: \Modules\Proef\Controllers\Proef::index
What am I missing?
If you put your modules folder "alongside" and not under your app folder, then your namespace is wrong.
So you would have something like
app/
Modules/ ==> can be modules or Modules but must be set in autoload with the same case
Proef/
Controllers/
Proef.php
NOTE: modules can be Modules or modules but the corresponding entry in the autoload must match.
For modules
'Modules' => ROOTPATH . 'modules'
For Modules
'Modules' => ROOTPATH . 'Modules'
It appears (from my limited testing) that the other folder names must
be 1st letter Upper case. This is under Apache on Linux.
let's use Modules for the folder name so in Autoload.php we would have...
$psr4 = [
'App' => APPPATH, // To ensure filters, etc still found,
APP_NAMESPACE => APPPATH, // For custom namespace
'Config' => APPPATH . 'Config',
'Modules' => ROOTPATH . 'Modules'
];
So your Proef Controller - Proef.php ... Note the namespace being used.
<?php
namespace Modules\Proef\Controllers;
use App\Controllers\BaseController;
class Proef extends BaseController {
public function index() {
echo 'Hello - I am the <strong>'. __CLASS__ . '</strong> Class';
}
}
To make this accessible via the URL you can set the routes (Routes.php) to... (simple version)
$routes->get('/proef', '\Modules\Proef\Controllers\Proef::index');
To make it callable within other Controllers... ( I have borrowed Home.php for this)
<?php namespace App\Controllers;
use \Modules\Proef\Controllers\Proef;
class Home extends BaseController
{
public function index()
{
$mProef = new Proef();
$mProef->index();
return view('welcome_message');
}
//--------------------------------------------------------------------
}
In your URL -
/proef will result in the just the message
/home will result in the class message and the welcome page.
So hopefully this will help you figure this out. its a lot of fun :)
Aside:
You can put your Modules Folder anywhere. I put mine under app/ for ole times sake, which removes the need to add the entry in Autoload.php as they fall under app/ which is already defined.
The namespace and use statement need to be changed appropriately as well.
Edit
namespace to Modules\Proef\Controllers in Proef class
Here is my app structure:
/application
/config
/library
/Foo
/Controler.php
/module
/User
/config
/src
/Bar
/Controler
/BarController.php
/public
/vendor
/init_autoloader.php
The Controler.php file...
namespace Foo_Controller;
use Zend\Mvc\Controller\AbstractRestfulController;
class Foo_Controller extends AbstractRestfulController {
protected $foo;
public function getFoo()
{
return "foooo";
}
function __construct()
{
parent::__construct();
$foo = $this->getFoo();
}
}
The BarController.php...
namespace Bar\Controler;
use Zend\Mvc\Controller\AbstractRestfulController;
use Foo_Controller\Foo_Controller;
use Zend\View\Model\JsonModel;
class BarController extends Foo_Controller {
.
..
....
}
Added the path /library folder in the init_autoloader.php
$loader = include 'vendor/autoload.php';
$zf2Path = 'vendor/zendframework/zendframework/library';
$loader->add('Zend', $zf2Path);
$loader->add('Julia', 'library'); // added the library folder
if (!class_exists('Zend\Loader\AutoloaderFactory')) {
throw new RuntimeException('Unable to load ZF2. Run `php composer.phar install` or define a ZF2_PATH environment variable.');
}
I get an error 500 with the following:PHP Fatal error: Class 'Foo_Controller\Foo_Controller' not found in /application/module/Bar/src/Bar/Controller/BarController.php on line #
I really don't know what to do now. I have been searching the internet for some time now for the correct way to extend a controller class in Zend Frazmework 2, but i can't seem to grasp it!
What am i doing wrong in the app?
Thank you
I would suggest you set this out slightly differently. Would really make more sense to create your own custom module which you can load into any project with directory structure like:
/zf2-MyCustomModule
/src
/MyCustomModule
/Controller
/Abstract
/MyAbstractController.php
namespace for MyAbstractController.php would be - MyCustomModule\Controller\Abstract
If it is specific to the project then why not just add
/Abstract
/MyAbstractController.php
to the User Module Controller dir.
but seems like what you have done is pretty much right you would just need to update namespace in Foo_Controller.php to:
namespace Julia\Foo\Controller;
not
namespace Foo_Controller;
Though I have never used the method you are using so am not 100% sure.
I would add a new local config to /config/autoload/
like /config/autoload/namespaces.global.php
Zend\Loader\AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
'Julia' => __DIR__ . '/Library',
),
)
));
then your namespace should still be Julia\Foo\Controller;
I want to put projecct dependent custom class in zend folder structure. I googled and found that we can put it in models folder, but we use models folder for database related classes. So I am confuse. Please help where can i put it so i can access it by directory mapping?
This is my way:
I create in library path, folder 'My'.
There I put file Utils.php.
So in Utils.php I have class
class My_Utils { /* some functions... */ }
in configs/application.ini autoload namespace 'My' with this line:
autoloaderNamespaces.my = "My_"
So in controller you can use that class like
$utils = new My_Utils();
$utils->someFunction();
Other way, you can autoload that folder(My) in bootstrap
protected function _initAutoLoad() {
// Add autoloader empty namespace
$autoLoader = Zend_Loader_Autoloader::getInstance();
$resourceLoader = new Zend_Loader_Autoloader_Resource(
array(
'basePath' => APPLICATION_PATH,
'namespace' => '',
'resourceTypes' => array(
'model' => array(
'path' => '../library/My/',
'namespace' => 'My_'
),
),
)
);
return $resourceLoader;
}
Look up autoloading in Zend.
This will let you define your own application folder and then include the ZF library.
From this application folder you can do as you wish when it comes to folders and classes and the autoloader will load them all for you without the need for includes or requires.
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..
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'
),
),
);