Parameters in Custom Router PHP - php

I am trying to create a custom router.
This is what I have so far:
<?php
/**
* Created by PhpStorm.
* User: antony
* Date: 5/30/16
* Time: 3:31 PM
*/
namespace Fab\Router;
class Router
{
private $_getUri = array();
private $_getController = array();
private $_getMethod = array();
private $_postUri = array();
private $_postController = array();
private $_postMethod = array();
public function __construct()
{
}
/**
* Build a collection of internal GET URLs to look for
* #param $uri - The url that the user types in the browser
* #param $controller - The controller that will handle the url
* #param $method - The method of the controller that will run
*/
public function get($uri, $controller, $method)
{
$this->_getUri[] = $uri;
$this->_getController[] = $controller;
$this->_getMethod[] = $method;
}
/**
* Build a collection of internal POST URLs to look for
* #param $uri - The url that the user types in the browser
* #param $controller - The controller that will handle the url
* #param $method - The method of the controller that will run
*/
public function post($uri, $controller, $method)
{
$this->_postUri[] = $uri;
$this->_postController[] = $controller;
$this->_postMethod[] = $method;
}
public function submit()
{
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
var_dump($_SERVER['REQUEST_URI']);
echo "\n";
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); //get the url
var_dump($path);
foreach ($this->_getUri as $key => $value)
{
if (preg_match("#^$value$#", $path))
{
// echo $key . ' => ' . $value; //See what the $path returns
//Instantiate Controller
$controller = 'Fab\Controllers\\' . $this->_getController[$key];
$controller = new $controller();
//Call the appropriate method
$method = $this->_getMethod[$key];
$controller->$method();
}
}
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); //get the url
foreach ($this->_postUri as $key => $value)
{
if (preg_match("#^$value$#", $path))
{
//echo $key . ' => ' . $value; //See what the $path returns
//Instantiate Controller
$controller = 'Fab\Controllers\\' . $this->_postController[$key];
$controller = new $controller();
//Call the appropriate method
$method = $this->_postMethod[$key];
$controller->$method();
}
}
}
}
}
This allows me to say in my index.php something like:
$router->get('/portfolio', 'MainController', 'portfolio');
What I want to do now is to create an individual page for every item in the portfolio and give it a unique URL.
For example, if I have the item 'dinosaur' in my portfolio, I want to be able to say:
$router->get('/portfolio/dinosaur', 'MainController', 'searchIfDinosaurExistsInDatabase');
So generally, I would like to have in my index sth like:
$router->get('/portfolio/{item}', 'MainController', 'searchInDbForItem');
How can I modify my router in order to achieve this?

