zendframework 2 layout's controller - php

i'm new to zend framework 2 and have stumbled on this problem that i can't find a answer to:
i have a layout.phtml and i want before the layout loads to have a (layout) controller fetch from my db some junk and pass it the the layout to render, so no matter if it is my application module or any other module that is running, the layout will always use the same controller.
my modules are:
--module
--application
--...
--src
--application
--controller
LayoutController.php <-- where i would like to hold my layout controller
--view
-- ...
--layout
layout.phtml <-- where i hold my layout
--shop
-- ... <-- shop module that uses the application's layout
and my module.config.php layout is defined:
'view_manager' => array(
....
'template_map' => array(
'layout/layout' => __DIR__ . '/../view/layout/layout.phtml',
thanks!

If you want to fetch some data from database and pass to layout, try this in your Module.php:
public function onBootstrap(MvcEvent $e)
{
//...
$application = $e->getApplication();
$sm = $application->getServiceManager();
$application->getEventManager()->getSharedManager()
->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch',
function($e) use ($sm) {
$dbResult = $sm->get('YourModule\Model\FooTable')->bar();
$sm->get('ControllerPluginManager')->getController()->layout()->dbResult = $dbResult;
}
, 2
);
//...
}
And in your layout.phtml use $this->dbResult for your database result.

Related

How to render a string with variables rather than a template file in Zend Framework 2

I can easily render a template with variables from a file with some code like:
$renderer = new PhpRenderer();
$vm = new ViewModel();
$resolver = new TemplateMapResolver();
$resolver->setMap($this->templateMap);
$renderer->setResolver($resolver);
// Set the template to use and pass in variables as you normally would a view
$vm->setTemplate($template);
if ($vars) {
$vm->setVariables($vars);
}
$content = $renderer->render($vm);
I am curious how I can provide a string to setTemplate rather than a path to a template file. This way, the content being passed in can come from various sources such as an administrator's panel or database.
In the configuration example in the documentation you can see that it is possible to define a template_map inside your view_manager config array. A template map is like an array of aliases for your template files. So inside your module.config.php:
'view_manager' => array(
//...
'template_map' => array(
'name_from_admin_panel' => __DIR__ . '/../view/layout/view.phtml',
'name_from_database' => __DIR__ . '/../view/layout/view.phtml',
)
//...
)
Now you can use these names from your template map to set the template in your ViewModel as normally:
$template = 'name_from_admin_panel';
$viewModel->setTemplate($template);

Custom layout from public folder in Zend Framework 2

I have templates folder in my public folder.
I want to have opportunity to upload different layouts form my admin panel and put them to public/templates folder.
How can I change standard layout then?
$template = 'christmas.phtml';
$viewModel = new ViewModel();
$viewModel->setTemplate('/../../public/templates/'.$template);
isn't working :(
I also tried this way (changing layout) up from current:
$this->layout('../../public/templates/'.$template);
I found the solution. I've added one line to my module.config.php:
'view_manager' => array(
...
'template_path_stack' => array(
__DIR__ . '/../view',
__DIR__ . '/../../../public' // newLine
),
),
Then simply use this in controller:
$template = 'sometemplate.phtml';
$this->layout('templates/'.$template);
can you try $viewModel->setTemplate(APPLICATION_PATH .'/../public/templates/'.$template); or $viewModel->setTemplate('./../../public/templates/'.$template); instead of $viewModel->setTemplate('/../../public/templates/'.$template);. Because I guess you want to go up from the application directory, not from the root directory and $template = 'christmas.phtml'; should be $template = 'christmas';
If there is no APPLICATION_PATH constant you can define it in index.php file. If you dont like to define it, you can use realpath('<path/to/your/public/templates/>') method.

Access to module config in Zend Framework 2

How I can get access to my module config from the controller?
I am really surprised at how obscure this is, because I had exactly the same problem and could not find a definitive answer. One would think the ZF2 documentation would say something about this. Anyhow, using trial and error, I came across this extremely simple answer:
Inside controller functions:
$config = $this->getServiceLocator()->get('Config');
Inside Module class functions (the Module.php file):
$config = $e->getApplication()->getServiceManager()->get('Config');
whereas $e is an instance of Zend\Mvc\MvcEvent
In general, the config is accessible from anywhere you have access to the global service manager since the config array is registered as a service named Config. (Note the uppercase C.)
This returns an array of the union of application.config.php (global and local) and your module.config.php. You can then access the array elements as you need to.
Even though the OP is quite old now, I hope this saves someone the hour or more it took me to get to this answer.
What exactly do you want to do in your controller with the module configuration? Is it something that can't be done by having the DI container inject a fully configured object into your controller instead?
For example, Rob Allen's Getting Started with Zend Framework 2 gives this example of injecting a configured Zend\Db\Table instance into a controller:
return array(
'di' => array(
'instance' => array(
'alias' => array(
'album' => 'Album\Controller\AlbumController',
),
'Album\Controller\AlbumController' => array(
'parameters' => array(
'albumTable' => 'Album\Model\AlbumTable',
),
),
'Album\Model\AlbumTable' => array(
'parameters' => array(
'config' => 'Zend\Db\Adapter\Mysqli',
)),
'Zend\Db\Adapter\Mysqli' => array(
'parameters' => array(
'config' => array(
'host' => 'localhost',
'username' => 'rob',
'password' => '123456',
'dbname' => 'zf2tutorial',
),
),
),
...
If you need to do additional initialization after the application has been fully bootstrapped, you could attach an init method to the bootstrap event, in your Module class. A blog post by Matthew Weier O'Phinney gives this example:
use Zend\EventManager\StaticEventManager,
Zend\Module\Manager as ModuleManager
class Module
{
public function init(ModuleManager $manager)
{
$events = StaticEventManager::getInstance();
$events->attach('bootstrap', 'bootstrap', array($this, 'doMoarInit'));
}
public function doMoarInit($e)
{
$application = $e->getParam('application');
$modules = $e->getParam('modules');
$locator = $application->getLocator();
$router = $application->getRouter();
$config = $modules->getMergedConfig();
// do something with the above!
}
}
Would either of these approaches do the trick?
for Beta5, you can add function like this in Module.php
public function init(ModuleManager $moduleManager)
{
$sharedEvents = $moduleManager->getEventManager()->getSharedManager();
$sharedEvents->attach(__NAMESPACE__, 'dispatch', function($e) {
$config = $e->getApplication()->getConfiguration();
$controller = $e->getTarget();
$controller->config = $config;
});
}
in controller, you can get config :
print_r($this->config);
To read module-only config your module should just implement LocatorRegisteredInterface
Before:
namespace Application;
class Module
{
// ...
}
After:
namespace Application;
use Zend\ModuleManager\Feature\LocatorRegisteredInterface;
class Module implements LocatorRegisteredInterface
{
// ...
}
That implementation says LocatorRegistrationListener to save module intance in service locator as namespace\Module
Then anywhere you can get access to your module:
class IndexController extends AbstractActionController
{
public function indexAction()
{
/** #var \Application\Module $module */
$module = $this->getServiceLocator()->get('Application\Module');
$moduleOnlyConfig = $module->getConfig();
// ...
}
}
There is a pull request ready now which pulls the module class (so the modules/foo/Module.php Foo\Module class) from the DI container. This gives several advantages, but you are also able to grab that module instance another time if you have access to the Zend\Di\Locator.
If your action controller extends the Zend\Mvc\Controller\ActionController, then your controller is LocatorAware. Meaning, upon instantiation your controller is injected with the locator knowing about modules. So, you can pull the module class from the DIC in your controller. Now, when your module consumes a config file and stores this inside the module class instance, you can create a getter to access that config data from any class with a locator. You probably have already an accessor with your module Foo\Module::getConfig()
While ZF2 is heavily under development and perhaps this code will change later on, this feature is currently covered by this test, with this the most relevant part:
$sharedInstance = $locator->instanceManager()->getSharedInstance('ListenerTestModule\Module');
$this->assertInstanceOf('ListenerTestModule\Module', $sharedInstance);
So with $sharedInstance your module class, you can access the config from there. I expect a shorthand for this feature soon, but this can only be done after PR #786 has been merged in ZF2 master.
You need to implements ServiceLocatorAwareInterface from your model. And then you can set setServiceLocator() and getServiceLocator() which give you direct access to the service manager. Take a look at this code sample https://gist.github.com/ppeiris/7308289
I created the module with controller plugin and view helper for reading a config in controllers and views. GitHub link __ Composer link
Install it via composer
composer require tasmaniski/zf2-config-helper
Register new module "ConfigHelper" in your config/application.config.php file
'modules' => array(
'...',
'ConfigHelper'
),
Use it in controller and view files
echo $this->configHelp('key_from_config'); // read specific key from config
$config = $this->configHelp(); // return config object Zend\Config\Config
echo $config->key_from_config;
you can also access any config value anywhere by this hack/tricks
$configReader = new ConfigReader();
$configData = $configReader->fromFile('./config.ini');
$config = new Config($configData, true);

ZF2: Dependency Injection, MVC, Configurations and Bootstrap

I have a questiom regarding the Zend Framework 2:
I have
library/System and library/Zend. the system is my custom library, which I want to configure de aplication (routes, modules, etc., and redirect user to correct module, controller and/or action).
I don't want to do this inside each application/modules/ModuleName/Module.php file. So, my library/System can do everything related to application configuration.
As said in the comments above: register to the bootstrap-event and add new routes there:
<?php
namespace Application;
use Zend\Module\Manager,
Zend\EventManager\StaticEventManager;
class Module
{
public function init(Manager $moduleManager)
{
$events = StaticEventManager::getInstance();
$events->attach('bootstrap', 'bootstrap', array($this, 'initCustom'), 100);
}
public function initCustom($e)
{
$app = $e->getParam('application');
$r = \Zend\Mvc\Router\Http\Segment::factory(array(
'route' => '/test',
'defaults' => array(
'controller' => 'test'
)
)
);
$app->getRouter()->addRoute('test',$r);
}
}
$app = $e->getParam('application'); does return an instance of Zend\Mvc\Application. Have a look there to see which additional parts you can get there. The bootstrap event is fired before the actual dispatching does happen.
Note that the ZendFramework 1 routes are not always compatible to the ZendFramework 2 ones.
Update to comments
public function initCustom($e)
{
$app = $e->getParam('application');
// Init a new router object and add your own routes only
$app->setRouter($newRouter);
}
Update to new question
<?php
namespace Application;
use Zend\Module\Manager,
Zend\EventManager\StaticEventManager;
class Module
{
public function init(Manager $moduleManager)
{
$events = StaticEventManager::getInstance();
$events->attach('bootstrap', 'bootstrap', array($this, 'initCustom'), 100);
}
public function initCustom($e)
{
$zendApplication = $e->getParam('application');
$customApplication = new System\Application();
$customApplication->initRoutes($zendApplication->getRouter());
// ... other init stuff of your custom application
}
}
This only happens in one zf2 module (named Application which can be the only one as well). This doesn't fit your needs? You could:
extend a custom module autoloader
extend Zend\Mvc\Application for your own logic
make your code zf2-compatible

Unable to access module forms in module controller

I have a test module. In test module I have a Form in forms folder.
myproject/application/modules/test/forms/TestForm.php
class Test_Form_TestForm extends Zend_Form {
//form elements
}
myproject/application/modules/test/controllers/TestController.php
class Test_TestController extends Zend_Controller_Action {
public function indexAction() {
$this->view->form = new Test_Form_TestForm(); // this is generating error
}
} // end class
Form initialization in controller is generating following error:
Fatal error: Class 'Test_Form_TestForm' not found in C:\wamp\www\student\application\modules\notification\controllers\NotificationController.php on line 16
How to make this form accessable in controller. Same type of case is working with default controller. I know I have to register my module in bootstrap with Form_ indicator but dont know exact syntax.
You can also initialize multiple modules in a separate function in one Bootstrap file like:
protected function _initAutoloaders() {
$test_loader = new Zend_Application_Module_Autoloader( array( 'namespace' => 'Test',
'basePath' => APPLICATION_PATH . '/modules/test'
));
$mynew_loader = new Zend_Application_Module_Autoloader( array( 'namespace' => 'Mynew',
'basePath' => APPLICATION_PATH . '/modules/mynew'
));
}
In order for Zend Autoloader to work for your modules, you need to have bootstraps for all of your modules, and also modules resource initialized.
So, in your application/modules/test/Bootstrap.php:
class Test_Bootstrap extends Zend_Application_Module_Bootstrap {}
Upd:
And in your application/configs/application.ini:
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules[] =
More info about autoloading in modules here
Vika's answer is correct on how to setup modules autoloader.
Your error states that the form class cannot be found in notification module under NotificationController controller.
So you need to have bootstrap class for the notification module
In your application/modules/notification/Bootstrap.php:
class Notification_Bootstrap extends Zend_Application_Module_Bootstrap {}
I don't know if this is the best way, but it works.
In your bootstrap
...
$autoloader = new Zend_Loader_Autoloader_Resource(array('namespace' => '', 'basePath' => APPLICATION_PATH));
$autoloader->addResourceType('Test_Form', '/test/forms', 'Test_Form');
...
Vika's answer seems to be correct.
If you still having problems, try modify your application.ini
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.frontController.moduleDefault = "test"
resources.modules[] = "test"
resources.modules[] = "other"
If you specify exact module names in resource list, Zend will auto-magically register the Form and other resource auto-loaders. In debugging case modules/test/Boostrap.php should be triggered and any _init method inside. Have fun.

Categories