Implementing vanity urls (like http://facebook.com/JohnDoe) in Zend framework? - php

Is there anyway to create a vanity url "catch all" route whilst maintaining the default /module/controller/action/value routing structure?
Thanks!

It's better if you setup a custom route, for example in your bootstrap:
protected function _initRoutes() {
$this->bootstrap('frontController');
$front = $this->getResource('frontController');
$router = $front->getRouter();
$router->addRoute(
'neat_url',
new Zend_Controller_Router_Route(
'profile/:username',
array(
'controller' => 'profiles',
'action' => 'view_profile'
)
)
);
}
This way you can still have the default route and have a custom route that will redirect everything under /profile/jhon.doe then under your controller you get the parameter using $this->_getParam('username');

You could use the PreDispatch() hook on a front controller plugin. Like so:
In your bootstrap
<?php
...
$frontController = Zend_Controller_Front::getInstance();
// Set our Front Controller Plugin
$frontController->registerPlugin(new Mystuff_Frontplugin());
?>
Then inside Mystuff/Frontplugin.php
<?php
class Mystuff_Frontplugin extends Zend_Controller_Plugin_Abstract
{
....
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
....
$controllerFile = $this->_GetRequestedControllerFile();
if (!is_file($controllerFile)) {
// Controller does not exist
// re-route to another location
$request->setModuleName('customHandler');
$request->setControllerName('index');
$request->setActionName('index');
}
}
....
}
Also preDispatch() is a handy location to handle application wide authentication.

Related

ZF2 - pass variables to zend build-in helper

