Symfony2 path to twig file - php

I have problem with path to twig file. I would like to shorten the path. Look at my example:
class DefaultController extends Controller
{
public function indexAction($name)
{
return $this->render('CatalogFrontendBundle:Default:index.html.twig', array('name' => $name));
}
}
Of course it will work, but look at the path to twig file it's so long.. I now that I could use annotation like this #Template() but I don't want to. Is there any other way to use default twig file to render my page?
Default twig file - I understand as the file whose name is the same like name of action method. So if name of action is indexAction the name of twig file should be index.html.twig.

If you'd like a Symfony experience that is more geared toward Rapid Application Development (RAD) take a look at the KNP RAD bundle. This gives you a lot of convention-based shortcuts to work with, including automatic template resolution. See here: http://rad.knplabs.com/#template

I can't found better solution to my problem. So I decide to use #Template, Mark have right it will be cached. And I think that better option is to use those annotation than write my own method or put all of the path in the render method.
So the answer is below:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class DefaultController extends Controller
{
/**
*
* #Template()
*/
public function indexAction($name)
{
return array('name' => $name);
}
}

Related

How do you access a template from inside a Symfony Bundle that is located in "<BundleLocation>/Ressources/views"?

I'm creating an application for a Symfony web project that I have made previously as a portable bundle. I've created a "media.html.twig" template and want it to be called by the render() helperfunction.
I've activated the bundle in /config/bundles.php with
App\OM\MediaManagerBundle\OMMediaManagerBundle::class => ['all' => true],
and followed the instructions on this site https://symfony.com/doc/current/bundles.html for setting up the Symfony-Bundle folder structure and the required files.
I've prefixed the path with "#MediaManager" as told by this site https://symfony.com/doc/4.1/bundles/best_practices.html#resources.
This is my Controller class:
<?php
namespace App\OM\MediaManagerBundle\Controller;
use App\OM\MediaManagerBundle\Form\ImageType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class MediaManagerController extends AbstractController
{
const TEMPLATE = "#MediaManager/MediaManager/media.html.twig";
/**
* #Route("/media/", name="om_mediamanager_media")
* */
public function index(Request $request)
{
$form = $this->createForm(ImageType::class);
return $this->render(self::TEMPLATE,
[
'form' => $form->createView(),
]);
}
}
This is how my folder structure looks like inside src:
https://i.imgur.com/9UomIEK.png
I hoped my form would be displayed but instead I got an error message saying:
There are no registered paths for namespace "MediaManager".
I know that it starts looking from the /templates folder so I tried setting the path to "../src/OM/MediaManagerBundle/Resources/views/MediaManager/media.html.twig" but got
Looks like you try to load a template outside configured directories (../src/OM/MediaManagerBundle/Resources/views/MediaManager/media.html.twig).
The code does work when I move the "media.html.twig" template inside the /templates folder but I expect it to ruin the portability of the bundle and therefor don't want to do that.
How do I access this template from inside my OMMediaManagerController class?
Am I even using Symfony Bundles the right way?
Is there any more documentation about Bundles in general? I found the ones provided by the official site to be very lacking.

Adding a new config file in Laravel 5 not working

