I've been working with zf2 for a while but i could not get a solution for the following problem.
eg. we have a module called "Product" which handle the most of the product stuff.
Another module - let's call it "Review" - should extend the Product module and extend the product view to offer a product review to the user.
Until this point there seems to be no problem. We can easily overwrite the product view in the Review module.
Now the tricky part where i got stuck.
There's a third module - let's call it "Social". This module should provide social functioniality to the whole application.
This module should also modify the product view and add a link in the product page called "EMail to a friend".
Now my problem...
The product view is modified by the review module. If i overwrite the view in the Social module, the changes from the Review module are lost.
// Edit
Info: The Social and Review module should be able to modify the views WITHOUT modifiing the Product module.
Any tips, hints or ideas are welcome.
It depends what code you are trying to avoid duplicating.
If it's just HTML content you can create a custom ViewHelper to render the required HTML. This could then be reused within each view that needs the "social" content.
I suspect however you are reffering to the a 'social' controller action and you wish to reuse the return result of that within other views. If so, one soultion would be to use the forward() controller plugin.
From the documentation:
Occasionally, you may want to dispatch additional controllers from within the matched controller – for instance, you might use this approach to build up “widgetized” content. The Forward plugin helps enable this [by] returning the results of the dispatched controller action
This is useful in a situation like yours as you require the "Social" module to be an additional element of the view; rather than a replacement.
For example
// SocialModule\Controller\SocialController.php
// Controller action to display social buttons (twitter/facebook etc)
public function socialAction()
{
// Some controller logic...
// Returns the social button view
return new ViewModel(array('foo' => $bar));
}
// ProductModule\Controller\ProductController.php
public function productViewAction()
{
$product = $this->productService->find($this->params('id'));
// Displays the product 'view' page
$view = new ViewModel(array(
'product' => $product,
));
// Re-disptach the social module action and return it's view model
$socialView = $this->forward()->dispatch('SocialModule\Controller\Social', array(
'action' => 'social',
));
// We now have the view model, attach it to our view
$view->addChild($socialView, 'social');
// Return the aggregated view
return $view;
}
All then is required is to render the content in the view
// product-view.phtml
echo $this->social;
Related
I have a large application using SF 3.4 and I need to find a better way to do custom rendering based on user on each page load.
Right now we have listeners for a side menu, a footer, side menu favorites, an application menu, and whether or not you are timed out. These listeners fire a theme event via the render function that adds information pertaining to the users access/favorites/if they are timed out. This cannot be done with roles in twig because we have things like menu favorites that change all the time.
We override the Controller render method to dispatch that theme event and array_merge what we get back with the render function's parameter array like this:
protected function render($view, array $parameters = array(), Response $response = null)
{
$themeEvent = new ExampleThemeEvent($this->getUser(), $this->getMyMenuApplication());
$result = $this->getDispatcher()->dispatch(ExampleThemeEvent::NAME, $themeEvent);
$parameters = array_merge($result->getModel(),$parameters);
return parent::render($view, $parameters, $response);
}
Because of this most of our Controllers extend ExampleThemeController instead of just Controller so every page we want to have these features does.
This works fine, but the render method has been marked final as of SF 3.4 and shouldn't be overridden. What would be the best way to approach this without overriding the render method? Is there another place I can dispatch our theme event?
We have tried onKernelController subscribers/listeners to no avail because we need to add the extra view parameters to the container which cannot be done at that point.
Thank you!
For such user-specific changes that go beyond passing globals to twig, I'd check out embedding controllers. You can render a part of your response with a different controller, responsible only for the favourites for example.
I am developing a system which has multiple user levels. However most of the time the views inside each modules should be the same.
For example a user view should be 99% identical to the admin view however an admin can have some small extras like delete buttons on user posts etc.
What is the best approach to not duplicate a ton of template view files within each module?
The best solution I can think of is using the _base module and putting the view files in there and inside them do an (if($user->isAdmin(): extra HTML bits) and have both the user module and admin module render the base module views?
If you are creating template file(layout.phtml) in your /module/Application/View/layout/ folder, then the layout will be applied for all the views.
If you want to disable layout for some specific view, you can use like this:
public function yourAction() {
$viewModel = new ViewModel(array(
'foo' => 'bar'
));
$viewModel->setTerminal(true);
return $viewModel;
}
Very first need to check if user is admin then in view you can call another view partially having extra code like delete button using $this->partial() helper.
I have a product controller and a header controller. When loading the product controller, I use Route::forward() to load my header controller.
return View::make('frontend.catalogue.product.view')
->with('head', Route::forward("GET", "frontend/page/head"));
I can then echo out the header on the page by doing:
<?php echo $head; ?>
Simple. However, I want to pass information to the header controller which will be defined in the product controller, such as the page title or meta description.
Ideally I would either be able to pass an array to the header, or have an instantiated class somewhere which both route requests have access to.
I've tried instantiating a library in my base controller constructor, however this gets re-instantiated on each Route::forward() request so any properties I set can't be read in the next forward.
The only way I can think of doing this is using session data, but that doesn't seem like it would be the best way to do it.
The header does need to be a controller and not just a view composer, as it deals with it's own models etc.
I know Laravel doesn't do HMVC, which would be the ideal solution, and I know there are bundles for HMVC, but I'm curious how you would approach this the Laravel way.
(Using Laravel 3.2 btw)
Managed to get this working how I want. For others interested:
Instead of using
return View::make('frontend.catalogue.product.view')
->with('head', Route::forward("GET", "frontend/page/head"));
I now do the following in the main request controller:
$document = array(
'meta_title' => 'My Title',
'meta_description' => 'My Description',
);
// Output the view
return View::make('frontend.catalogue.category.view')
->with('head', Controller::call('frontend.page.head#index', array($document)));
and in my frontend/page/head controller, I have the following:
public function action_index($document = array()) {
$meta_title = isset($document['meta_title']) ? $document['meta_title'] : 'Default Title';
return View::make('frontend.page.head')
->with('meta_title', $meta_title);
}
Props to the friendly guys on the Laravel IRC for helping with this
I'm developing a web application with Zend Framework 1.12, which is something new to me, and I'm not sure about the way to do something I want to.
EDIT: When I talk about Module, I mean Controller, sorry for that, I still mistake the terms ...
On my home page, the module Index, I made what I wanted to do with it, created several actions and all the stuff, but I'd like to add a search engine I'll make myself.
The problem is that I'd like to create the search engine as a separate module named Search, for example, but put the SearchForm in the home page. Hitting submit would send the datas from the form to the Search module.
I don't quite understand how to do that without having to go to /search to access my form and every associated actions.
Do I have to use a View Helper ?
Also, the searchForm in the front page would be some sort of QuicKSearch and accessing /search would show a more elaborated form for the research.
Can someone explain me how to access the searchForm from the Index module or redirect me to the part of the documentation talking about that ? My research are unsuccessful and Google doesn't help me either.
EDIT: When I talk about Module, I mean Controller, sorry for that, I still mistake the terms ...
First of all, build the searchform as viewHelper, then you can reuse it in several views.
The action attribute in form snippet set to searchModule/controller/action.
Additionaly make research about viewHelpers and Forms in Zend Documentation.
I actually prefer to do this as a an action helper and then just use a standard placeholder view helper to present the search form.
let me demonstrate:
the actual action helper just initiates a form and prepares it for display. I'll leave the form structure to you.
//the action helper
//Just fill in the args for the form to be displayed
class NameSpace_Controller_Action_Helper_Search extends Zend_Controller_Action_Helper_Abstract
{
public function direct($action, $label = null, $placeHolder = null)
{
$form = new Application_Form_Search();
//set the action
$form->setAction($action);
//set the submit button text
$form->search->setLabel($label);
//set the hint text displayed in the form window
$form->query->setAttribs(array('placeholder' => $placeHolder,
'size' => 27,
));
return $form;
}
}
I put the helper in the predispatch method of the controller so that each action in the controller can use the search form with having to build it in every page.
//to use the helper in your controller
class IndexController extends Zend_Controller_Action
{
public function preDispatch()
{
//setup action helper and assign it to a placeholder
$this->_helper->layout()->search = $this->_helper->search(
'/index/display', 'Search Collection!', 'Title');
}
//in your view script
<?php echo $this->layout()->search ?>
I like to put the placeholder in my master layout.phtml so that any time I populate the placeholder it will display. Now all you have to do is style it however you want.
Remember: As with any html form the action parameter is just a url so any valid url can be assigned to the form action. In this example I used the /controller/action parameters, but there are many other ways to pass a url to the form. The url helper comes to mind as good way to do it.
url($urlOptions, $name, $reset, $encode): Creates a URL string based
on a named route. $urlOptions should be an associative array of
key/value pairs used by the particular route.
I have pages in my application that make up the navigation tree. I would like to dynamically insert pages into my navigation using the values of the request. I already have the logic to find the page and then call the addPage() method on it. What I'm looking for is how to easily pass the Zend_Controller_Request values to Zend_Navigation_Page::factory() so I can add that page. Maybe even written as a plugin?
Solution
AngelP got the closest, so I'm giving him credit, but here's my solution:
$request = $this->getRequest();
if ($page = $this->view->siteNav->findBy('id', $page_id)) {
$page->addPage(Zend_Navigation_Page::factory($request->getParams())
->setParams($request->getParams())
->setLabel($this->view->title)
->setVisible(false));
}
This code is executed from a controller action. $this->view->siteNav is an instance of Zend_Navigation that I have in the view. getParams() from the Zend_Controller_Request instance is easily passed to Zend_Navigation_Page::factory() and then to the setParams() method of the Zend_Navigation_Page_Mvc instance.
I have limited resources at the moment so I cannot really check my suggestion, but if you're in you controller, why don't you..
$controller = $this->_request->getControllerName();
$action = $this->_request->getActionName();
$page = new Zend_Navigation_Page( array(
'label' => "Sonny's Page",
'controller' => $controller,
'action' => $action
));
Maybe you could use this as a plugin so that you overload your view? And then add to your Navigation Container?
Cheers,
Angel
Since we don't know the code you have written already, I'm only guessing…
You need to:
retrieve the actual Zend_Navigation container used in navigation() view helper
create new Zend_Navigation_Page instance from array of data retrieved from the request
add the page to the container
assign the new container to the navigation helper
This should be easy. The rest you need to know:
how to write controller plugin with preDispatch method, and put the above there,
how to access current view instance in this plugin (from the view renderer or from application resource/bootstrap)
Then in the plugin you operate on navigation view helper as usual in the view.
Hope this clarified some things.
Why not store the instance in Zend_Registry and then in a postDispatch from either a plugin, module bootstrap or action controller add the pages to the original nav?