I am struggling with namespaces in Zend Framework (at least I think it's a namespace issue).
I want to integrate PHPExcel into my Zend project. Relevant file structure is as follows:
/
-library
-ABCD
-PHPExcel
-Zend
-ZendX
-PHPExcel.php
Custom classes work fine, after
Zend_Loader_Autoloader::getInstance()->registerNamespace('ABCD_');
in the bootstrap. Also, those classes are all named ABCD_blahdeblah.
However, doing registerNamespace('PHPExcel_') doesn't help Zend find the appropriate classes. When I try
$sheet = new PHPExcel;
in the controller, I get a "Class not found" error. I am guessing that this is either because classes in PHPExcel aren't named with the namespace prefix, or because the main PHPExcel.php file sits outside of the namespace I've just declared. But the PHPExcel structure demands that it sit in the parent directory of the rest of the class/font/etc files.
Any pointers would be greatly appreciated.
Thanks in advance.
Create an autoloader for PHPExcel and add it to the Zend autoloader stack.
In library/My/Loader/Autoloader/PHPExcel.php:
class My_Loader_Autoloader_PHPExcel implements Zend_Loader_Autoloader_Interface
{
public function autoload($class)
{
if ('PHPExcel' != $class){
return false;
}
require_once 'PHPExcel.php';
return $class;
}
}
And in application/configs/application.ini:
autoloadernamespaces[] = "My_"
Then, in application/Bootstrap.php:
protected function _initAutoloading()
{
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->pushAutoloader(new My_Loader_Autoloader_PHPExcel());
}
Then you should be able to instantiate PHPExcel - say, in a controller - with a simple:
$excel = new PHPExcel();
The only sticky part is all of this is how PHPExcel handles loading all its dependencies within its own folder. If that is done intelligently - either with calls like require_once basename(__FILE__) . '/someFile.php' or with its own autoloader that somehow doesn't get in the way of the Zend autoloader - then all should be cool. #famouslastwords
Nowadays composer is a frequently used tool that wasn't so popular back in 2012. Even older projects built in ZF1 can make use of composer and its autoloader.
How to get all your libraries to work without having to add custom autoloaders to your application.ini each time?
Make use of composer's autoloader
First, start with setting up composer.json. Once created, run composer install to gather all required packages and create composer's autoloader.
Now, let's update your project's public/index.php. From now on all requirements that are loaded via composer will be autoloaded.
<?php
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'development'));
// Include composer autoloader
require_once __DIR__ . '/../vendor/autoload.php';
/** Zend_Application */
require_once 'Zend/Application.php';
// Create application, bootstrap, and run
$application = new Zend_Application(
APPLICATION_ENV,
array( 'config' => APPLICATION_PATH . '/configs/application.ini' )
);
$application->bootstrap();
$application->run();
Try modifying the PHPExcel autoloader:
Add
if (function_exists('__autoload')) {
spl_autoload_register('__autoload');
}
as the first two lines of the Register() method in /Classes/PHPExcel/Autoloader.php, immediately before
return spl_autoload_register(array('PHPExcel_Autoloader', 'Load'));
I've had a similar problem with both an exel-librare (phpxls) and a pdf-library (fpdf) and after some different tries I just settled with including the required file from the library manually and go from there. Booth phpxls and fdpd can then handle everything else without interfering with the zend autoloader methods.
A psudo_code example would look like this, where I return a object of the desired class and then can continue to work with that. You could offcourse choose to include things in the constructor and build from that.
<?php
class exelClass{
public function exelFunction(){
require_once 'required_file.php';
$exelObject = new exelObject();
return $exelObject->Output();
}
}
?>
This solution might not be that elegant, but I found that it was the easiest way to enable different types of libraries to co-exist without differnet autoloaders or magic functions interfearing with each other.
Related
I have come across a strange problem with Zend_Framework, I can not load forms or models from any module other than default.
I have the following line of code in my controller (in the user module)
$myAccountModel = new User_Model_MyAccount();
However all i get is the error
Fatal error: Class 'User_Model_MyAccount' not found in F:\My Webs\freedate\application\modules\user\controllers\MyAccountController.php on line 13
I have checked the files path and class name and all is correct, I have this project setup similar to other ZF projects I have done in the past, the only difference is that this is on a WAMP server rather than a LAMP server.
If I try to load a model or form from the default module it loads fine, these line of code produce no errors and the classes load fine.
$loginForm = new Form_Login();
$loginModel = new Model_Login();
If someone can shed some light as to what is going on here it would be most helpful as I have now hit a brick wall!!
I am using ZF 1.11.11 btw.
Many thanks
Garry
[EDIT]
This is definatly a probelm with ZF autoloader, if I add the following line the error goes away.
require_once(APPLICATION_PATH . '/modules/user/models/MyAccount.php');
In my application.ini i have
; modules
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules[] =
and in my bootstrap.php I have
/** * Initialize autoloader
* #return Zend_Loader_Autoloader
*/
protected function _initAutoload() {
return Zend_Loader_Autoloader::getInstance();
}
/** * Initialize module autoloader
* #return Zend_Application_Module_Autoloader
*/
protected function _initModuleAutoload() {
$modelLoader = new Zend_Application_Module_Autoloader(array(
'namespace' => '',
'basePath' => APPLICATION_PATH . '/modules/default'));
return $modelLoader;
}
Have you tried to add an empty Module_Bootstrap to the specific module? Like so:
<?php
class User_Bootstrap extends Zend_Application_Module_Bootstrap
{// nothing in here except you really want to do anything here ;)}
Once you do this, you can remove the _initAutoload() and _initModuleAutoload() methods from your bootstrap. When you create a module bootstrap, the Zend_Application_Module_Autoloader is automatically created by default for that module.
See The Module Resource Autoloader
Zend framework.
I want to autoload my models classes inside models folder, from within bootstrap class.
These models doesnt actually use any namespace (so I have Ex. User.php file's class named User and so on..).
If I understood correctly I should use the Zend_Loader_Autoloader_Resource and I tried:
function _initLoaderResource()
{
$resourceLoader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => APPLICATION_PATH,//points to the "application" path where resides "models" folder
'namespace' =>''
));
$resourceLoader->addResourceType('models', 'models/');
}
And I receive following 'Zend_Loader_Exception' message:
'Initial definition of a resource type must include a namespace'
My questions are:
Is this the right way to autoload models?
How should I manage resource code that doesn't follow Zend Framework coding standard?
Actually you probably don't want to use the resource autoloader for this, since (as you've discovered) it requires a namespace. The standard autoloader (which loads models from the include path) has an option setFallbackAutoloader which tells ZF that that autoloader should be used for any class not matching a namespace covered by another. So all you need to do is ensure your models directory is on the include path and set this option to true.
You are probably already using the standard autoloader for loading the Zend classes, so you'll probably want to modify your application.ini file to add your model directory to the include path, and then set the fallback option either in application.ini or in your Bootstrap class:
protected function _initAutoloader()
{
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setFallbackAutoloader(true);
return $autoloader;
}
Zend Autoloader uses namespaces to make sure you are not using the autoload process, on those classes you don't want. So you would have to choose a namespace for your classes.
You could start your classes with an application specific namespace, or a general one.
namespaces like 'My_' or 'App_' are general, yet for example if your application name is Job Board, you could use namespaces like 'JB_' in your class files.
You may also write your own autoloader (either a totally new one, or by extending the Zend autoloader) and register it as the SPL autoloader to bypass this.
Your class names does not have to follow the Zend Framework naming conventions, just make sure they have a namespace and register the namespace in the autoloader.
Here I attach a piece of my code that registers some resources to be autoloaded. I'm having multiple modules, and each module has a namespace regarding that module name. Please note that since there were many namespaces, I register them all in a loop.
$nameSpaceToPath = array(
'Application' => APPLICATION_PATH,
'Base' => APPLICATION_PATH . '/base',
'Store' => APPLICATION_PATH . '/modules/Store',
'Payment' => APPLICATION_PATH . '/modules/Payment',
'Admin' => APPLICATION_PATH . '/modules/Admin'
);
foreach($nameSpaceToPath as $ns => $path) {
$autoLoaderResource = new Zend_Loader_Autoloader_Resource(
array(
'basePath' => $path,
'namespace' => $ns
)
);
$autoLoaderResource->addResourceType('controller','controllers','Controller');
$autoLoaderResource->addResourceType('model','models','Model');
$autoLoaderResource->addResourceType('mapper','models/mappers','Model_Mapper');
$autoLoaderResource->addResourceType('service','services','Service');
// I'm using _Util_ in the name of my utility classes, I place them in 'utils' directory
$autoLoaderResource->addResourceType('util','utils','Util');
$autoLoaderResource->addResourceType('plugin','plugins','Plugin');
$autoLoaderResource->addResourceType('form','forms','Form');
// I'm using _Exception_ in the name of my module specific exception classes, I place them in 'exceptions' directory
$autoLoaderResource->addResourceType('exception','exceptions','Exception');
$autoLoader->pushAutoloader($autoLoaderResource);
}
When you are defining a resource type by calling:
$autoLoaderResource->addResourceType('service','services','Service');
You are actually telling Zend Autoloader that you have a type 'service' (1st param), which is placed in the directory named 'services' (2nd param), and you are using 'Service' token in the class names to specify classes of this type.
The above code tells Zend Autoloader to search for class Store_Service_Core in the path 'APPLICATION_PATH/modules/store/services/Core.php'.
As you can see I have registered the general 'Application' namespace for the APPLICATION_PATH path. This means that each class, starting with Application_ would be autoloaded from the APPLICATION_PATH. So forexample I have a class named Application_Init which uses some initialization tasks, and now Zend autoloads it from the path APPLICATION_PATH/Init.php.
How can I make every module have it's own layouts directory?
I.e. when I don't have any modules my layout entry in config file looks like this:
resources.layout.layoutPath = APPLICATION_PATH "/layouts"
I try entering i.e.
; Layout directory for admin module
admin.resources.layout.layoutPath = APPLICATION_PATH "/modules/admin/layouts"
Where admin is module name; but it doesn't work. For some strange reason ZF looks for module layouts in /module/admin/views/scripts directory.
I also have a separate module.ini config file for every module as per this tutorial, alas layout path there gets ignored as well. Also I've been trying to follow this modules layout tutorial but it didn't work, I guess due to differences in ZF versions (tutorial is rather old). So I don't know what else to do
Using plugin from the tutorial you are talked about:
class My_Controller_Plugin_RequestedModuleLayoutLoader extends Zend_Controller_Plugin_Abstract {
public function preDispatch(Zend_Controller_Request_Abstract $request) {
$config = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getOptions();
$moduleName = $request->getModuleName();
if (isset($config[$moduleName]['resources']['layout'])) {
Zend_Layout::startMvc($config[$moduleName]['resources']['layout']);
}
}
}
application.ini
resources.frontController.plugins.layoutloader = My_Controller_Plugin_RequestedModuleLayoutLoader
module.ini:
resources.layout.layout = "Admin"
resources.layout.layoutPath = APPLICATION_PATH "/modules/admin/layouts/scripts"
Working fine.
A slightly alternate method to Ololo recommendation (which is a great way to do it)..
class YourApp_Controller_Plugin_Modulelayout extends Zend_Controller_Plugin_Abstract
{
public function routeShutdown(Zend_Controller_Request_Abstract $request)
{
$module = $request->getModuleName();
if ($module != 'default')
{
if (file_exists(APPLICATION_PATH . '/layouts/' . $module . '.html')) {
Zend_Layout::getMvcInstance()->setLayout($module);
}
}
}
}
Place this controller plugin in /library/YourApp/Controller/Plugin/Modulelayout.php
Then save your module layouts as the module name in your layouts folder (E.g., /layout/admin.phtml). If it does not find a layout for that module, it will default back to layout.phtml or whatever you originally set it to.
Have a look at this gist - https://gist.github.com/891384
This uses a combination of
Action helper to inspect the requested module and given a matching configuration, change the layout's layout and layoutPath properties in the preDispatch hook
Application resource plugin to capture module layout options, inject them into the above helper and add it to the helper broker
Happened to me too I got around it by using this line in my controller (I created a init function)
Zend_Layout::startMvc(array('layoutPath' => APPLICATION_PATH . '/modules/admin/layouts'));
As of Zend Framework 1.12 (Haven't tested it on previous releases):
Create your modules
Initialize the layout in your prefered fashion. For example in application.ini as zend tools does it:
resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
Create a layout inside each module with the default layout name inside the modules path to "views/scripts/" for example "application/modules/default/views/scripts/layout.phtml"
Don't forget to create one for the default module as it will be your fallback layout!
DO NOT create the default layout inside /application/layouts/scripts or this won't work
You are ready to run!
When Zend_Layout doesn't find the default layut it will look into the modules folders for it.
If you need some extra tweaking you may create a plugin and assign it to the layout object itself. For example, inside application.ini:
resources.layout.pluginClass = "MyLibrary_Controller_Plugin_Layout"
...or in the Bootstrap:
Zend_Layout::getMvcInstance()->setPluginClass("MyLibrary_Controller_Plugin_Layout");
Cheers!
I am learning how to use Zend framework and realise that the action helper is something that would be useful.
I have set up a default installation of Zend on my machine, but I dont know where the helper file needs to go, what I need to put in the bootstrap file and how I use it. Can anyone point me in the right direction please - the ZF user guide is not to clear to me.
Thanks
John
Two thoughts for where to place your custom action-helpers:
In a separate, custom library
In the folder application/controllers/helpers
These ideas are not exclusive. Functionality that is general enough to work in multiple projects should probably be pulled into a separate library. But for functionality that is application-specific, there is an argument that it could be somewhere in the application folder.
#Jurian has already described the "separate-library" approach. For app-specific helpers, you can do as follows:
For a helper called myHelper, create a class Application_Controller_Helper_MyHelper in the file application/controllers/helpers/MyHelper.php. In Bootstrap, you have something like:
protected function _initAutoload()
{
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'Application',
'basePath' => APPLICATION_PATH,
));
Zend_Controller_Action_HelperBroker::addPath(
APPLICATION_PATH . '/controllers/helpers',
'Application_Controller_Helper_');
return $autoloader;
}
Then your helper can be invoked in a controller by using:
$this->_helper->myHelper;
As you can see, this presumes you are using appNamespace 'Application'. If not, you can (must!) modify your class names to accommodate your circumstance.
Cheers!
You can place action helpers in your own library. Besides library/Zend where all the Zend stuff is around, you can create a library/MyLibrary folder (MyLibrary is arbitrary chosen) and put the action helpers there.
A good place is the library/MyLibrary/Controller/Action/Helper folder you need to create and place your action helper there (i.e. Navigation.php). In this file, create the class MyLibrary_Controller_Action_Helper_Navigation.
The next step is to add the action helper to the HelperBroker of the Zend Framework during bootstrap. Therefore, create a new method in your Bootstrap.php file and add this function:
protected function _initActionHelpers ()
{
Zend_Controller_Action_HelperBroker::addHelper(
new MyLibrary_Controller_Action_Helper_Navigation()
);
}
One last remark is you need to configure the use of this library by adding this rule to your application.ini:
autoLoaderNameSpaces[] = "MyLibrary_"
You can do this through your application.ini file like so
resources.view[] =
resources.view.helperPath.Default_View_Helper_ = APPLICATION_PATH "/views/helpers/"
Then in your views/helpers path you can create a file like Time.php. This file would contain the following code:
<?php
class Default_View_Helper_Time extends Zend_View_Helper_Abstract
{
public function time()
{
$date = new Zend_Date();
return $date->get(Zend_Date::TIME_MEDIUM);
}
}
?>
To use this in your view scripts you would use
<?=$this->time()?>
Which would display the current time using your new View_Helper
You can avoid having to register your action helper namespace and path within the Bootstrap.php by declaring them in the application.ini instead like so:
resources.frontController.actionHelperPaths.My_Controller_Action_Helper = APPLICATION_PATH "/controllers/helpers"
Simply replace My_Controller_Action_Helper with your desired namespace, and modify the helpers directory path accordingly.
The helper can be initialized the same way:
$this->_helper->myHelper;
As mentioned by the docs, registering the prefix or path of the helpers is usually preferred because helpers would not be initialized until they are called like in the snippet above.
Of course, instantiating and passing helpers to the broker is a bit
time and resource intensive, so two methods exists to automate things
slightly: addPrefix() and addPath().
http://framework.zend.com/manual/1.12/en/zend.loader.pluginloader.html
Adding the config entry to the application.ini follows the same suggested pattern.
Trying to make a controller helper to have similar functionality in some controllers using the preDispatch method.
Error:
Fatal error: Class 'Helper_Action_Test' not found in /var/www/zend.dev/application/Bootstrap.php on line 9`
Application layout
/Application
/Helpers
**/Actions** this is where i will save the classes
/Views
/modules
/configs
/layouts
/Bootstrap.php
In the Bootstrap I have added:
protected function _initActionHelpers(){
Zend_Controller_Action_HelperBroker::addHelper(new Helper_Action_Test());
}
In the helper file I have:
class Helper_Action_Test extends Zend_Controller_Action_Helper_Abstract{
public function preDispatch() {
echo 'Test';
}
}
When I do this in the bootstap it works, it might have to do with the include or how I am trying to instantiate the new class with the addHelper();
include(APPLICATION_PATH.'/helpers/action/Test.php');
Zend_Controller_Action_HelperBroker::addHelper(new Test());
Any ideas?
try this one:
// Action Helpers
Zend_Controller_Action_HelperBroker::addPath(
APPLICATION_PATH .'/controllers/helpers');
$hooks = Zend_Controller_Action_HelperBroker::getStaticHelper('Quote');
Zend_Controller_Action_HelperBroker::addHelper($hooks);
You have to include the helper file bootstrap file I think.
Or I think you want to: require_once() it
By adding the following lines in your config file you will be able to achieve what you want
; Include path
includePaths.library = APPLICATION_PATH "/../library"
; Autoloader Namespace
autoloaderNamespaces[] = 'Helper_'
More info in the official ZF doc Autoloader
To solve your problem, make sure the _initAutoload() on your bootstrap is the first method and also make sure you have added the prefix path:
Zend_Controller_Action_HelperBroker::addPrefix('Helper_Action');
You can aslo provide the path to the classes if they are not on the include_path:
Zend_Controller_Action_HelperBroker::addPath(APPLICATION_PATH . '/helper/action/', 'Helper_Action');