I would like to pass variable (that would be service menager) to a build-in helper of zend. Is it possible? To be more clearly:
There is a zend helper called Url, which constructs url's
In this helper I would like to get some data from database, so I need to pass there connection or model (doesn't matter really)
Depends on data get in point 2. I would like to construct my custom link
Well, the thing looks like this: I'm trying to make own custom routing. So in database I have controller, action and it's alias. For example:
Home\Controller\Home | index | myalias
Routing works fine, that means that if I type url:
example.com/myalias
Then Zend will open Home controller and index action. But on whole page I have url's made by Zend build-in Url helper, which looks like this:
$this->url('home', array('action' => 'index'));
So link looks:
example.com/home/index
I would like to change link to
example.com/myalias
without changing links generated by Url helper on whole page. So before helper return url, should check if that url have alias, and if so then should return that alias exept regular url.
In Module.php of the module where you have he helper class file, write the following -
//use statements
class Module {
//public function getAutoloaderConfig() { [...] }
//public function getConfig() { [...] }
public function getViewHelperConfig() {
return array(
'factories' => array(
'Url' => function ($sm) {
$locator = $sm->getServiceLocator();
$viewHelper = new View\Helper\Url;
//passing ServiceLocator to Url.php
$viewHelper->setServiceLocator($locator); //service locator is passed.
return $viewHelper;
},
),
);
}
}
Now in the Url.php, we need a function setServiceLocator() and getServiceLocator().
//use statements
class Url {
public $serviceLocator;
public function getServiceLocator() {
return $this->serviceLocator;
}
public function setServiceLocator($serviceLocator) {
if($this->serviceLocator == null)
$this->serviceLocator = $serviceLocator;
return;
}
}
I hope it helps.

How to change layout in my all action of zend framework2

In my project where i have two modules, one is the frontend and the other is the backend,Because there are two different layout on the screen so each,Now I can change lauout on a single Action,I want to emulate zf1, the controller there is a init () method, you can perform, so that all action to change lauout or have any way to work.
zf1 init() Method
class AjaxController extends Zend_Controller_Action
{
function init(){
$this->_helper->layout()->disableLayout();
}
}
zf2 __construct() Method
public function __construct()
{
$this->layout('layout/administrator');//it's not work
}
pulic funciton indexAction()
{
$this->layout('layout/administrator');//it's work
}
Read this post: Module-specific layouts in Zend Framework 2
ZF1 and ZF2 are very different in terms of their architecture. ZF2 is event driven; meaning that you will need to attach an event listener to the controller dispatch event or order for it to correctly set the layout.
class Module
{
public function getControllerConfig()
{
return array(
'factories' => array(
'Namespace\Controller\Ajax' => function($cpm) {
$controller = new Namespace\Controller\Ajax();
$eventManager = $controller->getEventManager();
$eventManager->attach('dispatch', function($event) {
// Set layout or perform actions prior to dispatch
$controller = $event->getTarget(); // Controller
$controller->layout('foo/bar');
});
return $controller;
},
),
);
}

How to call multiple actions of multiple modules from different controller and combine the output?

Suppose I have the following code.
class Module1_IndexController extends Zend_Controller_Action {
public function indexAction() {
$this->view->data1 = "This is module1's index action view";
}
}
class Module2_IndexController extends Zend_Controller_Action {
public function indexAction() {
$this->view->data2 = "This is default module2's index action view";
}
}
class Default_IndexController extends Zend_Controller_Action {
public function indexAction() {
// $module1_output = Call Module1_IndexController's indexAction view output
// $module2_output = Call Module2_IndexController's indexAction view output
$this->_helper->actionStack("index", "index", "module1");
$this->_helper->actionStack("index", "index", "module2");
}
}
Is it possible to achieve the output like the one in the last controller ? I am using zend framework 1.11 and are there any other solutions to achieve this functionality ?
In modules/module1/views/scripts/index/index.phtml, I have
$module1NavArray = array(
array( 'label' => 'Nav1', 'uri' => '/home/module1/nav1' )
);
$container = new Zend_Navigation($module1NavContainer);
print $this->navigation( $container );
And in modules/module2/views/scripts/index/index.phtml
$module2NavArray = array(
array( 'label' => 'Nav2', 'uri' => '/home/module2/nav2' )
);
$container = new Zend_Navigation($module2NavContainer);
print $this->navigation( $container );
The output of the default module's index action is Nav2 link is printed 2 times. However, if the print a string instead of navigation, then the output is as desired like "Nav2" and "Nav1" sequentially.
Action View Helper will help you to accomplish this
in modules/default/views/scripts/index/index.phtml
<?php echo $this->action('index', 'index', 'module1')); ?>
<?php echo $this->action('index', 'index', 'module2'); ?>
But action view helper have several performance and debugging issues please see these links before deciding to use it
How can I speed up calls to the action() view helper?
Action View Helper in Zend - Work around?
Using Action Helpers To Implement Re-Usable Widgets

Zend Framework; check if a controller exists or not

I searched on Google, and I got this code to check if a controller exists or not.
$front = Zend_Controller_Front::getInstance();
if ($front->getDispatcher()->isDispatchable($request)) {
// Controller exists
}
But I don't know where I should put this code. What is $request?
I'm in Boostrap.php. I have _initRoute, I need to check if a controller doesn't exist, if it doesn't then I will add a new route.
Updated after first answer. I have some routes in Boostrap.php
public function _initRoute() {
$front = Zend_Controller_Front::getInstance();
$router = $front->getRouter();
$router->addRoute(
'username',
new Zend_Controller_Router_Route(':username',
array('controller'=>'profile',
'action'=>'index')
)
);
$router->addRoute(
'username/sets',
new Zend_Controller_Router_Route(':username/sets',
array('controller'=>'profile',
'action'=>'sets')
)
);
}
This Routes, will make mydomain.com/{username} show content same as mydomain.com/profile/index/username/{username}
But the problem is, when I type mydomain.com/{anything or any controller} , it routes as I define on Boostrap. So, I think, I need to check the controller is existing or not, if it doesn't then do the routes.
Am I wrong? After first answer, I added the plugin, and put it under _initPlugin to register it. But look like it not work.
This is my boostrap file:
<?php
//Zend_View_Helper_PaginationControl::setDefaultViewPartial('paginator.phtml');
class Plugin_MyX extends Zend_Controller_Plugin_Abstract {
public function routeStartup(Zend_Controller_Request_Abstract $request) {
$front = Zend_Controller_Front::getInstance();
$dispatcher = $front->getDispatcher();
if (!$dispatcher->isDispatchable($request)) {
$front = Zend_Controller_Front::getInstance();
$router = $front->getRouter();
$router->addRoute(
'username',
new Zend_Controller_Router_Route(':username',
array('controller'=>'profile',
'action'=>'index')
)
);
$router->addRoute(
'username/sets',
new Zend_Controller_Router_Route(':username/sets',
array('controller'=>'profile',
'action'=>'sets')
)
);
} else {
// exist
}
}
}
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initPlugin() {
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Plugin_MyX());
}
public function _initRoute() {
}
}
You should put that code in a Controller Plugin since the request object does not yet exist at bootstrap time.
The $request variable in question is an object of Zend_Controller_Request_Http. This object is initially created when the front controller goes to dispatch a request.
You could register a routeStartup plugin and place the code there. That would be the earliest point at which you can use the Request object. All controller plugin chains will pass the request object to your plugin except for dispatchLoopShutdown().
Here is sample plugin code:
class Application_Plugin_Example
{
public function routeStartup(Zend_Controller_Request_Abstract $request)
{
$front = Zend_Controller_Front::getInstance();
if ($front->getDispatcher()->isDispatchable($request)) {
// Controller exists
}
}
}
If you are trying to just handle 404 errors, this is what the ErrorHandler plugin can be used for this purpose.
You should have your routes from most specific to less specific. Also think about whether you can't have any other option in your URL - like /u/:username or /user/:username, which would solve the problem ;)
Also defining routes for each end every controller would solve it - create:
/controller-name/
/profile-controller-name/
... with
/:username
at the end of the list
That way any controller would be matched earlier and would work. And only controllers not listed would "fall through" to the :username route.

