Zend 2: How to change template for specific action? - zend-framework2

I'm looking for a way to change view template (NOT layout), but I don't have access to this module (it's part of vendor), so I can't do:
$view = new ViewModel();
$view->setTemplate($template);
It would be nice if I could do it by attaching to some event.
I have try following code:
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$eventManager->getSharedManager()->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch', function($e) {
$controller = $e->getTarget();
$routeMatch = $e->getRouteMatch();
$routeName = $routeMatch->getMatchedRouteName();
if ($routeName === 'login') {
$controller->layout('layout/layout');
$e->getViewModel()->setTemplate('views/login');
}
}, 1);
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
}
But it seems like it doesn't work that way. I mean, it changed my layout, it changed my view template, but when I try for example:
$this->form I get null. Looks like this way is clearing all variables from that action.
Any way how to make it working?

You can replace a template from other library to your template. Need to setup your module after module from library in a config file config/application.config.php.
'modules' => array(
'VendorModule',
'YourModule'
);
In a view manager config need change path to your template for template name of other library.
'view_manager' => array(
'template_map' => array(
'vendor/library/template_name' => __DIR__ . '/your_template.phtml',
)
)

In specific action you can switch the default layout:
$this->layout('layout/your-second-layout');
this is only for the layout to change in a specific action.

Related

ZF2 use view helper in Validator class