You need to add an regular expression to route. For example like this (it's a very simple example):
$router->get('/portfolio/[\w\d]+', 'MainController', 'searchInDbForItem');`
And you need use preg_match for comparison a route and an url.
It's a very simple example of router https://github.com/newage/example/blob/master/core/Route/HttpRoute.php

Related

PHP MVC "route not found"

i am using php mvc and when i try to edit a specific row, it says, "no route matched" but the route is well defined.
Here is the route for match
$router->add('{controller}/{id:\d+}/{action}');
and here is Router.php.
<?php
namespace Core;
/**
*Router
*/
class Router
{
protected $routes = [];
//saves the parameter from the matched route
protected $params = [];
/**
*Below, params=[] indicates that the paramentes in the url can be empty
*/
public function add($route, $params = [])
{
// Convert the route to a regular expression: escape forward slashes
$route = preg_replace('/\//', '\\/', $route);
// Convert variables e.g. {controller}
$route = preg_replace('/\{([a-z]+)\}/', '(?P<\1>[a-z-]+)', $route);
// Convert variables with custom regular expressions e.g. {id:\d+}
$route = preg_replace('/\{([a-z]+):([^\}]+)\}/', '(?P<\1>\2)', $route);
// Add start and end delimiters, and case insensitive flag
$route = '/^' . $route . '$/i';
$this->routes[$route] = $params;
}
/**
*matches the route and sets the parameters
* $url: The route URL
* returns true is match is found else flase
*/
public function match($url)
{
//Matches to the fixed URL format /controller/action
//$reg_exp = "/^(?P<controller>[a-z-]+)\/(?P<action>[a-z-]+)$/";
foreach ($this->routes as $route => $params) {
if (preg_match($route, $url, $matches))
{
//get named capture group values
//$params = [];
foreach ($matches as $key => $match) {
if (is_string($key))
{
$params[$key] = $match;
}
}
$this->params = $params;
return true;
}
}
return false;
}
// foreach ($this->routes as $route => $params) {
// if ($url == $route) {
// $this->params = $params;
// return true;
// }
// }
// return false;
// if (preg_match($reg_exp, $url, $matches))
// {
//
//
// }
//gets the currently matched parameters
public function getParams()
{
return $this->params;
}
public function dispatch($url)
{
$url = $this->removeQueryStringVariables($url);
if($this->match($url)){
$controller = $this->params['controller'];
$controller = $this->convertToStudlyCaps($controller); //Text can easily be converted from uppercase or lowercase to sTudLYcAPs
//$controller = "App\Controllers\\$controller";
$controller = $this->getNameSpace() . $controller;
if (class_exists($controller)){
$controller_object = new $controller($this->params);
$action = $this->params['action'];
$action = $this->convertToCamelCase($action);
if (is_callable([$controller_object, $action]))
{
$controller_object->$action();
}else{
throw new \Exception("Method $action (in controller $controller) not found");
}
}else{
throw new \Exception("Controller class $controller not found");
}
}else {
throw new \Exception("No route matched");
}
}
/**
*Convert the string with hypens to StudlyCaps,
*e.g. post-authors => PostAuthors
*/
protected function convertToStudlyCaps($string)
{
return str_replace(' ', '', ucwords(str_replace('-', ' ', $string)));
}
/**
*Convert the string with hypens to camelCase,
*e.g. add-new => addNew
*/
protected function convertToCamelCase($string)
{
return lcfirst($this->convertToStudlyCaps($string));
}
/**
*This removes query string variable i.e. posts/index&page=1 => posts/index
*
*/
protected function removeQueryStringVariables($url)
{
if($url != '')
{
$parts = explode('&', $url, 2);
if(strpos($parts[0], '=') === false){
$url = $parts[0];
}
else{
$url = '';
}
}
return $url;
}
/*
*Get the namespace for the controller class. The namespace defined in the
*route parameters is added if present.
*/
protected function getNameSpace()
{
$namespace = 'App\Controllers\\';
if(array_key_exists('namespace', $this->params))
{
$namespace .= $this->params['namespace'] . '\\';
}
return $namespace;
}
}
?>
P.S. i can add values to database but cannot edit it, as it says route not matched. if it helps,here is index.php:
<?php
/**
* FIRST CONTROLLER
*
*/
/**
*Twig
*/
require_once dirname(__DIR__) . '/vendor/Twig/lib/Twig/autoload.php';
Twig_Autoloader::register();
/**
*AutoLoader
*
*/
spl_autoload_register(function ($class){
$root = dirname(__DIR__); //gets the parent directory
$file = $root . '/' . str_replace('\\', '/', $class) . '.php';
if (is_readable($file))
{
require $root . '/' . str_replace('\\', '/', $class) . '.php';
}
});
/**
*Error and Exception handling
*
*/
error_reporting(E_ALL);
set_error_handler('Core\Error::errorHandler');
set_exception_handler('Core\Error::exceptionHandler');
/**
*Routing
*
*/
//require '../Core/Router.php';
$router = new Core\Router();
//Routes
$router->add('', ['controller' => 'Home', 'action' => 'index']);
$router->add('{controller}/{action}');
$router->add('admin/{controller}/{action}', ['namespace' => 'Admin']);
$router->add('{controller}/{id:\d+}/{action}');
$router ->dispatch($_SERVER['QUERY_STRING']);
/*
// Display the routing table
echo '<pre>';
//var_dump($router->getRoutes());
echo htmlspecialchars(print_r($router->getRoutes(),true));
echo '<pre>';
//Match the requested route
$url = $_SERVER['QUERY_STRING'];
if ($router->match($url)) {
echo '<pre>';
var_dump($router->getParams());
echo '<pre>';
}else{
echo "No Route Found For URL '$url'";
}
*/
?>

How to combine routing and templating?

So, I've created a webshop system. It all worked perfectly, except for that projects can always be improved. So someone told me that 'Templating' and 'Routing' would be nice improvements. I've written classes for both of them, and they work fine! Now, I do wish to know how to combine them? How can I combine these classes so that data from the routing (to determine the content) needs to be placed inside the template. How would I do this?
Templating class:
class Template
{
private $assignedValues = array();
private $tpl;
/*
** #description Creates one single instance of itself and checks whether the template file exists.
** #param $path [string] This is the path to the template
*/
public function __construct($_path = '')
{
if(!empty($_path)){
if(file_exists($_path)){
$this->tpl = file_get_contents($_path);
}
else{
echo '<b>Template Error:</b> File Inclusion Error.';
}
}
}
/*
** #description Assign a value to a part in the template.
** #param $_searchString [string] This is the part in the template that needs to be replaced
** #param $_replaceString [string] This is the code/text that will replace the part in the template
*/
public function assign($_searchString, $_replaceString)
{
if(!empty($_searchString)){
$this->assignedValues[strtoupper($_searchString)] = $_replaceString;
}
}
/*
** #description Shows the final result of the page.
*/
public function show()
{
if(count($this->assignedValues > 0)){
foreach ($this->assignedValues as $key => $value) {
$this->tpl = str_replace('{'.$key.'}', $value, $this->tpl);
}
}
echo $this->tpl;
}
/*
** #description Quickly load a part of the page
** #param $quickLoad [string] This is the name of the file that will be loaded and assigned
** #param $_searchString [string] This is the part in the template that needs to be replaced
*/
public function quickLoad($_searchString, $part)
{
if(file_exists(INCLUDES.DS.$part.'.php')){
$this->assign($_searchString,include(INCLUDES.DS.$part.'.php'));
}
else{
return "That file does not exist!";
}
}
}
And the routing class:
class Route
{
protected $controller = 'App';
protected $method = 'Call';
protected $params = [];
/*
** #description Loads the classes and methods which are referred to.
*/
public function __construct()
{
$url = $this->parseUrl();
if($this->checkUrl())
{
unset($url[0]);
if(isset($url[1]))
{
if (file_exists('core/classes/' . $url[1] . '.class.php'))
{
$this->controller = $url[1];
unset($url[1]);
}
}
require_once('core/classes/' . $this->controller . '.class.php');
$this->controller = new $this->controller;
if (isset($url[2]))
{
if (method_exists($this->controller, $url[2]))
{
$this->method = $url[2];
unset($url[2]);
}
}
$this->params = $url ? array_values($url) : [];
$this->arrayUrl($this->params);
call_user_func_array([$this->controller, $this->method], $this->params);
}
}
/*
** #description Check whether the URL part contains a string
*/
public function checkUrl($index = '0',$value = 'Route'){
$url = $this->parseUrl();
if($url[$index] == $value){
return true;
}
return false;
}
/*
** #description Splits the url into pieces.
*/
protected function parseUrl()
{
if(isset($_GET['url']))
{
return $url = explode('/', filter_var(rtrim(urldecode($_GET['url']), '/'), FILTER_SANITIZE_URL));
}
}
/*
** #description Sets arrays in routes.
*/
protected function arrayUrl($params = array())
{
foreach($params as $index => $param)
{
if (preg_match('/>/',$param))
{
$newParam = explode('>', $param);
unset($this->params[$index]);
$this->params['fields'][$newParam[0]] = $newParam[1];
}
else{
unset($this->params[$index]);
$this->params[] = $param;
}
}
print_r($this->params);
}
}
I can access my routes by URL's like this:
http://localhost:8080/Webshop/Route/User/logout
With: Class & Method.
This is a simple example that I already use, because no data needs to be showed using this method. It only logs out the user that is logged in. After that, you get redirected to the home page. But how could I use routing for other pages? For example, update some user data without having to create an update file?
EDIT:
This is an example of a page I use now (index.php):
<?php
/*
** #description Includes config.php once so that we can use classes, defines etcetera.
*/
require_once('core/preferences/config.php');
/*
** #description Instanciate new route object.
*/
$route = new Route();
/*
** #description Check if a route isset. When not, continue, else: run route
*/
if(!$route->checkUrl())
{
/*
** #description Instanciate new template object.
*/
$template = new Template(TEMPLATES_PATH .'/camerashop24.tpl.html');
/*
** #description Assign values.
*/
$template->assign('title', 'Home');
$template->assign('root', '');
$template->quickLoad('loginout', 'loginout');
$template->quickLoad('title_block', 'title_block');
$template->quickLoad('cart','cart');
$template->quickLoad('menu', 'menu');
$template->assign('page', 'Home');
$breadcrumbs = new Breadcrumbs($_SERVER["REQUEST_URI"],'');
$template->assign('breadcrumbs', $breadcrumbs->data());
$content = "";
foreach(explode(",",Config::get('settings/front_page_cat')) as $item) {
$content .= "<div id='blue-box' class='blue-box'><h2 style='color: white;'>" . strtoupper($item) . "</h2></div>";
$category = new Category();
$category->find($item);
if($category->exists($item)){
foreach (explode(",",$category->data()->products) as $item) {
$product = new Product($item);
$product->find($item);
$content .= '' . $product->showProduct($product->data()->type,$product->data()->name,$product->data()->price) . '';
}
}
}
$template->assign('text', $content);
$template->quickLoad('footer','footer');
/*
** #description Showing content.
*/
$template->show();
}
But, what I want as an answer, how can I show the data from the routing (select users profile for example) in this template without having to create a page for it like this one.
I think you're gonna have to modify your Routing class. Ideally you want the routing class to give YOU the ability to tell it what controller the route should use. Th controller you select would act as the middle man for processing. Each controller's method would represent a route, something like this:
/**
* URL = www.test.com/index.php/home
* URL = www.test.com/index.php/about
*
* ROUTE Class:
* - should define routes. Example:
*
* $route["/home"] = MainController/home
* $route["/about"] = MainController/about
* $route["/*"] = MainController/*
*
*/
class MainController
{
public function home()
{
$template = new Template(TEMPLATES_PATH . 'home.tpl.html');
$template->show();
}
public function about()
{
$template = new Template(TEMPLATES_PATH . 'about.tpl.html');
$template->show();
}
}

Failed to load function

i tried to generate URL with load another class
the error messages is :
Notice: Undefined variable: user in
/var/www/html/lab/system/core/Router.php on line 260
Warning: call_user_func_array() expects parameter 1 to be a valid
callback, first array member is not a valid class name or object in
/var/www/html/lab/system/core/Router.php on line 260
Router.php :
<?php
require_once 'class.php';
class AltoRouter {
/**
* #var array Array of all routes (incl. named routes).
*/
protected $routes = array();
/**
* #var array Array of all named routes.
*/
protected $namedRoutes = array();
/**
* #var string Can be used to ignore leading part of the Request URL (if main file lives in subdirectory of host)
*/
protected $basePath = '';
/**
* #var array Array of default match types (regex helpers)
*/
protected $matchTypes = array(
'i' => '[0-9]++',
'a' => '[0-9A-Za-z]++',
'h' => '[0-9A-Fa-f]++',
'*' => '.+?',
'**' => '.++',
'' => '[^/\.]++'
);
/**
* Create router in one call from config.
*
* #param array $routes
* #param string $basePath
* #param array $matchTypes
*/
public function __construct( $routes = array(), $basePath = '', $matchTypes = array() ) {
$this->addRoutes($routes);
$this->setBasePath($basePath);
$this->addMatchTypes($matchTypes);
}
/**
* Retrieves all routes.
* Useful if you want to process or display routes.
* #return array All routes.
*/
public function getRoutes() {
return $this->routes;
}
/**
* Add multiple routes at once from array in the following format:
*
* $routes = array(
* array($method, $route, $target, $name)
* );
*
* #param array $routes
* #return void
* #author Koen Punt
* #throws Exception
*/
public function addRoutes($routes){
if(!is_array($routes) && !$routes instanceof Traversable) {
throw new \Exception('Routes should be an array or an instance of Traversable');
}
foreach($routes as $route) {
call_user_func_array(array($this, 'map'), $route);
}
}
/**
* Set the base path.
* Useful if you are running your application from a subdirectory.
*/
public function setBasePath($basePath) {
$this->basePath = $basePath;
}
/**
* Add named match types. It uses array_merge so keys can be overwritten.
*
* #param array $matchTypes The key is the name and the value is the regex.
*/
public function addMatchTypes($matchTypes) {
$this->matchTypes = array_merge($this->matchTypes, $matchTypes);
}
/**
* Map a route to a target
*
* #param string $method One of 5 HTTP Methods, or a pipe-separated list of multiple HTTP Methods (GET|POST|PATCH|PUT|DELETE)
* #param string $route The route regex, custom regex must start with an #. You can use multiple pre-set regex filters, like [i:id]
* #param mixed $target The target where this route should point to. Can be anything.
* #param string $name Optional name of this route. Supply if you want to reverse route this url in your application.
* #throws Exception
*/
public function map($method, $route, $target, $name = null) {
$this->routes[] = array($method, $route, $target, $name);
if($name) {
if(isset($this->namedRoutes[$name])) {
throw new \Exception("Can not redeclare route '{$name}'");
} else {
$this->namedRoutes[$name] = $route;
}
}
return;
}
/**
* Reversed routing
*
* Generate the URL for a named route. Replace regexes with supplied parameters
*
* #param string $routeName The name of the route.
* #param array #params Associative array of parameters to replace placeholders with.
* #return string The URL of the route with named parameters in place.
* #throws Exception
*/
public function generate($routeName, array $params = array()) {
// Check if named route exists
if(!isset($this->namedRoutes[$routeName])) {
throw new \Exception("Route '{$routeName}' does not exist.");
}
// Replace named parameters
$route = $this->namedRoutes[$routeName];
// prepend base path to route url again
$url = $this->basePath . $route;
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
foreach($matches as $match) {
list($block, $pre, $type, $param, $optional) = $match;
if ($pre) {
$block = substr($block, 1);
}
if(isset($params[$param])) {
$url = str_replace($block, $params[$param], $url);
} elseif ($optional) {
$url = str_replace($pre . $block, '', $url);
}
}
}
return $url;
}
/**
* Match a given Request Url against stored routes
* #param string $requestUrl
* #param string $requestMethod
* #return array|boolean Array with route information on success, false on failure (no match).
*/
public function match($requestUrl = null, $requestMethod = null) {
$params = array();
$match = false;
// set Request Url if it isn't passed as parameter
if($requestUrl === null) {
$requestUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
}
// strip base path from request url
$requestUrl = substr($requestUrl, strlen($this->basePath));
// Strip query string (?a=b) from Request Url
if (($strpos = strpos($requestUrl, '?')) !== false) {
$requestUrl = substr($requestUrl, 0, $strpos);
}
// set Request Method if it isn't passed as a parameter
if($requestMethod === null) {
$requestMethod = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
}
foreach($this->routes as $handler) {
list($method, $_route, $target, $name) = $handler;
$methods = explode('|', $method);
$method_match = false;
// Check if request method matches. If not, abandon early. (CHEAP)
foreach($methods as $method) {
if (strcasecmp($requestMethod, $method) === 0) {
$method_match = true;
break;
}
}
// Method did not match, continue to next route.
if(!$method_match) continue;
// Check for a wildcard (matches all)
if ($_route === '*') {
$match = true;
} elseif (isset($_route[0]) && $_route[0] === '#') {
$pattern = '`' . substr($_route, 1) . '`u';
$match = preg_match($pattern, $requestUrl, $params);
} else {
$route = null;
$regex = false;
$j = 0;
$n = isset($_route[0]) ? $_route[0] : null;
$i = 0;
// Find the longest non-regex substring and match it against the URI
while (true) {
if (!isset($_route[$i])) {
break;
} elseif (false === $regex) {
$c = $n;
$regex = $c === '[' || $c === '(' || $c === '.';
if (false === $regex && false !== isset($_route[$i+1])) {
$n = $_route[$i + 1];
$regex = $n === '?' || $n === '+' || $n === '*' || $n === '{';
}
if (false === $regex && $c !== '/' && (!isset($requestUrl[$j]) || $c !== $requestUrl[$j])) {
continue 2;
}
$j++;
}
$route .= $_route[$i++];
}
$regex = $this->compileRoute($route);
$match = preg_match($regex, $requestUrl, $params);
}
if(($match == true || $match > 0)) {
if($params) {
foreach($params as $key => $value) {
if(is_numeric($key)) unset($params[$key]);
}
}
return array(
'target' => $target,
'params' => $params,
'name' => $name
);
}
}
return false;
}
/**
* Compile the regex for a given route (EXPENSIVE)
*/
private function compileRoute($route) {
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
$matchTypes = $this->matchTypes;
foreach($matches as $match) {
list($block, $pre, $type, $param, $optional) = $match;
if (isset($matchTypes[$type])) {
$type = $matchTypes[$type];
}
if ($pre === '.') {
$pre = '\.';
}
//Older versions of PCRE require the 'P' in (?P<named>)
$pattern = '(?:'
. ($pre !== '' ? $pre : null)
. '('
. ($param !== '' ? "?P<$param>" : null)
. $type
. '))'
. ($optional !== '' ? '?' : null);
$route = str_replace($block, $pattern, $route);
}
}
return "`^$route$`u";
}
}
$router = new AltoRouter();
$user = new Users();
$router->setBasePath('/lab/system/core/Router.php');
############################################################
$router->map('GET', '/home', function()
{
echo 'Home page';
}, 'home');
$router->map('GET', '/user', function()
{
call_user_func_array(array($user, 'user'), array());
}, 'user');
############################################################
$match = $router->match();
if($match && is_callable($match['target']))
{
call_user_func_array($match['target'], $match['params']);
}
else
{
echo "404 Not Found";
}
?>
class.php
<?php
class Users
{
function user()
{
echo "User page";
}
}
?>
anyone can explain it? thanks
did you try this ?
$router->map('GET', '/user', function($user)
{
call_user_func_array(array($user, 'user'), array());
}, 'user');

Creating customer specific routes in Zend Framework 2

I'm developing a WebApp which (as usual) must support customer specific functionalities.
To achieve it I plan to set the customer name in the local app configuration (config/autoload/local.php )
configuration file so that I can use it to call the specialized code later on.
The module folder structure is this:
/module/Application
/module/Application/config
/module/Application/src
/module/Application/src/Application
/module/Application/src/Application/Controller
/module/Application/src/Application/Controller/[customer_instance_name]
/module/Application/src/Application/Model
/module/Application/src/Application/Model/[customer_instance_name]
/module/Application/view
/module/Application/view/Application
/module/Application/view/Application/[action]
/module/Application/view/Application/[action]/[customer_instance_name]
Using a custom ViewModel I inject the specific customer name to the template path:
namespace Application\Model;
use Zend\View\Model\ViewModel;
use Zend\View\Resolver\TemplatePathStack;
use Zend\Mvc\Service\ViewTemplatePathStackFactory;
class MyViewModel extends ViewModel
{
private $customInstanceName;
private $pathStack;
/**
* Constructor
*
* #param null|array|Traversable $variables
* #param array|Traversable $options
*/
public function __construct($variables = null, $options = null)
{
parent::__construct ( $variables, $options );
$serviceLocator = MySingleton::instance()->serviceLocator;
$factory = new ViewTemplatePathStackFactory();
$this->pathStack = $factory->createService($serviceLocator);
$config = $serviceLocator->get('config');
if (isset($config['custom_instance_name']) AND ($config['custom_instance_name']!='')) {
$this->customInstanceName = $config['custom_instance_name'];
} else {
$this->customInstanceName = false;
}
}
/**
* Set the template to be used by this model
*
* #param string $template
* #return ViewModel
*/
public function setTemplate($template)
{
$this->template = (string) $template;
if ( $this->customInstanceName === false) {
return $this;
}
$pathComponents = explode('/', (string) $template);
$last = array_pop($pathComponents);
array_push($pathComponents, $this->customInstanceName);
array_push($pathComponents, $last);
$customTemplate = implode('/', $pathComponents);
if ($this->pathStack->resolve($customTemplate) !== false) {
$this->template = $customTemplate;
}
return $this;
}
}
Using the "Decorator Pattern" I can achieve the same customization level on my Models.
I'm having problem to handle specific behavior. In this case I plan to create custom Controllers extending
my base controller class, but I'unable to call those controllers since the routing is defined on the module
config (and I was unable to change it in runtime).
My questions are:
1) Is this approach correct, or there is a better way to do it?
2) If the approach is correct, how can I define a custom router to be used when the ServiceManager reads my routing config?
Just found a solution. Will register it here hoping someone will benefit from it.
All I had to do was to create a specific router class with a match method which returns the correct routing target for each customer controller, and add it to my module.config.php as the type for each action.
namespace TARGETNAMESPACE;
use Traversable;
use Zend\Mvc\Router\Exception;
use Zend\Mvc\Router\Http\RouteInterface;
use Zend\Mvc\Router\Http\RouteMatch;
use Zend\Mvc\Router\Http\Literal;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\RequestInterface as Request;
class MyRouterLiteral extends Literal {
public function match(Request $request, $pathOffset = null) {
if (! method_exists($request, 'getUri')) {
return null;
}
$uri = $request->getUri();
$path = $uri->getPath();
if ($pathOffset !== null) {
if ($pathOffset >= 0 && strlen($path) >= $pathOffset && ! empty($this->route)) {
if (strpos($path, $this->route, $pathOffset) === $pathOffset) {
return new RouteMatch($this->getDefaults(), strlen($this->route));
}
}
return null;
}
if ($path === $this->route) {
return new RouteMatch($this->getDefaults(), strlen($this->route));
}
return null;
}
private function getDefaults() {
$aux = explode('\\', $this->defaults['controller']);
$last = array_pop($aux);
array_push($aux, '[CUSTOM_INSTANCE_NAME]');
array_push($aux, '[CUSTOM_INSTANCE_NAME]'.$last);
$result = $this->defaults;
$result['controller'] = implode('\\', $aux);
return $result;
}
}
To address all cases I had to create a second custom router (for segment routes) which follows the same rules and can be easily derived from the above code.

How can I get the action name in a Symfony2 controller?

Is there a way to get the name of the action in a Symfony2 controller?
public function createAction(Request $request, $title) {
// Expected result: create
$name = $this->getActionName();
}
useļ¼š
$request->attributes->get('_controller');
// will get yourBundle\Controller\yourController::CreateAction
$params = explode('::',$request->attributes->get('_controller'));
// $params[1] = 'createAction';
$actionName = substr($params[1],0,-6);
// $actionName = 'create';
I found this snippet (here):
$matches = array();
$controller = $this->getRequest()->attributes->get('_controller');
preg_match('/(.*)\\\(.*)Bundle\\\Controller\\\(.*)Controller::(.*)Action/', $controller, $matches);
which seems to be a promising approach. This regexp actually doesn't work. But it won't be hard to fetch the action name by using strstr(). Works!
And returns (see example)
Array
(
[0] => Acme\MyBundle\Controller\MyController::myAction
[1] => Acme
[2] => My
[3] => My
[4] => my
)
If input was Acme\MyBundle\Controller\MyController::myAction.
Now, I am using this with Symfony 2.8, (and Symfony3):
<?php
namespace Company\Bundle\AppBundle\Component\HttpFoundation;
use Symfony\Component\HttpFoundation\Request as BaseRequest;
/**
* Request shortcuts.
*/
class Request extends BaseRequest
{
/**
* Extract the action name.
*
* #return string
*/
public function getActionName()
{
$action = $this->get('_controller');
$action = explode('::', $action);
// use this line if you want to remove the trailing "Action" string
//return isset($action[1]) ? preg_replace('/Action$/', '', $action[1]) : false;
return $action[1];
}
/**
* Extract the controller name (only for the master request).
*
* #return string
*/
public function getControllerName()
{
$controller = $this->get('_controller');
$controller = explode('::', $controller);
$controller = explode('\\', $controller[0]);
// use this line if you want to remove the trailing "Controller" string
//return isset($controller[4]) ? preg_replace('/Controller$/', '', $controller[4]) : false;
return isset($controller[4]) ? $controller[4] : false;
}
}
To use this custom request class, you must "use" it in your web/app*.php controllers:
use Company\Bundle\AppBundle\Component\HttpFoundation\Request;
// ...
$request = Request::createFromGlobals();
// ...
Then in your controller:
class AppController extends Controller
{
/**
* #Route("/", name="home_page")
* #Template("")
*
* #return array
*/
public function homePageAction(Request $request)
{
$controllerName = $request->getControllerName();
$actionName = $request->getActionName();
dump($controllerName, $actionName); die();
// ...
}
Will output:
AppController.php on line 27:
"AppController"
AppController.php on line 27:
"homePageAction"
You can also access these functions through the RequestStack service:
class MyService
{
/**
* #param RequestStack $requestStack
*/
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function myService()
{
$this->controllerName = $this->requestStack->getMasterRequest()->getControllerName();
$this->actionName = $this->requestStack->getMasterRequest()->getActionName();
// ...
}
If you use Controller as a Service than the schema is different:
$request->attributes->get('_controller'); will return "service_id:createAction"
A possible solution for both schemas:
$controller = $request->attributes->get('_controller');
$controller = str_replace('::', ':', $controller);
list($controller, $action) = explode(':', $controller);
In all version of symfony and without $request or container, service or nothing else... , directly in your method
public function myMethod(){
$methodName = __METHOD__;
return $methodName;
}
// return App\Controller\DefaultController::myMethod
public function mySecondMethod(){
$methodName = explode('::', __METHOD__);
return $methodName[1];
}
// return mySecondMethod

Categories