Route Not Found In Prefix Root Path - php

I'm trying to setting up symfony/routing component on my project..
Things go well but when I define prefix for routes, it throw route not found exception for root path of this prefix.
For example, let assume I have bunch of admin routes. Instead of defining "admin" keyword on each route I made a prefix route all of those. So my dashboard path turned into "/" from "/admin". And now it throws route not found error..
When I checked the route collections. Dashboard path seems as "/admin/". And its not matching with REQUEST_URI
Am I setting up the component poorly or there are some cautions that I need to do ?
Here is part of RouteProvider
foreach (scanDirectory(ROOT_PATH . "/routes") as $file) {
$subCollection = new RouteCollection();
$filepath = ROOT_PATH . "/routes/" . $file;
$routes = Yaml::parseFile($filepath);
$prefix = "api";
if (array_key_exists("prefix", $routes)){
$prefix = $routes["prefix"];
unset($routes["prefix"]);
}
foreach ($routes as $name => $route) {
$parameters = (new RouteParser($route))->parse();
$subCollection->add(
$name,
new Route(...$parameters)
);
}
$subCollection->addPrefix($prefix);
$subCollection->addOptions([
"trailing_slash_on_root" => false
]);
$collection->addCollection($subCollection);
}

I poked around a bit in the router component. The trailing_slash_on_root functionality is implemented as part of the loader process. So I think you need to set it in your routes file. You did not provide an example of what your admin route files look like so I'm not positive. Normally I would expect to see only a master routes file loaded which in turn would load individual sets of routes such as your admin routes.
However, using your posted code as an example, we can implement the same process that trailing_slash_on_root uses. Basically we explicitly drop the trailing slash for the dashboard route after all the processing takes place. Here is a complete standalone working example taken mostly from the routing component docs:
$rootCollection = new RouteCollection();
$adminCollection = new RouteCollection();
$route = new Route('/users',['_controller' => 'admin_users_controller']);
$adminCollection->add('admin_users',$route);
$route = new Route('/',['_controller' => 'admin_dashboard_controller']);
$adminCollection->add('admin_dashboard',$route);
$adminCollection->addPrefix('/admin'); # This actually adds the prefix
# *** Explicitly tweak the processed dashboard route ***
$route = $adminCollection->get('admin_dashboard');
$route->setPath('/admin');
$rootCollection->addCollection($adminCollection);
$context = new RequestContext('/');
// Routing can match routes with incoming requests
$matcher = new UrlMatcher($rootCollection, $context);
$parameters = $matcher->match('/admin/users');
var_dump($parameters);
$parameters = $matcher->match('/admin');
var_dump($parameters);

Related

Symfony3 Hook Up Annotation Routes

I'm writing my own PHP framework built on top of Symfony components as a learning exercise. I followed the tutorial found at http://symfony.com/doc/current/create_framework/index.html to create my framework.
I'd now like to wire up my routes against my controllers using annotations. I currently have the following code to setup the routing:
// Create the route collection
$routes = new RouteCollection();
$routes->add('home', new Route('/{slug}', [
'slug' => '',
'_controller' => 'Controllers\HomeController::index',
]));
// Create a context using the current request
$context = new RequestContext();
$context->fromRequest($request);
// Create the url matcher
$matcher = new UrlMatcher($routes, $context);
// Try to get a matching route for the request
$request->attributes->add($matcher->match($request->getPathInfo()));
I have come across the following class to load the annotations but I'm not sure how to use it:
https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php
I'd appreciate it if someone could help.
Thanks
I've finally managed to get this working. First I changed where I included the autoload.php file to the following:
use Doctrine\Common\Annotations\AnnotationRegistry;
$loader = require __DIR__ . '/../vendor/autoload.php';
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
Then I changed the routes collection bit (in the question) to:
$reader = new AnnotationReader();
$locator = new FileLocator();
$annotationLoader = new AnnotatedRouteControllerLoader($reader);
$loader = new AnnotationDirectoryLoader($locator, $annotationLoader);
$routes = $loader->load(__DIR__ . '/../Controllers'); // Path to the app's controllers
Here's the code for the AnnotatedRouteControllerLoader:
class AnnotatedRouteControllerLoader extends AnnotationClassLoader {
protected function configureRoute(Route $route, ReflectionClass $class, ReflectionMethod $method, $annot) {
$route->setDefault('_controller', $class->getName() . '::' . $method->getName());
}
}
This has been taken from https://github.com/sensiolabs/SensioFrameworkExtraBundle/blob/master/Routing/AnnotatedRouteControllerLoader.php. You may wish to modify it to support additional annotations.
I hope this helps.

How to cache routes when using Symfony Routing as a standalone?

I'm using the Symfony Routing components standalone, i.e. not with the Symfony framework. Here's my bare-bones code I'm playing with:
<?php
$router = new Symfony\Component\Routing\RouteCollection();
$router->add('name', new Symfony\Component\Routing\Route(/*uri*/));
// more routes added here
$context = new Symfony\Component\Routing\RequestContext();
$context->setMethod(/*method*/);
$matcher = new Symfony\Component\Routing\Matcher\UrlMatcher($router, $context);
$result = $matcher->match(/*requested path*/);
Is there a way to cache the routes, so I don't need to run all the add() calls on every page load? (See for example FastRoute.) I believe there is caching when using the full Symfony framework, can that be implemented easily here?
The Symfony Routing Component documentation contains an example of how to easily enable the cache: The all-in-one Router
Basically your example can be reworked like the following:
// RouteProvider.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('name', new Route(/*uri*/));
// more routes added here
return $collection;
// Router.php
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\RequestContext
use Symfony\Component\Routing\Loader\PhpFileLoader;
$context = new RequestContext();
$context->setMethod(/*method*/);
$locator = new FileLocator(array(__DIR__));
$router = new Router(
new PhpFileLoader($locator),
'RouteProvider.php',
array('cache_dir' => __DIR__.'/cache'), // must be writeable
$context
);
$result = $router->match(/*requested path*/);

