How to render a different view in controller action of ZF2 - zend-framework2

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',
),
),

Related

Zend 2: How to change template for specific action?

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.

ZF2 service manager use from a custom class

It looks like it has been touched several times already, but i still can't get it work. I set up an JSON-RPC server in a separate module, it works fine. Its functionality is in a new class Rpcapi. Now I want reuse DB related functions that already implemented in another module from that class. According to ZF2 docs my Rpcapi class has to be ServiceLocator-aware and it looks like I made it that way. Unfortunatelly still can't get it working. Please help keeping in mind that I'm new with ZF2 :)
Rpccontroller.php
namespace Rpc\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Json\Server\Server;
use Zend\Json\Server\Smd;
use Rpc\Model\Rpcapi;
class RpcController extends AbstractActionController
{
public function indexAction()
{
header('Content-Type: application/json');
$jsonrpc = new Server();
$jsonrpc->setClass(new Rpcapi);
$jsonrpc->getRequest()->setVersion(Server::VERSION_2);
if ($this->getRequest()->getMethod() == "GET") {
$smd = $jsonrpc->getServiceMap()->setEnvelope(Smd::ENV_JSONRPC_2);
echo $smd;
} else {
$jsonrpc->handle();
}
}
}
module.config.php for Rpc module
'service_manager' => array(
'invokables' => array(
'rpcapi' => 'Search\Model\SiteTable',
),
),
Rpcapi.php
namespace Rpc\Model;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class Rpcapi implements ServiceLocatorAwareInterface
{
protected $services;
protected $siteTable;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->services = $serviceLocator;
}
public function getServiceLocator()
{
return $this->services;
}
public function getSiteTable()
{
if (!$this->siteTable) {
$sm = $this->getServiceLocator();
$this->siteTable = $sm->get('rpcapi');
}
return $this->siteTable;
}
/**
* Returns list of all sites
*
*
* #return array
*/
public function getAllSites()
{
$results = $this->getSiteTable()->fetchAll();
$r = array ('1' => '1', '2' => 2); //Just to return something for now
return $r;
}
}
All I could get out is: Fatal error: Call to a member function get() on a non-object in /var/www/html/AmeriFluxZF2/module/Rpc/src/Rpc/Model/Rpcapi.php on line 28. Line 28 is:
$this->siteTable = $sm->get('rpcapi');
Any help is much appreciated!
Making the class service locator aware tells the ZF2 that the service locator should be injected into your class upon instantiation. However, you still need to use the service locator to instantiate this class, rather than creating an instance of it yourself, or this will never happen.
Your probably want to add a new entry to invokables for your Rpcapi class, and then grab this from the service locator instead of doing new Rpcapi in your controller.
PS: The naming of your classes is very confusing - you have an Rpcapi class, and an invokable called rpcapi, yet this invokable creates an instance of a completely different class?
If you want serviceLocator to be injected by the service manager in your Rpcapi, you must get it via the service manager itself :
'service_manager' => array(
'invokables' => array(
'rpcapi' => 'Search\Model\SiteTable',
'Rpc\Model\Rpcapi' => 'Rpc\Model\Rpcapi',
),
),
the action :
public function indexAction()
{
header('Content-Type: application/json');
$jsonrpc = new Server();
$jsonrpc->setClass($this->getServiceLocator()->get('Rpc\Model\Rpcapi'));
$jsonrpc->getRequest()->setVersion(Server::VERSION_2);
if ($this->getRequest()->getMethod() == "GET") {
$smd = $jsonrpc->getServiceMap()->setEnvelope(Smd::ENV_JSONRPC_2);
echo $smd;
} else {
$jsonrpc->handle();
}
}
And this is where you can see that your 'rcpai' name for SiteTable is not a good choice... ;)

ZF2 - Register/Login overlay in it's own action, JS to open and possible to deep link

