Zend Framework + Tricky controller name and module name conflict - zend-framework

We have a Zend application that has these following modules:
Users
Shop
etc...
Front - A content management module
While the Front module has the following controllers:
UsersController
ShopController
AuthController
etc...
In the middle of our development cycle we decided to set the default module for the Zend application to the Front module, but inadvertently broke our links, as http://domain.com/front/users/list are now generated as http://domain.com/users/list, which is now pointing to the wrong action.
We are generating links using the URL view helper, (i.e. $this->url(array('module' => 'front', 'controller' => 'users', 'action' => 'list'));), but the 'front' URI segment is omitted since switching the default module to the Front module.
I totally understand why this is so, but we are avoiding renaming all controllers under the Front module to avoid conflicts.
My question is, is there is a way to instruct the URL view helper to always include the 'front' module URI segment even if it is already set as the default one?

My question is, is there is a way to instruct the URL view helper to
always include the 'front' module URI segment even if it is already
set as the default one?
You can create your own url view helper with same name and override default url view helper add it to Zend_View object in your bootstrap.
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
if (null === $viewRenderer->view)
{
$viewRenderer->initView();
}
$view = $viewRenderer->view;
$view->addHelperPath('/path/to/some/helpers', 'My_View_Helper');
Now create class My_View_Helper_Url let it extend Zend_View_Helper_Url override url method.
Here is reference form ZF doc about this procedure
In fact, you can "stack" paths using the addHelperPath() method. As
you add paths to the stack, Zend_View will look at the
most-recently-added path for the requested helper class. This allows
you to add to (or even override) the initial distribution of helpers
with your own custom helpers.
Having said that I think http://domain.com/users/list, should have worked correctly in first place since you have specified default module to front.

Related

TYPO3 set template view for controller action

I want to use the view template of the list action for my listByYear action. I tried setTemplatePathAndFilename without success. It still cannot find the template.
Sorry, the requested view was not found.
The technical reason is: No template was found. View could not be
resolved for action "listByYear" in class
"XXX\YYY\Controller\EventController".
/**
* action listByYear
* #param \XXX\YYY\Domain\Model\Event $event
*
* #return void
*/
public function listByYearAction(\XXX\YYY\Domain\Model\Event $event)
{
$date = $event->getStart();
$events = $this->eventRepository->findByYear($date->format('Y'));
$this->view->setTemplatePathAndFilename(
'typo3conf/ext/' .
$this->request->getControllerExtensionKey() .
'/Resources/Private/Templates/Event/List.html'
);
debug('typo3conf/ext/' .
$this->request->getControllerExtensionKey() .
'/Resources/Private/Templates/Event/List.html');
$this->view->assign('events', $events);
}
How do I make it use the template for the list?
The very short answer is, you can't. The view will already have been initialised and asked to resolve a template well before your action fires, indeed well before any point where you can affect the template filename that it would look for.
The template file that by convention would be resolved must always exist. This is what allows your controller action to render. You can then, but I would not recommend that you do, override the template file by setting the template name (the action).
Overall recommendation: use the default template naming logic. If you need to re-use templates, consider refactoring the template parts you need to reuse, placing them in partial templates.
// Do not forget the use in the header ...,
// or write fully qualified class path..
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
// then add something like this in your action before the assign...
// or maybe create a Standalone view: search the web for "Extbase Standaloneview"
// have a look at: /typo3/sysext/about/Classes/Controller/AboutController.php
$this->view = GeneralUtility::makeInstance(StandaloneView::class);
$this->view->setTemplate('ActionName');
$this->view->setTemplateRootPaths(['EXT:your_ext/Resources/Private/Templates']);
$this->view->setPartialRootPaths(['EXT:your_ext/Resources/Private/Partials']);
$this->view->setLayoutRootPaths(['EXT:your_ext/Resources/Private/Layouts']);
$this->view->assignMultiple([
'whatever' => $whatever,
'youLike' => $youLike,
]);

Zend 3 - Change template basepath

