How can I get the route-, module-, controller- and actionname in a plugin in ZF2?
The plugin is being used by a viewHelper.
I tried different methods but none seem to work.
These are two of the methods i tried
Method 1:
public function __construct(MvcEvent $e)
{
$this->routename = $e->getRouteMatch()->getMatchedRouteName();
}
Method 2:
public function __construct()
{
$e = new MvcEvent();
$this->routename = $e->getRouteMatch()->getMatchedRouteName();
}
You can see in source code how it works this helper.
Current route helper
$routematch = $routeMatch = $serviceLocator->getServiceLocator()->get('Application')->getMvcEvent()->getRouteMatch();
if($routeMatch) {
$controller = $routeMatch->getParam('controller');
$action = $routeMatch->getParam('action');
$module = $routeMatch->getParam('__NAMESPACE__');
$route = $routeMatch->getMatchedRouteName();
}
Related
I have the following code inside my Admin Module.php
public function onBootstrap(MvcEvent $e)
{
$application = $e->getApplication();
$em = $application->getEventManager();
if(!SystemUtils::isApiRequest()){
$em->attach(\Zend\Mvc\MvcEvent::EVENT_DISPATCH, array($this,'initUser'),10000);
}
}
public function initUser(MvcEvent $e)
{
$isLoggedIn=false;
// determined elsewhere
$action = Pluto::registry('application_action');
if($action!=='login' && !$isLoggedIn){
$viewHelperManager = $e->getApplication()->getServiceManager()->get('ViewHelperManager');
$renderer = $viewHelperManager->getRenderer();
$url = $renderer->plutourl('login');
$response = $e->getApplication()->getServiceManager()->get('Response');
$response->getHeaders()->clearHeaders()->addHeaderLine('Location', $url);
$response->setStatusCode(302)->sendHeaders();
exit();
}
}
The problem is this is only supposed to activate when inside a page within the admin module and you are not logged in but its operating for every request regardless of the module the code is inside
I want to limit this check to just when accessing the admin module.
Update
$sm = $e->getApplication()->getServiceManager();
$router = $sm->get('router');
$request = $sm->get('request');
$matchedRoute = $router->match($request);
returns the admin controller class name which i can use but i want it for all of the admin controller classes
You can use the shared event manager to attach to events in a given name space. In the example below we attach to an event only for the current modules namespace.
use Zend\Mvc\MvcEvent;
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$em = $app->getEventManager()->getSharedManager();
$sm = $app->getServiceManager();
$em->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH, function ($e) use ($sm) {
/**
* this will be triggered only by controlers in
* this module/namespace...
*/
$this->doSomething();
});
}
You could add this inside your admin module and only this event will only be listened to for classes inside this module / namespace.
You should use the MvcEvent object!
If all controllers are under a single namespace, for instance \Admin\Controller:
$namespace = 'Admin\\Controller\\';
$matchController = $e->getRouteMatch()->getParam('controller');
if( $namespace === substr($matchController, 0, strlen($namespace))
{
// check here if user is logged in.
}
else {
// not an admin controller, do nothing.
}
I am new to laravel and would like to fetch list of all controllers and list of action in that controller.
Just want to know if there is a way to get a list of all Controllers as well as all their Methods by code?
Thanks,
DJ
By how you are explaining the need for you to know the controller actions, it seems that the actions are already mapped to routes, which means you can use the routes to get the list of mapped controllers and actions. The following code will generate an array of the registered route controller actions:
$controllers = [];
foreach (Route::getRoutes()->getRoutes() as $route)
{
$action = $route->getAction();
if (array_key_exists('controller', $action))
{
// You can also use explode('#', $action['controller']); here
// to separate the class name from the method
$controllers[] = $action['controller'];
}
}
This will ignore routes that have Closures mapped, which you don't need. Mind you, you might need to filter out any matches from routes registered by third party packages.
That worked for me.
Extracting all controllers (App\Http\Controllers)
$controllers = require_once base_path('vendor/composer/autoload_classmap.php');
$controllers = array_keys($controllers);
$controllers = array_filter($controllers, function ($controller) {
return strpos($controller, 'App\Http\Controllers') !== false;
});
$controllers = array_map(function ($controller) {
return str_replace('App\Http\Controllers\\', '', $controller);
}, $controllers);
dd($controllers);
Extracting all methods in a specific controller using ReflectionClass
$namespace = "App\Http\Controllers";
$controller = "TestController";
$controller_class = new ReflectionClass($namespace.'\\'.$controller);
$controller_methods = $controller_class->getMethods(ReflectionMethod::IS_PUBLIC);
dd($controller_methods);
One catch though. You may need to run composer dump-autoload after creating a controller.
Old code:
public static function Controllers()
{
$controllers = require_once base_path('vendor/composer/autoload_classmap.php');
$controllers = array_keys($controllers);
$controllers = array_filter($controllers, function ($controller) {
return strpos($controller, 'App\Http\Controllers') !== false;
});
$controllers = array_map(function ($controller) {
return str_replace('App\Http\Controllers\\', '', $controller);
}, $controllers);
return $controllers;
}
Edited:
This Code is much better:
public static function Controllers()
{
$classes = array_filter(get_declared_classes(), function ($class) {
$isController = substr($class, -10) == 'Controller';
$isNotPlainController = $isController && substr($class, -11) != '\Controller';
return $isNotPlainController;
});
//Optional: to clear controller name from its namespace
$controllers=array_map(function ($controller){
return last(explode('\\',$controller));
},$classes);
//Optional: to reset keys of array to start from 0,1,2,...etc
$controllers = array_values($controllers);
return $controllers;
}
Try running this:
$classes = get_declared_classes();
foreach ($classes as $class) {
if (is_subclass_of($class, 'App\Http\Controllers\Controller')) {
echo $class . '<br />';
$methods = get_class_methods($class);
foreach ($methods as $method)
echo '--- ' . $method . '<br />';
}
}
It should output all your controllers (anything inherited from the Controller class) and their methods.
I'm not sure how to tell which ones of the methods are actual controller actions though. If you use controller routes that would be the ones starting with "get", "post" or "any", but if you're using direct links that's not the case.
Try this
$controllers = [];
$i=0;
foreach (Route::getRoutes()->getRoutes() as $route)
{
$action = $route->getAction();
if (array_key_exists('controller', $action))
{
$_action = explode('#',$action['controller']);
$_namespaces_chunks = explode('\\',$_action[0]);
$controllers[$i]['controller'] = end($_namespaces_chunks);
$controllers[$i]['action'] = end($_action);
$controllers[$i]['namespace'] = $action['controller'];
$controllers[$i]['route'] = $route;
}
$i++;
}
dump($controllers);
Grep can be used to print a list of all actions per controller on unix systems.
Open a console in the laravel project root and execute the following command:
grep "public function" app/Http/Controllers/* -r
I created an instance of serviceFactory in index.php which simply gets/stores (private) variables in itself. But, the __get and __set magic methods for some reason are not working. Doing a var_dump($serviceFactory->language) in the index returns false. When I create an instance of serviceFactory, I pass the $router instance to the $serviceFactory constructor. The router is working (otherwise it would not display controller or view), but for some reason, when I try to __get the language from the $serviceFactory instance, it returns false and I'm confused as to why.
index.php
//create an instance of serviceFactory
$serviceFactory = new serviceFactory($router);
var_dump($serviceFactory->language);
serviceFactory
class serviceFactory {
private $data = array();
public function __construct($router) { //store the router data
$data['language'] = $router->getKey('language');
$data['class'] = $router->getKey('className');
$data['method'] = $router->getKey('method');
$data['arg'] = $router->getKey('arg');
}
public function __set($key, $val) {
$this->data[$key] = $val;
}
public function __get($key) {
if(isset($this->data[$key])) {
return $this->data[$key];
}
else {
return false;
}
}
}
Simple, you've made a common mistake in working with instances:
$data['language'] = $router->getKey('language');
$data['class'] = $router->getKey('className');
$data['method'] = $router->getKey('method');
$data['arg'] = $router->getKey('arg');
should all be
$this->data['language'] = $router->getKey('language');
$this->data['class'] = $router->getKey('className');
$this->data['method'] = $router->getKey('method');
$this->data['arg'] = $router->getKey('arg');
I am using a Spotify library called MetaTune and was able to do this easily in CodeIgniter but with Yii there have been some teething issues however currently it has started saying:
Fatal error: Call to undefined method stdClass::searchTrack() in ....public_html/Yii/news/protected/controllers/NewsController.php on line 67
Howevever, the the function is there. The files in this library all have a .class.php suffix (e.g. MetaTune.class.php) and the libray files are all stored in:
yii/application/protected/vendors/Metatune
With Codeigniter I made an additional spotify.php outside of the folder and autoloaded that to my controller, but im not sure if this is necessary.
I have loaded it in my config.php with:
'import'=>array(
'application.models.*',
'application.components.*',
'application.vendors.metatune.*',
),
Here is the Controller code:
public function actionView($id)
{
$model=$this->loadModel($id);
$spotify = MetaTune::getSomething();
$hello = $model->title;
Yii::import('application.vendors.metatune.MetaTune');
$spotify->autoAddTracksToPlayButton = true; // Will add all searches for tracks into a list.
$spotify->playButtonHeight = 330; // For viewing the entire playlist
$spotify->playButtonTheme = "dark"; // Changing theme
$spotify->playButtonView = "coverart"; // Changing view
try
{
$tracks = $spotify->searchTrack($hello);
$tracks = $spotify->getPlayButtonAutoGenerated($hello);
}
catch (MetaTuneException $ex)
{
die("<pre>Error\n" . $ex . "</pre>");
}
$song = 'tracks';
$this->render('view',array(
'model'=>$this->loadModel($id),
));
}
Please also see the code below where it has a function called getInstance which doesnt work well with Yii for some reason and Im not sure if I can change this as I used this to import MetaTune into the CodeIgniter controller without any issues.
Just a part of the MetaTune.class.php code:
Yii::import('application.vendors.metatune.Artist');
Yii::import('application.vendors.metatune.Album');
Yii::import('application.vendors.metatune.Track');
Yii::import('application.vendors.metatune.CacheRequest');
Yii::import('application.vendors.metatune.MBSimpleXMLElement');
Yii::import('application.vendors.metatune.SpotifyItem');
Yii::import('application.vendors.metatune.MetaTuneException');
....
class MetaTune {
const CACHE_DIR = 'application/vendors/metatune/cache/'; // Cache directory (must be writable) relative to this file
const USE_CACHE = false; // Should caching be activated?
const CACHE_PREFIX = "METATUNE_CACHE_"; // prefix for cache-files.
const SERVICE_BASE_URL_SEARCH = "http://ws.spotify.com/search/1/";
const SERVICE_BASE_URL_LOOKUP = "http://ws.spotify.com/lookup/1/";
const PLAYBUTTON_BASE_URL = "https://embed.spotify.com/?uri=";
public $autoAddTracksToPlayButton = false;
private $list = array();
// Holds instance
private static $instance;
.....
public static function getSomething()
{
if (!isset(self::$instance))
{
$class = __CLASS__;
self::$instance = new $class;
}
return self::$instance;
}
.....
public function searchTrack($name, $page = 1)
{
$url = self::SERVICE_BASE_URL_SEARCH . "track?q=" . $this->translateString($name) .
$this->addPageSuffix($page);
$contents = $this->requestContent($url);
$xml = new MBSimpleXMLElement($contents);
$tracks = array();
foreach ($xml->track as $track)
{
$tracks[] = $this->extractTrackInfo($track);
}
if ($this->autoAddTracksToPlayButton) {
$this->appendTracksToTrackList($tracks);
}
return $tracks;
}
If you have any suggestions I would be most grateful. Thanks.
You didn't initialize $spotify anywhere, and php made it into stdClass by default, since you were assigning values like to member properties using that variable, but it failed when you tried calling unexisting method on it.
Solution: initialise it before you use it
$spotify = MetaTune::getInstance();
in zf1, we can get controller and action name using
$controller = $this->getRequest()->getControllerName();
$action = $this->getRequest()->getActionName();
How we can achieve this in zf2?
UPDATE:
I tried to get them using
echo $this->getEvent()->getRouteMatch()->getParam('action', 'NA');
echo $this->getEvent()->getRouteMatch()->getParam('controller', 'NA');
But I am getting error
Fatal error: Call to a member function getParam() on a non-object
I like to get them in __construct() method;
Ideally I would like to check if there is no Action is defined it will execute noaction() method. I would check using php method method_exists.
Even simpler:
$controllerName =$this->params('controller');
$actionName = $this->params('action');
you can't access these variables in controller __construct() method, but you can access them in dispatch method and onDispatch method.
but if you would like to check whether action exist or not, in zf2 there is already a built in function for that notFoundAction as below
public function notFoundAction()
{
parent::notFoundAction();
$response = $this->getResponse();
$response->setStatusCode(200);
$response->setContent("Action not found");
return $response;
}
but if you still like to do it manually you can do this using dispatch methods as follow
namespace Mynamespace\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Stdlib\RequestInterface as Request;
use Zend\Stdlib\ResponseInterface as Response;
use Zend\Mvc\MvcEvent;
class IndexController extends AbstractActionController
{
public function __construct()
{
}
public function notFoundAction()
{
parent::notFoundAction();
$response = $this->getResponse();
$response->setStatusCode(200);
$response->setContent("Action not found");
return $response;
}
public function dispatch(Request $request, Response $response = null)
{
/*
* any customize code here
*/
return parent::dispatch($request, $response);
}
public function onDispatch(MvcEvent $e)
{
$action = $this->params('action');
//alertnatively
//$routeMatch = $e->getRouteMatch();
//$action = $routeMatch->getParam('action', 'not-found');
if(!method_exists(__Class__, $action."Action")){
$this->noaction();
}
return parent::onDispatch($e);
}
public function noaction()
{
echo 'action does not exits';
}
}
You will get module , controller and action name like this in Zf2 inside your controller...
$controllerClass = get_class($this);
$moduleNamespace = substr($controllerClass, 0, strpos($controllerClass, '\\'));
$tmp = substr($controllerClass, strrpos($controllerClass, '\\')+1 );
$controllerName = str_replace('Controller', "", $tmp);
//set 'variable' into layout...
$this->layout()->currentModuleName = strtolower($moduleNamespace);
$this->layout()->currentControllerName = strtolower($controllerName);
$this->layout()->currentActionName = $this->params('action');
$controllerName = strtolower(Zend_Controller_Front::getInstance()->getRequest()->getControllerName());