I have overlays and a hidden panel on my site which uses AJAX to pull in content from another controller/action. You click a link which pushState the URL and AJAX pulls in the content with setTerminal(true); so there is no layout wrapped around. These overlay/hiddenpanels are login/register and would also like them to already be in HTML when you request the page without ajax (refresh) so they can be deeplinked to.
Currently I have something like this in the register controller/action:
public function registerAction () {
$request = $this->getRequest();
// Possible $form = new RegisterForm(); with validation and error population etc
// If we're not requesting via AJAX, forward dispatch to index
if (!$request->isXmlHttpRequest()) {
return $this->forward()->dispatch('index', array(
'action' => 'index',
'overlay' => 1
));
}
$view = new ViewModel();
$view->setTerminal(true);
return $view;
}
If there isn't an XmlHttpRequest then it forward dispatches to index/index with the following check:
public function indexAction () {
$routeRequest = $this->getEvent()->getRouteMatch();
$view = new ViewModel([]);
// Check if user went to somewhere like register page
$overlay = $routeRequest->getParam('overlay', null);
if (null !== $overlay) {
$view->overlay = true;
}
return $view;
}
In my layout I'm checking if the view has the overlay view variable set and then including the overlay in HTML for the previous action like so (controllerName and actionName ViewHelpers get populated onBootstrap so contain register as controller etc):
// Layout.phtml
if ($viewVariables->overlay) {
echo $this->partial('application/' . $this->controllerName() . '/' . $this->actionName() . '.phtml');
}
But this currently doesn't contain any data from the registerAction. I was thinking of having a registration form and having everything registration related done inside there as well and having it accessible where-ever I might forward dispatch to but now it's getting rather complex forwarding and passing variables about.
I was thinking of creating a ViewHelper like echo $this->overlay() which would contain ViewVariables from the previous view and include the other view as a partial but then also thought about passing the view from registerAction to indexAction via the forward dispatcher and nesting it.
Right now i'm triggering statechange with JS at the bottom of the page which takes the current URL and grabs it via AJAX but making the user wait for a full page render and then loading symbol after that is confusing when it can already be there.
This seems a rather complex problem as I'm not too familiar with what's available. I'm seeing alot of modules with the eventManager so am wondering how others would approach this?
Managed to do this and it was easier than I expected.
// RegisterController/indexAction
public function registerAction()
{
$request = $this->getRequest();
$view = new ViewModel();
// If we're not requesting via AJAX, forward dispatch to index
if (!$request->isXmlHttpRequest()) {
$view->setTemplate('application/register/index');
return $this->forward()->dispatch('index', array(
'action' => 'index',
'overlay' => $view
));
}
$view->setTerminal(true);
return $view;
}
// IndexController/indexAction
public function indexAction()
{
$request = $this->getRequest();
$routeRequest = $this->getEvent()->getRouteMatch();
$view = new ViewModel();
$overlay = $routeRequest->getParam('overlay', null);
if (null !== $overlay) {
$view->addChild($overlay, 'overlay');
}
return $view;
}
My only gripe with this is that I have to manually set the template for the child view before I forward dispatch it, as it gets populated onDispatch with a default path if none was specified.
public function registerAction () {
$request = $this->getRequest();
// Possible $form = new RegisterForm(); with validation and error population etc
// If we're not requesting via AJAX, forward dispatch to index
if (!$request->isXmlHttpRequest()) {
return $this->forward()->dispatch('index', array(
'action' => 'index',
'overlay' => 1
));
}
$view = new ViewModel();
$view->setTerminal(true);
return $view;
}

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;

ZF2 Service Locator

