I'm trying to achieve this
http://symfony.com/doc/current/cookbook/routing/custom_route_loader.html#more-advanced-loaders
I need the bundle routing to automatically activate itself when the bundle is registered
so I created this file into the path
src/Gabriel\AdminPanelBundle\Routing\AdvancedLoader.php
with the content
<?php
//namespace Acme\DemoBundle\Routing;
namespace Gabriel\AdminPanelBundle\Routing;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\RouteCollection;
class AdvancedLoader extends Loader
{
public function load($resource, $type = null)
{
$collection = new RouteCollection();
$resource = '#GabrielAdminPanelBundle/Resources/config/import_routing.yml';
$type = 'yaml';
$importedRoutes = $this->import($resource, $type);
$collection->addCollection($importedRoutes);
return $collection;
}
public function supports($resource, $type = null)
{
return $type === 'advanced_extra';
}
}
I copied this configuration
gabriel_admin_panel:
resource: "#GabrielAdminPanelBundle/Controller/"
type: annotation
prefix: /superuser
from
/app/config/routing.yml
and pasted it into my own configuration file
/src/Gabriel/AdminPanelBundle/Resources/config/import_routing.yml
The problem:
Symfony2 completely ignores my AdvancedLoader.php file, I can put any
syntax error in it and the site won't even throw an error, also the
router:debug doesn't show the routes that are defined inside of the
bundle unless I move the configuration back into its original router.yml file.
PS: clearing the cache doesn't change anything
Edit: when I add the service and the resource, this error appears
FileLoaderImportCircularReferenceException: Circular reference
detected in "/app/config/routing_dev.yml"
("/app/config/routing_dev.yml" > "/app/config/routing.yml" > "." >
"#GabrielAdminPanelBundle/Controller/" >
"/app/config/routing_dev.yml").
Looks like you could have missed some steps in the process.
First one: did you define the service?
services:
gabriel.routing_loader:
class: Gabriel\AdminPanelBundle\Routing\AdvancedLoader
tags:
- { name: routing.loader }
Note the tag. As the documentation says:
Notice the tag routing.loader. All services with this tag will be
marked as potential route loaders and added as specialized routers to
the DelegatingLoader.
Second but very important because, as the documentation says, if you didn't add this lines your routing loader wouldn't be called:
# app/config/routing.yml
Gabriel_Extra:
resource: .
type: advanced_extra
The important part here is the type key. Its value should be "advanced_extra" in your case. This is the type which your AdvancedLoader supports and this will make sure its load() method gets called. The resource key is insignificant for the AdvancedLoader, so it is set to ".".
I think it will get loaded now.
Related
I am trying to learn the Symfony framework and struggling with it. The instructions are not very helpful or assume I know a lot more than I know. I am just trying to create a single web page with proper route and controller. I have been googling to find answers and made some progress but no luck yet. Right now I just have the standard install of Symfony with just default bundles etc. I created a project called "gtest3" and chose PHP for it...
I am not sure where I put the new route in (what file) or maybe it needs to be put in more than one file?
I found the "routing.yml" file which seems that is where I need to put it...
here is what is in there right now:
gtest3:
resource: "#gtest3Bundle/Resources/config/routing.php"
prefix: /
app:
resource: "#AppBundle/Controller/"
type: annotation
I am guessing I need to add something to this and put the location/filename of the controller? I have tried doing this a few ways and just get errors.
There is also the "routing.php" file that is referenced in the above code. I am not sure if this is the "controller" or if it is an additional piece of the "route". Here is the code from that file:
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('gtest3_homepage', new Route('/hello/{name}', array(
'_controller' => 'gtest3Bundle:Default:index',
)));
return $collection;
I am not sure what if anything I would add here.
Finally - there is the "DefaultConroller.php" file I found as well which may also be the controller. I dont think I need to include the code of that file here.
So - all I am trying to do is create a route of maybe "/gman" and then have the controller just echo something on the page. Super basic stuff. And I cannot figure out how to get this going.
Can anyone help with this? Thanks so much...
You can define your routes in three ways, either by using yml files, xml files, or by using a php file. This is documented behaviour.
You are from the looks of your routing.yml trying to set up a php version. I would not recommend it, and instead use configuration over coding the routing.
The annotation example would look like:
Adding a controller:
namespace Gtest3Bundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class Gtest3Bundle extends Controller
{
/**
* #Route("/hello/{name}")
* #Template()
*/
public function indexAction($name)
{
return array('name' => $name);
}
}
And add in your app/config/routing.yml:
gtest3:
resource: "#Gtest3Rights/Controller/"
type: annotation
prefix: /what-ever-you-want
You can check what kind of routes you have defined by using:
./app/console router:debug
If it doesn't appear hear, you have something misconfigured.
Please be advised that your bundle names breaks the convention of how bundles should be named in the symfony2 context.
It is advised to use NamespaceProjectBundle. This also documented. If you are stuck, try generating a bundle via the ./app/console generate:bundle. This way you can create a whole symfony2 structure which should show the default page hello/foo page just fine.
If it doesn't seem to run at all, make sure you have registered your bundle at the in the app/AppKernel.php file in the registerBundles() method.
To configure routes you can use yml, php or xml file. You can specify it in app/config/config.yml
framework:
router:
resource: "%kernel.root_dir%/config/routing.yml"
It's where you can check which file is used now.
There are some manners to store the routes.
app:
resource: "#AppBundle/Controller/"
type: annotation
This is how you use routes by writing annotations above your actions (eg indexAction) inside your controller class. Read more.
Another common approach is to build one or more .yml files inside each Bundle.
in app/config/routing.yml you should write this:
app:
resource: "#AppBundle/Resources/config/routing.yml"
prefix: /
Then you need to create the file (and directories if necessary) src/AppBundle/Resources/config/routing.yml and add the following content:
app_homepage:
path: /
defaults: { _controller: AppBundle:Default:index }
It will then try to find the DefaultController and fires the indexAction in the AppBundle (src/AppBundle/Controller/DefaultController.php).
To debug your routes simply type in your console from your projects directory:
app/console router:debug
I need my website to be accessible with 2 different URL, for example:
/blog => Homepage
/blog/article/1 => Article page (etc.)
/abcde/blog => Homepage
/abcde/blog/article/1 => Article page (etc.)
When "abcde" is in my URL, I need to have the same controller/action running, but being able to get this "abcde" value (a few changes will result from this parameter).
I have found many similar questions but never exactly what I am looking for:
I am not looking for a language/translation routing bundle
"abcde" parameter must be optional
My bundle has several controllers and actions, I don't want to write 2 routes for each. (Neither do I want to have to update anything each time I add a new controller/action.)
Initial app/config/routing.yml
mywebsite_blog:
resource: "#MywebsiteBlogBundle/Resources/config/routing.yml"
prefix: /blog/
If I simply add a second route, as resource is the same, it overwrites the first one.
I tried to add an optional parameter on the initial route:
mywebsite_blog:
resource: "#MywebsiteBlogBundle/Resources/config/routing.yml"
prefix: /{_label}/blog/
defaults: {_label: mylabel}
requirements:
_label: .*
That way I can access /abcde/blog, //blog, but not /blog (404).
Not finding how to resolve my issue simply with using routing.yml (if it is possible I will be happy to learn how and stop there), I read How to Create a custom Route Loader on Symfony doc. I am not sure I'm going to the right direction but I tried it and manage to do a few things, still not exactly what I want.
LabelLoader.php
class LabelLoader extends Loader {
private $loaded = false;
public function load($resource, $type = null) {
if (true === $this->loaded) {
throw new \RuntimeException('Do not add the "label" loader twice');
}
$routes = new RouteCollection();
// Prepare a new route (this is were I tried to play with it)
$path = '/{_label}/blog/';
$defaults = array(
'_controller' => 'MywebsiteBlogBundle:Index:home',
);
$route = new Route($path, $defaults);
// Add the new route to the route collection
$routeName = 'labelRoute';
$routes->add($routeName, $route);
$this->loaded = true;
return $routes;
}
public function supports($resource, $type = null) {
return 'label' === $type;
}
}
That way, /blog and /abcde/blog works the way I want to. I can access the _label variable in my _init() function (I'm using listeners and InitializableControllerInterface, in case it is important to know here), checking if empty or not, etc.
But obviously it works for only one controller/action (Index:home). I would like to change this so that it can works for my whole MywebsiteBlogBundle.
In my custom Loader, I wanted to test something like:
$routes = new RouteCollection();
// Hypotetical function returning all the routes I want to have twice.
// Methods getPath() and getController() called below are hypotetical too.
$mywebsiteBlogRoutes = getExisitingMywebsiteBlogBundleRoutes();
foreach($mywebsiteBlogRoutes as $blogRoute) {
// Prepare a new route
$path = '/{_label}/blog/'.$blogRoute->getPath();
$defaults = array(
'_controller' => $blogRoute->getController(),
);
$route = new Route($path, $defaults);
// Add the new route to the route collection
$routeName = 'labelRoute';
$routes->add($routeName, $route);
}
But:
It does not seem very clean (but if that's the only way I find to do it for now, I'll try it)
I tried to get all my routes reading this answer and as soon as I try $collection = $router->getRouteCollection(); I have a "Circular reference detected" error that I don't manage to fix. I'm trying to understand what's happening here. Edit: I fixed it. Still, I don't think it's a very clean solution.
Should I go back to routing.yml or is it possible to do something here with the custom Routing Loader?
You should try this in your routing.yml:
mywebsite_blog:
resource: "#MywebsiteBlogBundle/Resources/config/routing.yml"
prefix: /{_label}blog/
defaults: {_label: mylabel/}
requirements:
_label: ([\w\d]+/)?
Results:
app/console router:match /mylabel/blog/ # OK
app/console router:match /blog/ # OK
I am storing a string in database that I want to access from various places in my application. I figure out that the best solution will be create a function that is taking that string from database and register it as a service.
Function:
public function shopUrlAction()
{
return new Response($this->getDoctrine()->getRepository('AppBundle:Settings')->find(1)->getName());
}
service.yml
services:
app.default_controller:
class: AppBundle\Controller\DefaultController
output in other controller:
$return['base_url'] = $this->forward('app.default_controller:shopUrlAction');
Unfortunately I am constantly getting
CRITICAL - Uncaught PHP Exception
Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException:
"You have requested a non-existent service "app.default_controller"."
at /app/bootstrap.php.cache line
2099 Context:
{"exception":"Object(Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException)"}
I've cleared cache.
As I see from your question you have service.yml instead of services.yml (in plural form).
You should include your service.yml in main config.yml in imports section or use standard path to it (AppBundle/Resources/config/services.yml)
I found the following undocumented feature in the documentation chapter Miscellaneous Configuration:
A resource file can be one of many types. PHP, XML, YAML, INI, and
closure resources are all supported by the imports directive.
The class in charge of loading these resources seems to be:
Symfony\Component\DependencyInjection\Loader\ClosureLoader ( APIdoc , Code )
In my understanding according to the documentation it should be possible to import services and/or parameters using a PHP closure inside a configuration file.
non-working example:
# app/config/config.yml
imports:
# closure? something like eval( <multiline-string> ) ?
- { resource: >
function($containerBuilder) {
return array(
'parameters' => array(
'upload_tmp_dir' => ini_get('upload_tmp_dir')
)
);
}
}
I can't find an example showing how to use this loader in a PHP,XML,YAML configuration file with the imports directive.
Is there any example available using imports?
Is the ClosureLoader compatible with the YAML format?
There is an example available that shows how to use the Symfony\Component\Routing\Loader\ClosureLoader to load a route collection ( Routes as Closures).
But it does not show how to use the closure with the imports directive as the documentation states.
The idea behind my question is that it might be possible to access PHP's configuration values or some class constants in config.yml without creating an extension/compiler-pass or importing a file in a different configuration format. ( i.e. accessing constants is not possible in YAML format ).
This would open up some great possibilities ...
... like adding a closure that imports a parameter for the upload_tmp_dir path in php.ini that can instantly be used in LiipMonitorBundle's liip_monitor.checks.writable_directory array afterwards.
I've been able to use that loader in AppKernel:
class AppKernel extends \Symfony\Component\HttpKernel\Kernel
{
// ...
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load($this->getRootDir() . '/config/config_' . $this->getEnvironment() . '.yml');
// Load config from the closure
$loader->load(function () {
// ..
});
}
}
The problem is that this is not taken into account when recomputing the cache (i.e. if you change your closure, the cache will not change).
to use an ini file you can juste call the file as ressource:
- { resource: parameters.ini }
theres 4 type of closure with the symfony dependcyInjection Component (Symfony 2.3). there are IniFileLoader, PhpFileLoader, XmlFileLoader and YamlFileLoader.
To use a personal closure you should create you personal closure that extends from
Symfony\Component\DependencyInjection\Loader\FileLoader
This a good exemple : http://www.adayinthelifeof.nl/2013/01/30/custom-symfony2-config-loader/
I need to send a parameter to all my views (twig template) with Symfony2.
Each view extend a special view : base.html.twig, so I think I just need to send this parameter to this base view.
But how can I do that?
Here is the way I get this parameter in my php file:
$svn = File('.svn/entries');
$svnrev = $svn[3];
Which represents the number of current revision.
Is there a way to retrieve this variable in a .yml?
You should register a global variable using a twig extension.
The class
// src/Acme/YourBundle/Twig/SvnExtension.php
namespace Acme\YourBundle\Twig;
class SvnExtension extends \Twig_Extension
{
public function getGlobals()
{
// you might need to adapt the file path
$svn = File('.svn/entries');
return array(
'svn_rev' => $svn[3]
);
}
public function getName()
{
return 'svn_extension';
}
}
The service definition
# i.e. app/config/config.yml
services:
acme.twig.svn_extension:
class: Acme\DemoBundle\Twig\SvnExtension
tags:
- { name: twig.extension }
Accessing the variable
Now your variable is accessible in every twig template as usual:
{{ svn_rev }}
Further improvements would include:
getting the filename from a parameter or environment variable (if the svn repo is stored elsewhere)
caching the file using apc(u) / memcache / whatever for performance
add more svn info and turn the variable into an array itself ( svn.rev , svn.state , ... )
...