I am just starting to migrate some of my applications from Zend#1 to Zend#3. All is working fine, but regarding the views I've some trouble understanding the underlying concept.
As the tutorials suggest my project layout is like this:
module
Application
view
application
index
main.phtml
foo.phtml
baz
index.phtml
I'm wondering why you need to dublicate the "application" folder inside the view directory - you are already in the directory hirachy of the module. Is there a way to change the search path for the default template resolver so that the module name is omited? Just relying on the viewManager's "template_path_stack" is not working. Do I really need to write a custom resolver here?
Thanks a lot!
PS. Nope, I do not want to use custom template maps here ;-) I want to understand and use the default revolver without template maps, if possible.
"Application" is the name of a module in your overall 'application.' At the start, "Application" is the only module, but it is common to add other modules: you might have a module for "Clients" and another module for "Vendors." The hierarchy of the view folders follows the same hierarchy as ModuleName:ControllerName:ActionName, and ZF needs to use the module name in the view folders hierarchy in case you happen to have identical controller and action name pairs in two or more modules. It is likely that a "Clients" module and a "Vendors" module will both have an "index" action. It is less likely that the two would have identical controller names but it's not completely out of the question. If you had a controller named "Contacts" in both a "Clients" module and a "Vendors" module, "contacts/index" isn't enough information to tell ZF which view to use. It needs the module name in the folder hierarchy to distinguish between "clients/contacts/index" and "vendors/contacts/index".
UPDATE
Something to wrap your head around is that ZF3 takes things like router definitions, view folders and who knows what else from all of your different modules and aggregates them into a single structure. In other words,
module
Application
view
application
add
add.phtml
delete
delete.phtml
edit
edit.phtml
index
index.phtml
module
Clients
view
clients
add-client
add.phtml
delete-client
delete.phtml
edit-client
edit.phtml
client-index
index.phtml
module
Vendors
view
vendors
add-vendor
add.phtml
delete-vendor
delete.phtml
edit-vendor
edit.phtml
vendor-index
index.phtml
gets recognized somewhat like this:
module
....
view
application
add
add.phtml
delete
delete.phtml
edit
edit.phtml
index
index.phtml
clients
add-client
add.phtml
delete-client
delete.phtml
edit-client
edit.phtml
client-index
index.phtml
vendors
add-vendor
add.phtml
delete-vendor
delete.phtml
edit-vendor
edit.phtml
vendor-index
index.phtml
and you could probably put all of your view files into a single module if you wanted to.
Perhaps this helps explain why a folder with the module name is included beneath the "view" folder. The folder with the module name that is above the "view" folder has a storage function. The folders with module names below the "view" folder serve as a means of referencing which modules the view files are associated with in the aggregated definition.
Found one possible solution!
In your module's config add this one to the view_manager::
'controller_map' => [
'Dashboard\Controller\DashboardController' => 'Dashboard'
],
Instead of looking for a template called "Dashboard/view/dashboard/[controller]/[action].phtml" the framework will now look for "Dashboard/view/[controller]/[action].phtml". Basically you are telling Zend to use some kind of shorthand here and strip the array's value from the template resolution (have a look at InjectTemplateListener::mapController())
Anyways... a better solution and explanation is welcome!

Call Modules Controller from Application Bootstrap

I've asked a question like this previously but I believe this is different (that one was just a general question).
I implemented Zend_Navigation.
For menu I used DB Table to store menu items and did recursion on Array-s to get the tree of menu items.
All of this action takes place in my module called Menu. Inside I have:
Menu --
Controllers --
IndexController.php
Models--
DbTable--
Menu.php
Bootstrap.php
inside index controller I have a function menuGenerator($menu_id)
So following tutorials on Zend_Navigation, the menu is initialized in the application bootstrap.
my function inside application's bootstrap looks like this:
public function _initMenus() {
$menuArray = new Menu_IndexController();
$outArray = $menuArray->menuGenerator(1);
$mainmenu = new Zend_Navigation($outArray);
$this->view->navigation($mainmenu);
}
and it gives me an error:
Fatal error: Class 'Menu_IndexController' not found in D:\Server\xampp\htdocs\project\application\Bootstrap.php on line 8
So, Any ideas how should I make it to work correctly?
P.S. is it possible to start 2 new menus at a time? for ex: I need 1. main menu 2. footer menu (any link to an article would be nice)
By default, Zend Framework's autoloader doesn't autoload controllers in the same way it loads other components (models, view helpers, forms, etc), so PHP throws the error saying it can't find the class. The quickest way to get around this is to explicitly include the controller in Bootstrap.php. The following should work:
public function _initMenus() {
require_once('./Controllers/IndexController.php');
$menuArray = new Menu_IndexController();
$outArray = $menuArray->menuGenerator(1);
$mainmenu = new Zend_Navigation($outArray);
$this->view->navigation($mainmenu);
}
It's pretty unusual to call a controller method during Bootstrap since there are many bootstrapping tasks upon which controller actions depend. In your case, the controller method menuGenerator() is not actually an action, so presumably it will not be a problem.
Nonetheless, it's still unusual enough that I would move the menuGenerator() method out into its own class. Then invoke that operation both at Bootstrap and in your controller.