How to bind route with controller with Symfony2 Routing component

I want to use a standalone Symfony2 Routing component in my small site. I've created this according to documentation and some examples:
$request = Request::createFromGlobals();
$routeTest = new Symfony\Component\Routing\Route('/route-test', array('controller' => 'test'));
$routes = new Symfony\Component\Routing\RouteCollection();
$routes->add('test', $routeTest);
$context = new Symfony\Component\Routing\RequestContext();
$context->fromRequest($request);
$matcher = new Symfony\Component\Routing\Matcher\UrlMatcher($routes, $context);
$matcher->match($request->getPathInfo());
I don't understand how I should call my controller test, that I've passed to the Route constructor. As a result I want to get something like Silex Route matching:
$app->get('/hello/{name}', function($name) use($app) {
return 'Hello '.$app->escape($name);
});
And sorry for my english...
$matcher->match() returns[1] the attributes of the matching route[2] (including a special _route attribute containing the route name [3]).
The controller default is included in the attributes too, so you can easily access it and then use something like call_user_func to call the controller:
// ...
$attributes = $match->match($request->getPathInfo());
$controllerResult = call_user_func($attributes['controller']);

Symfony2 create a URL by replacing a router pattern vaiable from the current URL (How to edit current URL?)

I want to replace the emailtemplates router to contactpoints from the following URL
http://localhost/app_dev.php/config/workflow/configset-72/partner_site/emailtemplates
My routing pattern is: /config/workflow/configset-{configSetId}/partner_site/{steps}
I am currently using following code to generate a URL. But in this way I have to redefine the confgSetId, so, is there any other way to replace only "steps" (emailtemplates with contactpoints) route pattern?
$routes = new RouteCollection();
$routes->add('deploy_config', new Route('/config/workflow/configset-{configSetId}/partner_site/{steps}'));
$context = new RequestContext();
$context->fromRequest($this->getRequest());
$urlGenerator = new UrlGenerator($routes, $context);
$r = $urlGenerator->generate('deploy_config', array('configSetId' => $this->getConfigSet()->getId(), 'steps' => 'contactpoints'));
If I ask it simply I want to know:
How to edit current URL inside a controller in Symfony2?
You could redirect to the new url like this assuming you are inside an action:
return $this->redirect($r);
Here is the relating part in the documentation: http://symfony.com/doc/current/book/controller.html#redirecting
To get the current route use this:
$currentRoute = $request->attributes->get('_route');
$currentUrl = $this->get('router')->generate($currentRoute, array(), true);
Therefore you need to implement the Request in the action:
use Symfony\Component\HttpFoundation\Request;
...
function someAction(Request $request){

How check if action exsist in Controller Plugin preDispatch

I have two modules (default and mobile) the module mobile is a rewrite the default portal in jquery mobile but with much less controllers and actions!
I thought of write a controller plugin that check if controller and action exist in module mobile, if not I would like overwrite the module mobile to default.
I try this:
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$dispatcher = Zend_Controller_Front::getInstance()->getDispatcher();
if ($request->getModuleName() == 'mobile') {
if (!$dispatcher->isDispatchable($request)) {
// Controller or action not exists
$request->setModuleName('default');
}
}
return $request;
}
but $dispatcher->isDispatchable($request) return always true though the action not exist! :S
and i receive "Action foo does not exist and was not trapped in __call()"
How can I do?
Thanks
Have you ever wondered how to check if a controller/action exist in zend FM from any side of app ? Here is the code
$front = Zend_Controller_Front::getInstance();
$dispatcher = $front->getDispatcher();
$test = new Zend_Controller_Request_Http();
$test->setParams(array(
'action' => 'index',
'controller' => 'content',
)
);
if($dispatcher->isDispatchable($test)) {
echo "yes-its a controller";
//$this->_forward('about-us', 'content'); // Do whatever you want
} else {
echo "NO- its not a Controller";
}
EDIT
Check like this way
$classMethods = get_class_methods($className);
if(!in_array("__call", $classMethods) &&
!in_array($this->getActionMethod($request), $classMethods))
return false;
and also please see detail link
I would suggest you make a static or dynamic routes either via config resource manager, bootstrap or via front controller plugin:
Example of defining static routes in Bootstrap.php:
public function _initRoutes()
{
$front = Zend_Controller_Front::getInstance();
$router = $front->getRouter(); // default Zend MVC routing will be preserved
// create first route that will point from nonexistent action in mobile module to existing action in default module
$route = new Zend_Controller_Router_Route_Static(
'mobile/some-controller/some-action', // specify url to controller and action that dont exist in "mobile" module
array(
'module' => 'default', // redirect to "default" module
'controller' => 'some-controller',
'action' => 'some-action', // this action exists in "some-controller" in "default" module
)
);
$router->addRoute('mobile-redirect-1', $route); // first param is the name of route, not url, this allows you to override existing routes like default route
// repeat process for another route
}
This would effectively route request for /mobile/some-controller/some-action to /default/some-controller/some-action
some-controller and some-action should be replaced with proper controller and action names.
I was using static routing which is ok if you route to exact urls, but since most applications use additional params in url for controller actions to use, it is better to use dynamic routes.
In above example simply change route creation class to Zend_Controller_Router_Route and route url to "mobile/some-controller/some-action/*" and every request will be routed dynamically like in example:
/mobile/some-contoller/some-action/param1/55/param2/66
will point to
/default/some-controller/some-action/param1/55/param2/66
For more info about routing in ZF1 check this link

Categories