UPDATED!
ZF2, l10n view helper. I can't understand how to use my view helper inside of a class.
I want to use it like: $this->t('STRING TO TRANSLATE');
example bellow
NB! i'm only localizing project, i'm not allowed to change code structure or smth like that.Also i'm absolute newb in ZF2.
my Class -
class Project extends InputFilter{
as i understood i have to implement ServiceLocatorAwareInterface interface, tried this:
use Zend\ServiceManager\ServiceLocatorInterface as ServiceLocator;
class Project extends InputFilter implements ServiceLocator
{
protected $services;
public function __construct(Connection $p4, $mode, ServiceLocator $services)
{
$this->services = $services;
//some code
$this->add(...);
$this->add(
array(
'name' => 'name',
'filters' => array('trim'),
'validators' => array(
array(
'name' => 'NotEmpty',
'options' => array(
'message' => "Name is required and can't be empty."
)
),
array(
'name' => '\Application\Validator\Callback',
'options' => array(
'callback' => function ($value) use ($p4, $toId, $mode, $reserved) {
$id = $toId($value);
if (!$id) {
return $this->t('STRING TO TRANSLATE');
}
// more code here
return true;
}
)
)
)
)
);
//some code
$this->add(...);
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
$this->serviceLocator = $serviceLocator;
}
public function getServiceLocator() {
return $this->serviceLocator;
}
//how to get this method work ???
public function t($msg) {
$translate = $this->services->get('ViewHelperManager')->get('t');
return $translate($msg);
}
}
Usage in Controller:
use Projects\Filter\Project as ProjectFilter;
...
protected function doAddEdit($mode, $project = null)
{
$p4Admin = $this->getServiceLocator()->get('p4_admin');
$request = $this->getRequest();
// process add request.
if ($request->isPost()) {
// pull out the data
$data = $request->getPost();
// configure our filter with the p4 connection and add/edit mode
$filter = new ProjectFilter($p4Admin, $mode); //
$filter->setData($data);
// if the data is valid, setup the project and save it
$isValid = $filter->isValid();
if ($isValid) {
$values = $filter->getValues();
$project = new Project($p4Admin);
$project->set($values)
->save();
}
return new JsonModel(
array(
'isValid' => $isValid,
'messages' => $filter->getMessages(), // THESE array of messages i want to localize
'redirect' => '/projects/' . $filter->getValue('id')
)
);
}
// prepare view for form.
$view = new ViewModel;
$view->setVariables(
array(
'mode' => $mode,
'project' => $project ?: new Project
)
);
return $view;
}
What am i doing wrong ?
You're calling tr method inside a class constructor. tr method calls $this->getServiceLocator(). $this->getServiceLocator() will not return a service locator instance since it'll only be injected by service manager after it has created the object that implements ServiceLocatorAwareInterface.
So you'd have to pass a service locator instance to the constructor, or don't depend on it in your __construct method. Probably the easiest way to go is to move your code from the constructor to the init method. Init is supposed to get called automatically as long as you get your input filter through the InputFilterManager.
Also I don't think you need the translator view helper. You should be able to get it from the service manager like so: $serviceManager->get('translator')
There is no need to do this at all the validation message will be translated by the validator anyway. But your config is a bit wrong I think
$this->add(
array(
'name' => 'name',
'filters' => array('trim'),)
'validators' => array(
array(
'name' => 'NotEmpty',
'options' => array(
'messages' => array(
\Zend\Validator\NotEmpty::IS_EMPTY => 'YOUR_TRANSLATION_STRING_IS_EMPTY',
\Zend\Validator\NotEmpty::INVALID => 'YOUR_TRANSLATION_STRING_INVALID',
)
)
)
),
Have a read of https://zf2-docs.readthedocs.org/en/latest/modules/zend.validator.html#translating-messages
You will need to ensure you are managing your dependencies correctly for this to work so really depends how you are using the input filter.
If your not directly adding to a form or using InputFilterAwareInterface on your model you'll need to make sure your InputFilter is registered with InputFilterPluginManager and you retrieve it using InputFilterPluginManager rather than instantiating directly

Dynamically loading ZF2 Modules

I have following application.config
return array(
'modules' => array(
'Application',
'ErrorHandler'
),
'module_listener_options' => array(
'module_paths' => array(
'./module',
'./vendor'
),
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php'
)
)
);
and in the Application/Module.php I have (few of the functions):
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$this->initModules($e);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
private function getModules(MvcEvent $e) {
$sm = $e->getApplication()->getServiceManager();
$moduleTable = $sm->get('ModuleTable');
$modules = array();
foreach ($moduleTable->fetchAll() as $m) {
$modules[] = $m;
}
return $modules;
}
private function initModules(MvcEvent $e) {
$modules = $this->getModules($e);
$serviceManager = $e->getApplication()->getServiceManager();
$moduleManager = $serviceManager->get('ModuleManager');
$loadedModules = $moduleManager->getLoadedModules();
foreach ($loadedModules as $module) {
$this->loadedModules[] = str_replace('\Module', '', get_class($module));
}
foreach ($modules as $module) {
try {
$moduleManager->loadModule($module->getName());
$this->loadedModules[] = $module->getName();
} catch (\Exception $e) {
$this->failedModules[] = $module->getName();
}
}
if (count($this->failedModules) > 0) {
// Error in loading modules
exit;
}
return $this;
}
public function getServiceConfig()
{
return array(
'factories' => array(
'ModuleTable' => function($sm) {
return new ModuleTable($sm->get('Zend\Db\Adapter\Adapter'));
},
),
);
}
what I'm trying to achieve here is to have modules dynamically loaded based on a setting from database.
i get no error in loading modules ... when i try calling back $moduleManager->getLoadedModules(); i see that the module is in the loaded list but its config and its functionality doesnt work. Specifically i have routes in that module and when trying to access them i get 404. but if i include the module in the application.config all works perfect.
Possible to achieve? If yes any guidelines?
Thanks
UPDATE
I managed to get the modules dynamically loaded within the Module::init() method ... but without any success accessing the ServiceManager and/or db access to load the list of modules from db ...
This is an old question but I saw it today trying to do the exact same thing. My code is geared toward ZF3 but should work on ZF2:
https://github.com/basicinvoices/basicinvoices-modulemanager
The basics I've followed...
Wait until the modules have been loaded (ModuleEvent::EVENT_LOAD_MODULES_POST) this way we have access to the database configuration. I hooked into it with hight priority (9000) to be sure it is runned before other events.
At this point I load a Database adapter. The module services have NOT yet been assigned so we must create it by hand but it is easy. We search for active modules in the database which haven't been loaded yet and we load them with the ModuleManager::loadModule() method. We also add it to the modules array and set it back to the ModuleManager
The config is not merged and, to a certain point that is great as if the config has been cached we would have problems if the module has changed in out database... but it requires an extra step, we should check if the module has a config and, if it does, we shall merge it into the merged config ourselves.
...and that's about it.

ZF2 - get was unable to fetch or create an instance for getAlbumTable

I used the getAlbumTable in the title since the problem I'm facing is based on the Zend Album Tutorial, might be easier for others having a similar problem to find. All I've done is rename Album to "Champrallies".
My error:
Zend\Mvc\Controller\PluginManager::get was unable to fetch or create an instance for getChampralliesTable
What I'm trying to do is execute a function from a second Model I've created. I can execute from the 'original' model no problem AlbumTable or in my case ChampionshipTable. I'm trying to get data from a second table, but within the same module. The second table is called "champ_rallies" and the Model files are Champrallies.php and ChampralliesTable.php.
Changing this part in my controller
'champRallies' => $this->getChampralliesTable()->fetchAll(),
to
'champRallies' => $this->getChampionshipTable()->fetchAll(),
means the error message disappears. So my first thought is that there is something wrong with namespaces, or module.php. (Forgive me, I'm fairly new at all this)
Module.php
<?php
namespace Championship;
use Championship\Model\Championship;
use Championship\Model\ChampionshipTable;
use Championship\Model\Champrallies;
use Championship\Model\ChampralliesTable;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\TableGateway;
class Module
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
// Add this method:
public function getServiceConfig()
{
return array(
'factories' => array(
'Championship\Model\ChampionshipTable' => function($sm) {
$tableGateway = $sm->get('ChampionshipTableGateway');
$table = new ChampionshipTable($tableGateway);
return $table;
},
'ChampionshipTableGateway' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Championship());
return new TableGateway('championships', $dbAdapter, null, $resultSetPrototype);
},
'Championship\Model\ChampralliesTable' => function($sm) {
$tableGateway = $sm->get('ChampralliesTableGateway');
$table = new ChampralliesTable($tableGateway);
return $table;
},
'ChampralliesTableGateway' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Champrallies());
return new TableGateway('champ_rallies', $dbAdapter, null, $resultSetPrototype);
},
),
);
}
}
My second thought is that perhaps I'm not declaring the right namespace, or something similar.
ChampralliesTable.php
<?php
namespace Championship\Model;
use Zend\Db\TableGateway\TableGateway;
class ChampralliesTable
Champrallies.php
<?php
namespace Championship\Model;
class Champrallies
I fear I'm overlooking something failure but I haven't found any similar posts through google or on here, any help is appreciated!
edit: I had forgotten to add getChampralliesTable function to the controller itself,
public function getChampralliesTable()
{
This is Line 50 -> if (!$this->champralliesTable) {
$sm = $this->getServiceLocator();
$this->champralliesTable = $sm->get('Championship\Model\ChampralliesTable');
}
return $this->champralliesTable;
}
But now I'm getting this,
Notice: Undefined property: Championship\Controller\ChampionshipController::$champralliesTable in /usr/home/phil/git/rallystats/module/Championship/src/Championship/Controller/ChampionshipController.php on line 50
I had forgotten to add getChampralliesTable function to the controller itself,
public function getChampralliesTable()
{
This is Line 50 -> if (!$this->champralliesTable) {
$sm = $this->getServiceLocator();
$this->champralliesTable = $sm->get('Championship\Model\ChampralliesTable');
}
return $this->champralliesTable;
}
But now I'm getting this,
Notice: Undefined property: Championship\Controller\ChampionshipController::$champralliesTable in /usr/home/phil/git/rallystats/module/Championship/src/Championship/Controller/ChampionshipController.php on line 50
Solving this was just a matter of adding
protected $champralliesTable;
to the top of my ChampionshipController.
edit: Doing the above solves my problem.