Zend Framework - How to set up auto loader for custom decorators

I have placed my custom decorators in windows
application/modules/Tab/forms/decorators
My application works well on windows, but in linux zend cannot found my code in Zend/Forms/Decorators. (upper case F and D)
How can I set up auto loader for zend auto load my decorator?
(My custom decorator's name is Tab_Forms_Decorators_Hr)
Since default path segment is Form/Decorator and class prefix segment is Form_Decorator (see Zend/Form.php source, getPluginLoader() method ), the plural form is weird and the singular one should by used. The Forms directory may be used for storing your own Forms models, so maybe some mis-configuration? You didn't post the whole error...
But this don't solve your problem. Try using addPrefixPath() to add your own path and prefix, like this (and adjust directories and classes names according to this):
$form->addPrefixPath('Tab_Form_Decorator', 'Tab/Form/Decorator', 'decorator');
Note the path must begin in some place previously defined (in eg. _initAutoload() Bootstrap.php).

Zend Framework - Extending Controllers

I'd like to have my controller - i.e. Page_IndexController - extend a base controller.
For example;
class Page_IndexController extends Content_IndexController {
}
However, it seems the autoloader doesn't pick up the fact it's a controller class at any point - I get the error Fatal error: Class 'Content_IndexController' not found
First question: How do I fix this?
I can temporarily fix this by require_once'ing the generic 'content' controller, but this is hardly ideal.
The next issue is that if my Page controller has it's own view script for an action, it works no problem.
But if I'm extending a controller, and I call for example 'listAction' on the Page controller, but this action is implemented in Content_IndexController, it still looks for the list view script in the page controllers "scripts" directory.
Second question: How do I configure my controller to use its parents view script if it doesn't have its own?
If your application can find Page_IndexController you probably have a Page module. If you are not using modules in your application you have to name your controllers PageController and ContentController, not Page_IndexController, ... So the solution is to register "Content_" namespace with the autoloader.
As for the second question. You can extend the provided ViewRenderer controller action helper and override methods for finding the view script so they can look in other places if needed. You just have to pass your viewrenderer to the front controller. For passing your own ViewRenderer to the controller take a look at Advanced Usage Examples.
The auto loader can't find your controller because you haven't told it where to search. The Content_IndexController isn't in your "library" folder (I assume its inside of the Content module)
What I would suggest is creating a My_Controller_IndexBase class in your library folder that both Content_IndexController and Page_IndexController inherit.
Did a little more research on the topic of the view script. You could change up the view's script paths during init() somewhere. I'm pretty sure this would probably need to be done in a ViewRenderer - but might also work inside the controller's init/action code.
$this->view->setScriptPath(
array(
realpath(APPLICATION_PATH+'/../path/to/other/module/views'),
) + $this->view->getScriptPath());
Script paths are processed Last In First Out according to the Zend_View_Abstract
For the second question:
If you don't want to write your own ViewRenderer, you can use $this->renderScript('parent/index.phtml') to render a specific view script. You could call that in your child controllers instead of letting the views be rendered for you automatically, or if your child controllers rely on the parent controller to do the rendering of the script you can just place that in your parent controllers.
I do that mode.
Frist I register a new name who a plugin in my index.php into the public folder:
/public/index.php
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('modelo_');
Secound I create a new folder to put my controller
/library/modelo/
Third I create my controller model and put it into the folder created and rename it.
class Modelo_ModeloController extends Zend_Controller_Action
{
protected $_db = null;
protected $_menu = null;
protected $_util = null;
protected $_path = null;
... actions to my model
public function inicial(){}
}
and I extend this class im my application
class Sispromo_PedidoController extends Modelo_ModeloController
{
public function init(){}
....
}