Given I have an arbitrary URL mapped (amongst many others) like this
...
Route::get('/foobar/{parameter}', 'MyFoobarController#index');
...
How can I "reverse parse/resolve" the URL (like http://localhost/foobar/foo) into this configured controller (MyFoobarController) again? Please note: I am not talking about the current Request but a general approach to parse any URL that is mapped in Laravel to its corresponding Controller and Action (anywhere in the code independent of the current request). Thanks!
Update: It should also correctly match Routes, that have parameters in them.
You can compare the URL path, to the paths added to the router. So let's take your example:
Route::get('/foobar', 'MyFoobarController#index');
You can use the Route facade to get a list of all registered routes:
// This is your URL as a string
$url = 'http://localhost/foobar';
// Extract the path from that URL
$path = trim(parse_url($url, PHP_URL_PATH), '/');
// Iterate over the routes until you find a match
foreach (Route::getRoutes() as $route) {
if ($route->getPath() == $path) {
// Access the action with $route->getAction()
break;
}
}
The getAction method will return an array containing the relevant information about the action mapped for that route. You can check out the Illuminate\Routing\Route API for more info on what methods are available for you to use once you have matched a route.
private function getMatchRoutes($request)
{
$referer = $request->header('Referer');
$refererPath = parse_url($referer,PHP_URL_PATH);
$routes = Route::getRoutes()->getRoutes();
$matchedRoute = null;
foreach ($routes as $route) {
$route->compiled = (new RouteCompiler($route))->compile();
if (is_null($route->getCompiled())) continue;
if (preg_match($route->getCompiled()->getRegex(), rawurldecode($refererPath))) {
$matchedRoute = $route;
}
}
if (is_null($matchedRoute)) return $matchedRoute;
return explode('#',$matchedRoute->getActionName())[0];
}
the codes above i wrote is to get controller/action from request referer ,you can replace it by a valid url, try it , may be helpful ~~~
Related
I have a router class in my php project that works like this:
public function dispatch(){
foreach ($this->routes as $url => $action) {
if( $url == $_SERVER['REQUEST_URI'] ){
if(is_callable($action)) return $action();
$actionArr = explode('#', $action);
$controller = 'My\\system\\controllers\\'.$actionArr[0];
$method = $actionArr[1];
return (new $controller)->$method();
}
}
}
And I define the routes like this:
My\system\classes\Registry::get("Router")->add('/My/admin/','AdminController#index');
So when the URL SERVER/My/admin is called the index method of the AdminController class is called.
My problem: How do I handle query strings?
I'd like to have a page with a form. On submit, the form gets sent to SERVER/My/admin/check, i.e. to the check.php page in the admin folder.
I defined the route like this
My\system\classes\Registry::get("Router")->add('/My/admin/check','AdminController#check');
but the URL isn't found, of course, because the query string is attatched to the URL. How should I handle this best?
Before checking $_SERVER['REQUEST_URI'], remove everything past the first ?, if one is present. Use that value to check if it matches with $url. Something as simple as this will do the trick:
$request = $_SERVER['REQUEST_URI'];
if( ($pos = strpos($request, '?')) !== false) $request = substr($request, 0, $pos);
Any controllers that need to work with query parameters should be able to get them from $_GET, or at worst $_SERVER['QUERY_STRING'].
This example is from my project, how I handle this.
REQUEST_URI - The URI which was given in order to access this page; for instance, '/index.html'.
$full_router = $_SERVER['REQUEST_URI'];
strtok() splits a string (string) into smaller strings (tokens), with each token being delimited by any character from token.
$router = strtok($full_router, '?'); // This is how you can handle query parameters
Now you can match the URL with if statement
if($router === '/' ){
include('/pages/home.php');
}
Im using PHP MVC for my site and I have an issue with routing.
When I go to the index (front page), I use http://www.example.com or http://www.example.com/index.
When I go to the contact page, I use http://www.example.com/contact.
When I go to the services or about pages, I use http://www.example.com/content/page/services or http://www.example.com/content/page/about.
My index and contact pages have their own controllers because they are static pages. But the services and about pages are pulled from my db, thus dynamic. So I created a controller, named it content and just pass the parameters needed to get whatever page I want.
I want to make my URLs more consistent. If I go to the services or about pages, I want to use http://www.example.com/services or http://www.example.com/about.
How can I change my routing to meet this requirement? I ultimately would like to be able to create pages in my db, and then pull the page with a URL that looks like it has its own controller. Instead of having to call the content controller to get it to work.
Below are my controllers and what methods they contain, as well as my routing code.
Controllers:
IndexController
function: index
ContentController
function: page
function: sitemap
ContactController
function: index
function: process
Routing
class Application
{
// #var mixed Instance of the controller
private $controller;
// #var array URL parameters, will be passed to used controller-method
private $parameters = array();
// #var string Just the name of the controller, useful for checks inside the view ("where am I ?")
private $controller_name;
// #var string Just the name of the controller's method, useful for checks inside the view ("where am I ?")
private $action_name;
// Start the application, analyze URL elements, call according controller/method or relocate to fallback location
public function __construct()
{
// Create array with URL parts in $url
$this->splitUrl();
// Check for controller: no controller given ? then make controller = default controller (from config)
if (!$this->controller_name) {
$this->controller_name = Config::get('DEFAULT_CONTROLLER');
}
// Check for action: no action given ? then make action = default action (from config)
if (!$this->action_name OR (strlen($this->action_name) == 0)) {
$this->action_name = Config::get('DEFAULT_ACTION');
}
// Rename controller name to real controller class/file name ("index" to "IndexController")
$this->controller_name = ucwords($this->controller_name) . 'Controller';
// Check if controller exists
if (file_exists(Config::get('PATH_CONTROLLER') . $this->controller_name . '.php')) {
// Load file and create controller
// example: if controller would be "car", then this line would translate into: $this->car = new car();
require Config::get('PATH_CONTROLLER') . $this->controller_name . '.php';
$this->controller = new $this->controller_name();
// Check for method: does such a method exist in the controller?
if (method_exists($this->controller, $this->action_name)) {
if (!empty($this->parameters)) {
// Call the method and pass arguments to it
call_user_func_array(array($this->controller, $this->action_name), $this->parameters);
} else {
// If no parameters are given, just call the method without parameters, like $this->index->index();
$this->controller->{$this->action_name}();
}
} else {
header('location: ' . Config::get('URL') . 'error');
}
} else {
header('location: ' . Config::get('URL') . 'error');
}
}
// Split URL
private function splitUrl()
{
if (Request::get('url')) {
// Split URL
$url = trim(Request::get('url'), '/');
$url = filter_var($url, FILTER_SANITIZE_URL);
$url = explode('/', $url);
// Put URL parts into according properties
$this->controller_name = isset($url[0]) ? $url[0] : null;
$this->action_name = isset($url[1]) ? $url[1] : null;
// Remove controller name and action name from the split URL
unset($url[0], $url[1]);
// rebase array keys and store the URL parameters
$this->parameters = array_values($url);
}
}
}
In order to do this you should map your urls to controllers, check following example:
// route mapping 'route' => 'controller:method'
$routes = array(
'/service' => 'Content:service'
);
also controller can be any php callable function.
Answer Version 2:
Brother in the simplest mode, let's say you have an entity like below:
uri: varchar(255), title: varchar(255), meta_tags: varchar(500), body: text
and have access to StaticPageController from www.example.com/page/ url and what ever it comes after this url will pass to controller as uri parameter
public function StaticPageController($uri){
// this can return a page entity
// that contains what ever a page needs.
$page = $pageRepository->findByUri($uri)
// pass it to view layer
$this->renderView('static_page.phtml', array('page' => $page));
}
I hope this helps.
I've literally downloaded Laravel today and like the looks of things but i'm struggeling on 2 things.
1) I like the controllers' actions method of analysing urls instead of using routes, it seems to keep everything together more cleanly, but lets say I want to go to
/account/account-year/
how can I write an action function for this? i.e.
function action_account-year()...
is obviously not valid syntax.
2) If i had
function action_account_year( $year, $month ) { ...
and visited
/account/account_year/
An error would be displayed about missing arguments, how do you go about making this user friendly/load diff page/display an error??
You would have to manually route the hyphenated version, e.g.
Route::get('account/account-year', 'account#account_year');
Regarding the parameters, it depends on how you are routing. You must accept the parameters in the route. If you are using full controller routing (e.g. Route::controller('account')) then the method will be passed parameters automatically.
If you are manually routing, you have to capture the params,
Route::get('account/account-year/(:num)/(:num)', 'account#account_year');
So visiting /account/account-year/1/2 would do ->account_year(1, 2)
Hope this helps.
You can think of the following possibility as well
class AccountController extends BaseController {
public function getIndex()
{
//
}
public function getAccountYear()
{
//
}
}
Now simply define a RESTful controller in your routes file in the following manner
Route::controller('account', 'AccountController');
Visiting 'account/account-year' will automatically route to the action getAccountYear
I thought I'd add this as an answer in case anyone else is looking for it:
1)
public function action_account_year($name = false, $place = false ) {
if( ... ) {
return View::make('page.error' );
}
}
2)
not a solid solutions yet:
laravel/routing/controller.php, method "response"
public function response($method, $parameters = array())
{
// The developer may mark the controller as being "RESTful" which
// indicates that the controller actions are prefixed with the
// HTTP verb they respond to rather than the word "action".
$method = preg_replace( "#\-+#", "_", $method );
if ($this->restful)
{
$action = strtolower(Request::method()).'_'.$method;
}
else
{
$action = "action_{$method}";
}
$response = call_user_func_array(array($this, $action), $parameters);
// If the controller has specified a layout view the response
// returned by the controller method will be bound to that
// view and the layout will be considered the response.
if (is_null($response) and ! is_null($this->layout))
{
$response = $this->layout;
}
return $response;
}
I'm trying to implement canonical URLs and combine it with custom route-classes.
The URL-scheme is something like this:
/category-x/article/123
/category-y/article/123
I create a custom route-class extending Zend_Controller_Router_Route_Regex and checks that the article 123 exists and that the URL includes the correct category-name. If article 123 belongs in category-x and the user is accessing category-y I want to redirect to the correct URL.
But the routes does not have any obvious possibility to do this directly. What's the best practice approach here?
I often do this in my action controller. Something like this...
// assuming GET /category-y/article/123
// $article->url is generated, and contains /category-x/article/123
if (this->_request->getRequestUri() != $article->url) {
return $this->_helper->redirector->goToUrl($article->url);
}
In this example, $article->url would need to be generated from your database data. I often use this to verify a correct slug, when I also pull in the object id.
You could also potentially move this to your routing class, if you wanted to use a custom one instead of using Regex (you could subclass it).
I ended up with this solution:
The custom route-class creates the canonical URL in its match()-method like this:
public function match($path, $partial = false) {
$match = parent::match($path, $partial);
if (!empty($match)) {
$article = $this->backend->getArticle($match['articleId']);
if (!$article) {
throw new Zend_Controller_Router_Exception('Article does not exist', 404);
}
$match['canonicalUrl'] = $this->assemble(array(
'title' => $article->getTitle(),
'articleId' => $article->getId()
));
}
return $match;
}
$article is populated inside match() if the parent::match() returns array.
I've created a front controller plugin which hooks on the routeShutdown() like this:
public function routeShutdown(Zend_Controller_Request_Abstract $request) {
if ($request->has('canonicalUrl')){
$canonicalUrl = $request->getBaseUrl() . '/' . $request->get('canonicalUrl');
if ($canonicalUrl != $request->getRequestUri()) {
$this->getResponse()->setRedirect($canonicalUrl, 301);
}
}
}
It simply checks if the route(custom or native Zend) created a canonical URL and if the requested URL does not match, redirect to the correct canonical URL.
In symfony, is there a method that I can use which does a reverse lookup on my routes to determine the module and action a URL points to?
Say, something like:
get_module("http://host/cars/list"); // ("cars")
get_action("http://host/frontend_dev.php/cars/list"); // ("list")
Bear in mind, I don't want to perform simple string-hacking to do this as there may be mappings that are not quite so obvious:
get_module("/"); // (What this returns is entirely dependent on the configured routes.)
Thanks!
Use the sfRouting class to match URLs. This is part of the sfContext object. Your code (in an action) would look like this:
public function executeSomeAction(sfWebRequest $request)
{
if($params = $this->getContext()->getRouting()->parse('/blog'))
{
$module = $params['module']; // blog
$action = $params['action']; // index
$route = $params['_sf_route'] // routing instance (for fun)
}
else
{
// your URL did not match
}
}