I want a new config file in my Laravel 5 app to store all my constants in. After looking around the net I found the recommended solution seems to be to create a new config file that returns an array of key value pairs and then use that. So I created the following file:
<?php
// config/constants.php
return [
'SITE_NAME' => 'Site Name',
'SITE_EMAIL' => 'email#site.com',
'ADMIN_EMAIL' => 'admin#site.com'
];
Then in one of my controllers I try to access one of these values like so:
echo Config::get('constants.ADMIN_EMAIL');
I just get the following error:
FatalErrorException in WelcomeController.php line 46:
Class 'App\Http\Controllers\Config' not found
Do I have to do something else to get it to work?
In Laravel 5, to avoid this kind of headache, you can use the config helper function to get a config item, like this :
config('constants.ADMIN_EMAIL')
Nice and easy ;)
The Config class is an alias in the global namespace. To reference it from inside the controller (which is in the App\Http\Controllers namespace) you have to prepend it with a backslash:
echo \Config::get('constants.ADMIN_EMAIL');
Or add a use statement above the controller class:
use Config;
class MyController extends Controller {
As an alternative you might also want to use dependency injection to access the config. That would look somewhat like this:
class MyController extends Controller {
public function __construct(Illuminate\Config\Repository $config){
$this->config = $config;
}
public function index(){
echo $this->config->get('constants.ADMIN_EMAIL');
}
}
As #Bernig suggests you can also simply use the new config() helper function:
echo config('constants.ADMIN_EMAIL');
I met the same issue today, and I find an elegant solution:
add the config/your_new_config.php to ConfigServiceProvider, like this:
/**
* Overwrite any vendor / package configuration.
*
* This service provider is intended to provide a convenient location for you
* to overwrite any "vendor" or package configuration that you may want to
* modify before the application handles the incoming request / command.
*
* #return void
*/
public function register()
{
config([
'config/your_new_config.php', // add your new config file here!
]);
}
The reason is well explained in the function's comments

Symfony 2 dynamically load routes

I'm going to create a modules system in my Symfony 2 app. Each module will be a bundle.
I don't know how to I can dynamically (in my service code) load routes from file (eg. AcmeSomeModuleBundle/Resources/config/routing.yml) and apply them with some prefix (or host). Like it's done by embedding code below in app/config/routing.yml:
berg_applications:
resource: "#AcmeSomeModuleBundle/Resources/config/routing.yml"
host: foobar.com
Any solutions?
You need custom route loader IMO: http://symfony.com/doc/current/cookbook/routing/custom_route_loader.html
For one project, I had to build route loader which loaded routes by fetching them from remote URL via CURL and it worked perfectly.
Documentation is very clear and it's silly easy to build one yourself when you look at the example. Basically, key things are:
"type" when you're defining a route resource. You should make your custom type so that your route loader recognizes it and takes it for processing.
::load() method.
If you have any concrete problems you stumble upon don't hesitate to post question in comment. Basically, your RouteLoader will receive "resource" in it's load method and should do whatever it needs to do with it to add new Route to Router.
If you do a true bundle approach for each module, then the easiest way to accomplish what your trying to do is use the JMS Security-Extra bundle with attribute-based routing.
To your composer.json file, add this:
"require": {
...
"jms/security-extra-bundle": "1.5.*",
Update your composer file with
php composer.phar update
Then in your BundleName/Resources/config/routing.yml file do this:
some_name:
type: annotation
resource: "#SomeBundle/Controller"
Finally, for each action in your controller, decorate it with #Route attributes:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
/**
* #Route("/SomeBundle/SomeController")
*/
class SomeController extends Controller {
/**
* #Route("someAction", name="myAction")
* #Method("GET") OR
* #Method({"GET", "POST"})
*/
public function someAction() {
}
}
Some of the other attributes in the JMS bundle make things really nice as well. For example, I use the #Template attribute on my actions quite a bit. This means that I no longer have to do:
public function recentListAction() {
...
return $this->render(
'AcmeArticleBundle:Article:recentList.html.twig',
array('articles' => $articles)
);
}
I can simply do:
/**
* #Route("/Articles/List")
* #Template()
*/
public function recentListAction() {
...
return array('articles' => $articles);
}
And as long as I have a Resources/views/ControllerName/recentList.html.twig file, everything will be weaved together for me automatically.

Global partial behaviour in Controllers

I have a base controller for most of the controllers in my app like so:
class BaseController extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
/**
*
* #Route("/")
*/
public function indexAction($partial = false)
{
$this->partial = $partial;
$this->currentAction = 'index';
return $this->r();
}
}
This is accompanied by a pack of templates that can be either full html pages with a layout or just the content. This is done by a line in the templates:
{% extends this.partial ? "SomeProject:_base:partial.html.twig" : "SomeProject::layout.html.twig" %}
(where this is a the controller reference).
These templates can then be rendered in other controllers without the duplication of layout via.
{% render 'SomeProject:SomeController:index' with { "partial":true } %}
My problem with this approach is:
Every action that needs to be partial controller must have a $partial argument. Since most actions have the potential to be partial, all the controllers must be sprinkled with it.
Every potentially partial action must have the $this->partial = $partial line, which can be easily forgotten.
It there a cleaner way by using some Symfony or Twig magic (overriding the render tag etc. ). For getting rid of the above problems?
I have overridden the render method in most of my controllers in order to inject some standard variables into my templates. It seems to work well but all it will do for you will be to make rendering a template from a controller a bit easier since you won't have to explicitly pass $this to the template.
I am not so sure passing a reference to the controller is such a good idea. For your example at least, Just passing partial would seem to be all you need.
Do you really need to give your templates this partial capability? Seems easier to just have two templates (one for the page and one for the partial). But perhaps your use case requires it.
After some research and digging around in the internals I've come up with an elegant solution.
The answer is to build an Event Listener (covered in Symfony2 Docs). More precisely: a Controller Listener with the meat of the class looking like below. This allows for transparent handling of partial without any changes made to the Controller code.
class ControllerListener
{
/**
*
* #param BaseController $ctrl
* #param Request $request
* #return BaseController
*/
public function injectPartial($ctrl,Request $request)
{
if ($ctrl instanceof BaseController)
{
if ($request->get("partial",false))
$ctrl->setPartial($request->get ("partial"));
}
return $ctrl;
}
public function onKernelController(FilterControllerEvent $event)
{
$ctrl = $event->getController();
$ctrl[0] = $this->injectPartial($ctrl[0], $event->getRequest());
$event->setController($ctrl);
}
}

PHP routing system

I'm trying to create a routing system based on annotations (something like on Recess Framework).
<?php
class MyController extends ActionController {
/** !Route GET /hello/$firstname/$lastname **/
public function helloAction($firstname, $lastname) {
echo('Hello '.$firstname.' '.$lastname);
}
}
?>
If I go to http://domain.com/hello/James/Bond I get
Hello James Bond
So I have two questions:
1) Is it a good idea? Pros and cons vs centralized routing system (like Zend Framework). Maybe I don't see problems that my arise later with this routing technique.
2) How to check for duplicate routes if there is regexp in routes
<?php
class MyController extends ActionController {
/**
*!Route GET /test/$id = {
* id: [a-z0-9]
*}
**/
public function testAction($id) {
echo($id);
}
/**
*!Route GET /test/$id = {
* id: [0-9a-z]
*}
**/
public function otherTestAction($id) {
echo($id);
}
}
?>
I get two routes: /test/[a-z0-9]/ and /test/[0-9a-z]/ and if i go to http://domain.com/test/a12/ both routes are valid.
Thanks :)
Try using the Java annotation format which should be much easier to parse uniformly.
It looks something like this:
<?php
class MyController extends ActionController {
/**
#Route(get=/hello/$firstname/$lastname)
#OtherVal(var1=2,var2=foo)
#OtherVal2
**/
public function helloAction($firstname, $lastname) {
echo('Hello '.$firstname.' '.$lastname);
}
}
?>
And parse your annotation out with the following regex:
#(?P<annotation>[A-Za-z0-9_]*)(\((?P<params>[^\)]*))?
And of course cache these where possible to avoid repeated parsing.
Cons:
It may be difficult to keep an overview of URL mappings of all methods in your server.
To change a URL you have to change the source code, mapping is not separated from the application.
If the method signature and mapping are always as related as the example you might use reflection to extract the mapping where helloAction is picked up as /hello and each method argument is a subdirectory of this in the order as they're defined.
Then the annotation wouldn't need to duplicate the URL, only the fact that the method is an endpoint, something like this:
<?php
class MyController extends ActionController {
/** !endpoint(method=GET) **/
public function helloAction($firstname, $lastname) {
echo('Hello '.$firstname.' '.$lastname);
}
}
I think it's a good idea / Decoupling code and entry point seems pretty much used everywhere
Usually you don't check for it: the first one that matches wins.
Doing so is a great idea, as long as you cache the compiled routes in production. There is a cost associated to parsing your files when routing so you want to avoid that when not developing.
As for checking for duplicates, don't check by comparing the declaration. Simply check when routing. If two rules matches, throw a DuplicateRouteException. So, when routing http://domain.com/test/a12/, you'll see that both routes are valid and throw the exception.

Categories