I've been struggling with Zend_Navigation all weekend, and now I have another problem, which I believe has been the cause of a lot of my issues.
I am trying to add Zend_Navigation to a legacy 1.7.6 Zend Framework application, i've updated the Zend Library to 1.9.0 and updated the bootstrap to allow this library update.
The problem is that I don't know how, and the examples show the new bootstrap method of how to add the Navigation object to the view, I've tried this:
//initialise the application layouts with the MVC helpers
$layout = Zend_Layout::startMvc(array('layoutPath' => '../application/layouts'));
$view = $layout->getView();
$configNav = new Zend_Config_Xml('../application/config/navigation.xml', 'navigation');
$navigation = new Zend_Navigation($configNav);
$view->navigation($navigation);
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
$viewRenderer->setView($view);
This seems to run through fine, but when I go to use the breadcrumb view helper in my layout, it errors with: Strict Standards: Creating default object from empty value in C:\www\moobia\development\website\application\modules\employers\controllers\IndexController.php on line 27
This is caused by the following code in the init() function of my controller.
$uri = $this->_request->getPathInfo();
$activeNav = $this->view->navigation()->findByUri($uri); <- this is null when called
$activeNav->active = true;
I believe it's because the Zend_Navigation object is not in the view.
I would look at migrating the bootstrap to the current method, but at present I am running out of time for a release.
Thanks,
Grant
First you need to work out whether your suspicion that Zend_Navigation is not in the view is correct. Easiest way to do this would be to add:
var_dump($this->view->navigation());exit;
to your controller init(). This should return the Zend_Navigation object if it's there.
If it's not there, an alternative way of supplying the Zend_Navigation object is to use the registry, which might be easier. To do this you'd remove the view stuff from your bootstrap and just do this:
$configNav = new Zend_Config_Xml('../application/config/navigation.xml', 'navigation');
$navigation = new Zend_Navigation($configNav);
Zend_Registry::set('Zend_Navigation', $navigation);
your controller init() stuff would remain the same as the view object will look in the registry if it doesn't already have a Zend Navigation object.
However, I'm not sure that your controller init() code will quite work the way that you want. I don't think findByUri() will work on Mvc pages (but I could be wrong), and from your previous question it looked like most of the pages in your XML file are Mvc ones. The Mvc class has an 'href' property which appears to be the equivalent. If your XML file contains both page types, you might need to check both, so I'd suggest something like this:
$uri = $this->_request->getPathInfo();
if (($activeNav = $this->view->navigation()->findByHref($uri)) !== null) {
$activeNav->active = true;
} else if (($activeNav = $this->view->navigation()->findByUri($uri)) !== null) {
$activeNav->active = true;
}
Related
I'm maintaining a PHP Zend application. I'm attempting to add functionality to it.
I'm trying to call a Controller through a phtml file. I'm thinking I'm approaching this the wrong way, but I'm not sure what the correct way is.
I've modified three files and added another. I've added code to FileController.php.
public function getModifiedBy($filename) {
$groupFiles =$this->getServiceLocator()->get('qatools\Model \GroupFilesTable');
$modified = $groupFiles->fetch($filename);
return $modified;
}
I've also added code to job-wizard.phtml.
<?php
use qatools\Controller\FileController;
$fileControl = new FileController;
$fileControl->init();
$modified =$fileControl->getModifiedBy("addresscleaningservice.xlsx");
?>
The new file is 'GroupFileTable.php' which extend AbstractModelTable and queries a MySQL database. I added the following lines to module.config.php.
'qatools\Model\GroupFilesTable' => function($sm) {
return defModelTable($sm, 'GroupFilesTable');
},
'GroupFilesTableGateway' => function($sm) {
return defModelTableGateway($sm, 'group_files', 'GroupFiles');
},
The code is failing on $fileControl->init() in job-wizard.phtml. I tried taking out that line, but then the code failing on the getServiceLocator call.
#0 /mnt/c/git-repos/qatools/vendor/zendframework/zendframework/library /Zend/ServiceManager/AbstractPluginManager.php(103): Zend\ServiceManager\ServiceManager->get('init', true)
#1 /mnt/c/git-repos/qatools/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/PluginManager.php(82): Zend\ServiceManager\AbstractPluginManager->get('init', NULL, true)
You didn't specify what version of Zend Framework are you using, anyway whatever version is, you should not create an istance of a controller within a view script.
In ZF, if you need to call a view functionality, a better approach is to create a view helper, the logic between ZF1 and ZF2/3 is the same, only the implementation changes, I'll leave to you google searches to see examples, your final view script code should be something like the follow
<?php echo $this->getModifiedBy("addresscleaningservice.xlsx"); ?>
The viewHelper will contain the logic. If at some point you'll need to reuse this logic within a controller or in whatever other place, my advice is to create a component with the logic, then create a service that returns the instance of this component and eventually create an action helper ( $this->myComponent in any controller ) and a view helper ( $this->myComponent in any view ) and inject the same component instance.
I'm looking for a way to programatically get a list of controllers in a Kohana application.
Something like:
public function build_site_map(){
$controllers = Kohana::get_controllers();
echo '<ul>';
foreach($controllers as $controller){
echo '<li>'.$controller.'</li>';
}
echo '</ul>';
}
I realize I could read the /application/classes/controllers/ directory, but I'm hoping there's an easier way.
Thanks,
Getting a list of your controller files could be done with Kohana::list_files('classes/controller'). But as Michal already said, there isn't a 1:1 realtionships between controllers/actions and routes.
I'm afraid there is no Kohana::get_controllers() method that you can easily call to get a a sitemap of sorts. This is because controllers are called dynamically, i.e. based on the request's URL and Routes configuration Kohana's checking whether a controller (and action) exist and then call them. Kohana does not keep record of all available controllers and actions that can be accessible.
Neither traversing the /application/classes/controllers directory and getting the list of all files would give you the desired result, because there are not only actions to be read (which can be fairly easily done with Reflection class), but there are also Routes which you have to take into account.
As you can see this is potentially very complex issue and one that cannot be simply answered with a snippet of code that can be pasted here.
If you decide to write a script that would actually create such a map, but you stumble into a problem on the way, we would be able to more helpful then otherwise this question is too open. Also, if you were to write it, I suggest you create it as a module that you would be able to include in any other projects and share it.
Here my solution to get all the controllers and their actions. I use it to add permissions into our system https://github.com/open-classifieds/openclassifieds2/
/**
* get all the controllers and the actions that can be used
* #return array
*/
public static function list_controllers()
{
$list_controllers = array();
$controllers = Kohana::list_files('classes/controller');
foreach ($controllers as $controller)
{
$controller = basename($controller,'.php');
$list_controllers[] = $controller;
$class = new ReflectionClass('Controller_Panel_'.$controller);
$methods = $class->getMethods();
foreach ($methods as $obj => $val)
{
if (strpos( $val->name , 'action_') !== FALSE )
{
$list_controllers[$controller][] = str_replace('action_', '', $val->name);
}
}
}
return $list_controllers;
}
I'm using the Skeleton Zend Framework 2 to build my application.
I'd like to modify the current navigation bar in layout.phtml to show 2 links as standard, then some more links based on user permissions.
How would I go about getting the active module in use (/user for ZfcUser) to display as li class="active", and, how would I go about implementing the navigation items based on the modules loaded?
In controller, you could get active modules like this:
$modules = $this->getEvent()->getApplication()->getServiceManager()->get('modulemanager')->getLoadedModules();
$moduleNames = array_keys($modules);
Then you could check module loaded by module name:
$moduleLoaded = in_array('ZfcUser', $moduleNames); //true or false
Here's the problem... What's your definition of the "active module"? In ZF2, a module is loosely defined by the top-level namespace, but even that's not an absolute, as modules can provide code under several namespaces if they wish.
I wrote a blog post about configuring module-specific layouts where I explain this issue in a bit more detail, as well as demonstrate one possible "solution" to performing actions based on the active module: http://blog.evan.pro/module-specific-layouts-in-zend-framework-2
In that example, I'm attaching to the 'dispatch' event with the event identifier which is the module name (namespace), which is only triggered for the top-level namespace of the controller being dispatched (I specifically added this functionality to ZF2, as it was becoming a common question in the beta period). If you're curious how or why this works, see https://github.com/zendframework/zf2/blob/master/library/Zend/Mvc/Controller/AbstractController.php#L153-159 (specifically line 158 as of writing this).
Alternatively, you could attach to the dispatch event, and get the top-level namespace of the controller being dispatched. Again, there's no guarantee that this is actually the "module name" you're looking for, and it's best to just think about controllers and actions when it comes to dispatching requests rather than "which module is this?"
class Module
{
public function onBootstrap($e) {
$events = $e->getApplication()->getEventManager()->getSharedManager();
$events->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch', function($e) {
$controllerClass = get_class($e->getTarget()); // $e->getTarget() is the controller
$controllerTopNamespace = substr($controllerClass, 0, strpos($controllerClass, '\\'))
// Do whatever here, maybe something like:
// $nav = $e->getTarget()->getServiceLocator()->get('navigation');
// $nav->...
});
}
}
In controller, you can check active module in this way
$bIsModule = (bool) $this->getEvent()
->getApplication()
->getServiceManager()
->get('modulemanager')
->getModule('ZfcUser'); // returns true or false
Hi I have a form element error and it keeps breaking it when I wrap it in the translation function. I am using gettext for the translation.
I understand that if I have it set in the registry and Zend_Form it should pick it up automatically but how does poedit see it if at all?
My Bootstrap (Relevant Part):
// Set the instance of Zend_Translate in the registry
$registry->set('Zend_Translate', $translate);
// Set an instance of Zend Translate object for validators
Zend_Form::setDefaultTranslator($translate);
My Error Form:
public function formErrors(Zend_Form $form)
{
$registry = Zend_Registry::getInstance();
$translate = $registry->get('Zend_Translate');
$form->setTranslator($translate);
if ($form->getMessages()) {
$error = '<p class="errorBox">Error text here</p>';
$error->setTranslator($translate);
$error->getView()->translate($error);
return $error;
}
return '';
}
Note:
$this->translate('string to translate'); or $this->getView()->translate('string');
works everywhere else but not here
The usage within a form which I used on the last project was
$this->getTranslator()->translate('text to translate')
This was only used minorly as we had auto-detect resource paths so we did not need to call it. The way you want to use it does seem slightly different. I would recommend adding the extra call after getView() though as it could be the solution.
I've volunteered to create some db app, and I told those guys that it will be very easy, since I wanted to use CakePHP. Sadly after some time they told me they want it inside their already existing web, which is ancient highly customized PHPNuke.
So what I want is to generate just content of one <div> inside an already existing page with CakePHP. I looked up on the internet, but I didn't find what I was looking for. I'm rather a user of the framework, not developer, so I don't know much about the backend and how MVC frameworks are working inside (and this is my first try with CakePHP, since I'm Rails guy).
What I did so far is disabling mod_rewrite for Cake. Inside PHPNuke module I included Cake's index.php and rendering views with an empty layout. This somehow works, but the thing is how to form URLs. I got it working by now with
http://localhost/modules.php/posts?op=modload&name=xxxxx&file=index&do=xxxxx
but with this all links to CSS and images on PHPNuke site are broken.
Is there any way to use something like
http://localhost/modules.php?op=modload&name=xxxxx&file=index&do=xxxxx&CakePHP=/posts/bla/bla
or any other way that could do the job? I really don't want to change anything in existing PHPNuke app.
Thank you very much
Well, if you don't understand how CakePHP works you'll have trouble doing what you want, since it would mean putting hacks into the CakePHP core files to bypass the default routing. This basically means that you would be re-working the way CakePHP works, so you can forget about ever updating to a newer CakePHP version, and maintenance would be hell.
If you want to modify the system, but keep PHP-Nuke, I'd advise against jamming CakePHP in there, since that would open up too many problems to be able to predict beforehand.
I think your options are as follows:
Learn how PHP-Nuke works so you can modify it
Use regular php for the pages
Either of those are easier by orders of magnitude compared to what you wanted to do.
So to sum up solution I found, if someone will be looking for something similar. Problem solved by using two custom route classes ( http://manual.cakephp.neoboots.com/2.0/en/development/routing.html#custom-route-classes )
class CustomParserRoute extends CakeRoute {
function parse($url) {
if (parent::parse($url) != false) //if default parser has the match continue
{
// call to Router class to do the routing for new url string again,
// if &cakePHP= is in query string, use this, or use default
if ($_GET['cakePHP']) {
$params = Router::parse($_GET['cakePHP']);
} else {
$params = Router::parse("/my_controller");
}
return $params;
}
return false;
}
}
class CustomMatcherRoute extends CakeRoute {
// cusotm mathc function, that generates url string.
// If this route matches the url array, url string is generated
// with usual way and in the end added to url query used by PHPNuke
function match($url) {
$result_url = parent::match($url);
if($result_url!= false) {
$newurl = function_to_generate_custom_query()."&cakePHP=".$result_url;
return $newurl;
} else {
return $result_url;
}
}
}
And then simple configuration in routes php
App::import('Lib', 'CustomParserRoute');
App::import('Lib', 'CustomMatcherRoute');
// entry point to custom routing, if route starts with modules.php it matches
// the url and CustomParserRoute::parse class is called
// and route from query string is processed
Router::connect('/modules.php', array('controller' => 'my_controller'), array('routeClass' => 'CustomParserRoute'));
// actual routes used by cakephp app, usual routes that need to use
// CustomMatcherRoute classe, so when new url is generated, it is modified
// to be handled later by route defined above.
Router::connect('/my_controller/:action/*', array('controller' => 'my_controller'), array('routeClass' => 'CustomMatcherRoute'));