Repeat code in Twig and pass parameter from controller all the time - symfony

I have this piece of code in several Twig templates:
{% if is_granted('IS_AUTHENTICATED_REMEMBERED') == false %}
{{ render(controller('FOSUserBundle:Security:login')) }}
{% include 'FOSMapyetBundle:Registration:register.html.twig' with {form:form} %}
{% endif %}
As you may see it receive the parameter form from controllers but here comes the problem in every site I like to use the code lets said in templates I need to pass the parameter form to the view. By now it's only on 15 templates but this make me think, what about if tomorrow should be 20 or 60 or even 100 templates? So what is the best solution to handle this? Twig Extensions? Symfony Services? Any advice or help on this one?

A kernel event listener may work for this. Render your mini template inside the listener and then "inject" it into the final response, similar to how the debug toolbar works:
https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php#L106
Note that this is the approach you may want if you want to modify all pages universally. You can apply checks inside the listener to ignore ajax requests, redirection, and the like. You could also probably implement a simple map that knows which routes should have the response modified.
If you want to explicitly render the template in specific pages, then I agree with Maerlyn.

When you need logic like this, your best option is to render a sub-controller, like you already do with your login form.
Create a RegistrationController in your bundle that creates the form and returns the rendered register.html.twig, then embed it like the line above the include.

You can use Response event, and inject your peace on code at the end of the template.
http://api.symfony.com/2.4/Symfony/Component/HttpKernel/KernelEvents.html

Related

Render forms in sonata admin listview

I my application i have a listview representing News-items. On each row end i want to add a plus-sign button and by clicking it, a little inline form should pop up in a bubble (via bootstrap dropdown). To render this form, my actually approach is to render this via the render(controller(...)) mechanism in the field template.
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
{{ render(controller(...)) }}
{% endblock %}
The render controller call renders a form according to the official symfony documentation ...
The rendered form sends it data back to the commentAction in the same controller.
In theory it works fine, but practical its slow as hell. My site runs in timeout after 60 seconds just because in the listview are default 64 items shown and each item calls this render controller function ...
Is there a clean way to render a little extra form not by invoking a expensive render controller call?
I think it could work with manipulating the listAction method in the controller (injecting here the extra form?) ... but this does not seem a clean solution.
Any ideas?
Thanks
Edit:
Ok, i think i found out, where the bottleneck is ... the example above was simplified. In reality i load a collection into the form for every row ... in the form you can choose, which author created the news. There are at least 500 Authors in that list ... so displaying these authors for a single new, everything is fine. But rendering the choose options 64 times is too much. Is there a way to make this faster? Some caching mechanism?
I think in this case, instead of rendering all forms at once, just load the form via AJAX once user click on the button. Another solution could be to replace collection for some autocomplete somyou dont need to render all data at once.

Sonata Admin, custom flashBag

I have a formMapper Sonata. I have add the prePersist and preUpdate function who are performing some checks before approving changes.
If it fail, I would like to display a nice error message. I have seen that there is some flags who can use but nothing really interesting. Even more, a return false does not stop the action.
Des someone know how could I manage this feature ?
Thanks a lot.
You can show a flash message in your hooks accessing the getRequest method.
public function prePersist($object) {
$this->getRequest()->getSession()->getFlashBag()->add('error', 'Error message');
}
To stop an action from inside an Admin hook, I found that the best way is to throw a ModelManagerException. This particular exception is, in fact, handled by the CRUDController.
Not too clean I have to admit, but effective.
Note: The way to access the flashbag may be a little different with respect to your SonataAdmin version.
I don't really understand what you don't get in the doc. Here it says that in your controller you need to add
$session->getFlashBag()->add('key', 'message');
And to get it in the view :
{% for message in app.session.flashbag.get('key') %}
<p>{{ message }}</p>
{% endfor %}
What you are probably searching for is a custom validator constraint.
The validator will automatically check for all validator constraints when you submit a sonata form. If there's an error, the entity is not persisted and a nice message is displayed (depending on what constraint validation message you set).

Twig Post request when rendering a controller

I use the twig render function to render a controller :
{% render(controller("MyAppDemoBundle:Default:footer")) %}
It seems that the rendered controller didn't receive the request as the main controller.
So the request method is always set to "GET", even if the main request is POST.
Is it normal ?
You can forward the request like this:
{% render(controller("MyAppDemoBundle:Default:footer", {request: app.request})) %}
Remember that you need to pass the request as a variable of your controller action in order for this to work.
Yes, this is completely normal. The render function from twig initiates another request which is completely independent from the main request. So you will also not be able to access any post or get variables in this sub-request.

Call result controller/view in anoter view

I try to call controller/view in another view. I have a homepage Default:index using a block view of my controller Event and I want to put this block.html.twig in my folder of my controller. In my controller Event, I want an action block, in this way I keep the logic of events, in my controller Event.
How can I do for in Event:index.html.twig call my controller/view ?
I saw the helper render, but I think it makes many requests to include the result.
You seem to be on the right track. When calling sub-renders from a view, you have several options, as detailed here: http://symfony.com/doc/current/book/templating.html
The one I think you're looking for is:
{{ render(controller('YourBundle:Event:index')) }}
which will call the controller action and relevant view.
As an aside, if you want a sub-render, but require no controller logic, use
{{ include('YourBundle:Event:index.html.twig') }}
as this seems to be a lot more lightweight.
If you need to use any of these with parameters, normal format is used.

Symfony2 main request object in partial controller

I use twig render and in partial controller I want get main controller name, but
$this->get('request')->get('_template')->get('controller')
return partial.
How I can get main request object in partial controller?
Now I use:
{% render 'FooBundle::Controller:action' with {'controller': getControllerName() } %}
with custom Twig function.
Maybe there are more elegant ways to do this?
You will need to explicitly pass this value in the render tag.
{% render 'AcmeBundle:Demo:something' with { 'via': app.request.attributes.get('_controller') } %}
What you probably need is the include tag which includes another template.
{% include 'FooBundle::partial.html.twig' %}
Template inclusion is for simple cases, where you just need things like partials.
The render tag is for invoking controllers, doing more complex things, or if your template can't access the variables needed to display easily. Doing that the kernel creates a new request as if it came as a standalone request. In fact using ESI it could be a standalone request to that controller.See here for details. Because of that, you can't get the main request object, there is probably no main request object because you use ESI or because you create a route for that controller and invoke it via AJAX or whatever. Relying on the the information that your controller is invoked via a sub-request is not supported by the framework as I know, and I think it's intentional. You have to pass all information in query parameters.
Your example isn't correct, it would be like this:
{% render 'FooBundle:MyController:actionName' ... %}
You have to have a FooBundle\Controller\MyControllerController class like this for tihs to work.
namespace FooBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class MyControllerController extends Controller {
public function actionNameAction() {
...
}
}

Resources