How to Access Application Config from View in Zend Framework 2 (zf2)?

I wanted to access the application config from a view. How can I achieve that in ZF 2?
Actually you shouldn't need to access application config inside a view. In MVC, views just responsible for displaying/rendering data (output) and shouldn't contain any business or application logic.
If you really want to do that you can simply pass to view in your controller something like this:
<?php
namespace YourModule\Controller;
use Zend\View\Model\ViewModel;
// ...
public function anyAction()
{
$config = $this->getServiceLocator()->get('config');
$viewModel = new ViewModel();
$viewModel->setVariables(array('config' => $config ));
return $viewModel;
}
// ...
?>
So in your view.phtml file;
<div class="foo">
...
<?php echo $this->config; ?>
...
</div>
You should create a view helper.
Config.php
<?php
namespace Application\View\Helper;
class Config extends \Zend\View\Helper\AbstractHelper
{
public function __construct($config)
{
$this->key = $config;
}
public function __invoke()
{
return $this->config;
}
}
Module.php or theme.config.php
return array(
'helpers' => array(
'factories' => array(
'config' => function ($sm) {
return new \Application\View\Helper\Config(
$sm->getServiceLocator()->get('Application\Config')->get('config')
);
},
)
),
);
Then you can use config variables in any view.
echo $this->config()->Section->key;
I created the module with controller plugin and view helper for reading a config in controllers and views. GitHub link __ Composer link
After installation via composer you can use it easily.
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;

How to render a different view in controller action of ZF2

How to render a different view other than default in controller action. by default it try to find the same view as action in the view folder but I would like render different view available in views folder for controler action.
We can do this ZF1 like this $this->_helper->viewRenderer('foo');
Can Anyone know, how to achieve above in Zendframework 2?
We can disabled the view using
$response = $this->getResponse();
$response->setStatusCode(200);
$response->setContent("Hello World");
return $response;
I don't know how to change/render a different view in zf2.
can be done using
public function abcAction()
{
$view = new ViewModel(array('variable'=>$value));
$view->setTemplate('module/controler/action.phtml'); // path to phtml file under view folder
return $view;
}
Thanks to akrabat for covering almost every scenario.
My solution in Zend Framewor 2 is simple. For index action i prefer to call parrent::indexAction() constructor bcs we extend Zend\Mvc\Controller\AbstractActionController . Or just return array() in indexAction. ZF will atomaticly return index.pthml whitout definig what must be returned.
return new ViewManager() is the same return array()
<?php
namespace Test\Controller;
use Zend\Mvc\Controller\AbstractActionController,
Zend\View\Model\ViewModel;
// Or if u write Restful web service then use RestfulController
// use Zend\Mvc\Controller\AbstractRestfulController
class TestController extends AbstractActionController
{
/*
* Index action
*
* #return main index.phtml
*/
public function indexAction()
{
parent::indexAction();
// or return new ViewModel();
// or much simple return array();
}
/*
* Add new comment
*
* #return addComment.phtml
*/
public function addAction()
{
$view = new ViewManager();
$view->setTemplate('test/test/addComment.phtml'); // module/Test/view/test/test/
return $view;
}
Dont forget to configure route and view_manager in module/config/module_config
'view_manager' => array(
'template_path_stack' => array(
'Test' => __DIR__ . '/../view',
),
),

Resources