I'm quite new to zf2 and I'm experimenting with it. I have a view helper and I need it to access a table object. In my controller I can run:
$this->getServiceLocator();
But ideally I would run this inside my view helper. Unfortunately, I can't seem to access it from within my view helper. I tried passing it through the constructor, configuring a factory method in module.config.php, but when I try that, Zend will no longer pass a tablegateway object into one of my model objects created from a service factory method in the module's Module.php file. This seems to be because it no longer calls the factory method, and opts to run instantiate without any parameters.
I'm not certain I understand why the view factory methods would affect a different set of factory methods with different names.
Can anyone tell me what is wrong with what I'm doing? I can provide more details, but at this point I'm unclear on what details are actually important without supplying the entire codebase.
Thanks.
Crisp does provide a valid answer to your question, but I would suggest to take it one step further. The injection of the service locator makes your view helper tightly coupled to the framework and service locator pattern and vulnerable because every piece of code inside your application can modify every service in the service locator.
There are reasons to inject your dependency directly, so you only depend on your dependencies and you're not implementing this anti-pattern anymore. Let's assume your view helper depends on MyModule\Model\MyTable, then the constructor of your view helper would just look like this:
namespace MyModule;
use MyModule\Model\MyTable;
use Zend\View\Helper\AbstractHelper;
class MyViewHelper extends AbstractHelper
{
protected $table;
public function __construct(MyTable $table)
{
$this->table = $table;
}
}
As you pointed out, you just inject your MyTable now:
namespace MyModule;
class Module
{
public function getViewHelperConfig()
{
return array(
'factories' => array(
'MyViewHelper' => function($sm) {
$sm = $sm->getServiceLocator(); // $sm was the view helper's locator
$table = $sm->get('MyModule_MyTable');
$helper = new MyModule\View\Helper\MyHelper($table);
return $helper;
}
)
);
}
}
Note that inside a view helper factory your service manager is the view helper's service manager and not the "main" one where the table is registered (see also a blog post of I wrote earlier). The $sm->getServiceLocator() solves this for you.
I'm not certain I understand why the view factory methods would affect a different set of factory methods with different names.
It's not, so there is probably a bug in your code. If above does not work, please provide some more details on your service manager configuration so I can update my answer.
One of the great advantages of above approach is you make unit testing really easy for your view helper. You can mock the table gateway and focus on the complete behaviour of your view helper.
use MyModule\View\Helper\MyHelper;
public function testHelperusesTable
{
$mock = $this->getMock('MyModule\Model\MyTable');
$helper = new MyHelper($mock);
// Test your $helper now
}
You can inject the service locator into your view helper from the view helper config in Module.php
// Application/Module.php
public function getViewHelperConfig()
{
return array(
'factories' => array(
'myViewHelper' => function ($serviceManager) {
// Get the service locator
$serviceLocator = $serviceManager->getServiceLocator();
// pass it to your helper
return new \Application\View\Helper\MyViewHelper($serviceLocator);
}
)
);
}
In your view helper
<?php
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper,
Zend\ServiceManager\ServiceLocatorInterface as ServiceLocator;
class MyViewHelper extends AbstractHelper
{
protected $serviceLocator;
public function __construct(ServiceLocator $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
}
While working in Zend Framework,we often need custom helper,that make our work easy, In zf1 accessing database model from helper was easy,but i got stuck that how to access database model for any table in Custom View Helper, but as i was needing it i get around through the problem in unprofessional way by creatina new db adapter object in the view, which was never good way, but recently i came to know through very interesting way to access the database adapter in the view helper and there i have to execute any query on any table, it may be not so Zend F2 way, but very simple and short way to solve the issue.
Here is my Model Example...
<?php
namespace Application\Model;
use Zend\Db\TableGateway\TableGateway;
class SlideImageSubTable {
protected $tableGateway;
public $adapter;
public function __construct(TableGateway $tableGateway) {
$this->tableGateway = $tableGateway;
$this->adapter = $this->tableGateway->getAdapter();
}
public function fetchAll() {
$resultSet = $this->tableGateway->select();
return $resultSet;
}
public function getSlideImageSub($id) {
$id = (int) $id;
$rowset = $this->tableGateway->select(array('id' => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
return $row;
}
public function getImageMenu($id) {
$id = (int) $id;
$rowset = $this->tableGateway->select(array('slide_image_id' => $id));
$rows = array_values(iterator_to_array($rowset));
if (!$rows) {
throw new \Exception("Could not find row $id");
}
return $rows;
}
public function saveSlideImageSub(SlideImageSub $slideImageSub) {
$data = array(
'slide_image_id' => $slideImageSub->slide_image_id,
'title' => $slideImageSub->title,
'description' => $slideImageSub->description
);
$id = (int) $slideImageSub->id;
if ($id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getSlideImageSub($id)) {
$this->tableGateway->update($data, array('id' => $id));
} else {
throw new \Exception('Form id does not exist');
}
}
}
public function deleteSlideImageSub($id) {
$this->tableGateway->delete(array('id' => $id));
}
}
Just look at the 'public $adapter' public variable. And in the constructor i am going to initialize it by calling $this->tableGateway->getAdapter(); method, getAdapter() is available thorugh gateway object.
Then in my controller action view, i have to assign it to any variable and pass that variable to view page. like this..
public function equitiesAction() {
$image_id = $this->params('id');
$result = $this->getTable('SlideImageSub')->getImageMenu($image_id);
$adapter = $this->table->adapter;
$view = new ViewModel(array(
'result' => $result,
'adapter' => $adapter,
));
return $view;
}
And in the view i pass the 'adapter' object to custom view like this..
<?php echo $this->GetMenuProducts( $this->adapter); ?>
Now in custom view i can use this database adapter object and create select query on any table.
Hope this will help someone, i look around for using database access in custom view helper but the configurations methods provided was not working for me.
Thanks
$this->getView()->getHelperPluginManager()->getServiceLocator();

Resources