Zend Framework - Zend_Loader_PluginLoader

I'm just really starting with the Zend Framework, and currently I'm having a problem with the Zend_Loader_PluginLoader.
I managed to get a module specific plugin working easily enough using the following code:
class Api_Bootstrap extends Zend_Application_Module_Bootstrap
{
protected function _initPlugins()
{
$loader = new Zend_Loader_PluginLoader(array(
'Api_Plugin' => 'application/modules/api/plugins',
));
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Api_Plugin_ErrorControllerSelectorPlugin());
}
}
Edit: The class file is located at application/modules/api/plugins/ErrorControllerSelectorPlugin.php
I then tried to adapt this to get a plugin loaded for the whole application using:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initAppAutoload()
{
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'App',
'basePath' => dirname(__FILE__),
));
return $autoloader;
}
protected function _initPlugins()
{
$loader = new Zend_Loader_PluginLoader(array(
'My_Plugin' => 'application/plugins',
));
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new My_Plugin_ModuleConfigLoaderPlugin());
}
}
But I'm getting errors:
Fatal error: Class 'My_Plugin_ModuleConfigLoaderPlugin' not found in /var/www/localhost/application/Bootstrap.php on line 22
Edit: The class file is located at application/plugins/ModuleConfigLoaderPlugin.php
So - since the files are where I would expect them to be as far as the prefix/path pairs sent to Zend_Loader_PluginLoader() and the code in both cases are the same, what's the difference?
How do I get it to recognise my application-level plugins?
If you want the app-level plugin to reside within the namespace My_, you either need to put the My folder out in the library folder or declare the app-level namespace to be My_.
Assuming that you already have other stuff within your top-level app that uses the App_ namespace, then the easiest thing would be the former: move your My folder out into the library.
So, the plugin would reside in:
library/My/Plugins/ModuleConfigLoaderPlugin.php.
Then make sure that your configs/application.ini registers the My_ namespace:
autoloaderNamespaces[] = "My_"
Then the app-level Bootstrap could contain something like:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initAppAutoload()
{
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'App',
'basePath' => dirname(__FILE__),
));
return $autoloader;
}
protected function _initPlugins()
{
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new My_Plugin_ModuleConfigLoaderPlugin());
}
}
Alternatively, since your plugin does not sem to require any params, you could instantiate it via configs/application.ini using:
resources.frontcontroller.plugins[] = "My_Plugin_ModuleConfigLoaderPlugin"

Categories