Symfony Twig: send a set parameter with a form submit request - symfony

I have a form in my twig, i want that by clicking on its submit button, another parameter (that i've set in the twig) get sent to the same action that handles the form :
this is the variable i've set
{% set idprof = profil.id %}
I want to send it with the submit request : ( i know this code is false)
{{ form_widget(form.id),{'idprof': idprof} }}
and the action will look like this :
public function gestProfAction(Request $request, $idprof)
{
}
I'm sorry i know this is a stupid question, but I'm still new in symfony, I couldn't find a solution by myself.

Just pass it in the form start line
{{ form_start(form, {'action': path('idprof', { 'idprof': idprof })}) }}
Remember to add the annotation or yaml for the routing
/**
* #Route("/idprof/{idprof}", name="idprof")
*/
public function gestProfAction(Request $request, $idprof)
{
}
For reference:
http://symfony.com/doc/current/forms.html#rendering-the-form
http://symfony.com/doc/current/form/action_method.html
http://symfony.com/doc/current/templating.html#linking-to-pages

Related

symfony parent template controller

Is there a standard/preferred way in Symfony to pass common variables to the base template?
There are things that are on every page, like a username in the menu that I obviously don't want to have to remember to add for every controller.
My thought was to create a controller for the template and return the data. But wondering if there is something more built in to handle this.
return $this->render(
'#secured/account/profile.html.twig',
array('userForm' => $form->createView(),
'base' => call_base_layout_controller()
);
{# templates/account/profile.html.twig #}
{% extends '#secured/base.html.twig' %}
{% block body %}
{% endblock %}
I cannot find it in the current version docs but, as far as I know you can still access a lot directly from twig through the app twig variable defined automatically by the framework for every request https://symfony.com/doc/3.4/templating/app_variable.html
For example, you can get the current user's username as follows:
{{ app.user.username }}
app holds user, request, session, environment, and debug so you can put other values needed into the session or environment variables, and fetch/render those values directly in the template.
If the data that you want is related to the authenticated user, I would recommend what Arleigh Hix said
Otherwise you can create a class that extends AbstractExtension and fill it with your logic
Anything on this class can be accessed from all your twig pages
// src/Twig/AppExtension.php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class AppExtension extends AbstractExtension
{
public function getFunctions()
{
return [
new TwigFunction('myGlobalData', [$this, 'myGlobalData']),
];
}
public function myGlobalData()
{
return "what ever you want ( ex: a query resualt..)";
}
}
Than on your twig template just call it like this
{{ myGlobalData() }}
Even if it retuns an array, u can access it.

How to solve passing IDs in URL with Symfony

i'm making an app to make appointments with doctors, and in the template of the patient I need to use the id of the entity doctor in the path because I use it in the code of my controller, and I still can't get the Id of the entity doctor because when i do {{ doctor.id }} nothing appears on my screen, the content is empty.
As a solution I used the email of the doctor in the URL but it is quite ugly.
This is the code of the controller
/**
* #Route("/patient/ajouter-medecin-favoris/{id}", name="meet_my_doc_ajouter_medecin_favoris")
*/
public function ajouterMedecinFavoris(MedecinRepository $repoMedecin, ObjectManager $manager, $id)
{
$patient = $this->getUser();
$medecin = $repoMedecin->findOneById($id);
$patient->addMedecinsFavori($medecin);
$manager->persist($patient);
$manager->flush();
return $this->RedirectToRoute('accueil');
}
here's the code of the element I use to click on it to redirect on the controller previously
<fieldset>
<h4><i class="fa fa-star"></i> Dr. {{ medecin.nom }} {{ medecin.prenom }}</h4>
</fieldset>
I expected to be able to use the id of the doctor in the URL but I think when you're logged as an entity you can't get the ID (not the id used to log in) of other users and the content of the variable is empty. But I would know if there's a solution to use the ID because passing the email into the url isn't the solution I want, and I had the same problem on other piece of code where the of other entity is empty.
Here's the code of the controller that send the datas to the view
/**
* #Route("/patient/medecins-favoris", name="meet_my_doc_afficher_medecin_favoris")
*/
public function afficherMedecinFavoris()
{
$patient = $this->getUser();
$medecins = $this->getUser()->getMedecinsFavoris();
return $this->Render('meet_my_doc/afficherLesMedecinsFavoris.html.twig', ['medecins' => $medecins]);
}
try this
{{path('meet_my_doc_ajouter_medecin_favoris',{(id): medecin.id})}}
I would do something like this, first controller to receive Medicine Object like this, in this case you dont need to use $medecin = $repoMedecin->findOneById($id);
/**
* #Route("/patient/ajouter-medecin-favoris/{id}", name="meet_my_doc_ajouter_medecin_favoris")
*/
public function ajouterMedecinFavoris(MedecinRepository $repoMedecin, ObjectManager $manager, Medecin $medecin)
{
$patient = $this->getUser();
$patient->addMedecinsFavori($medecin);
$manager->persist($patient);
$manager->flush();
return $this->RedirectToRoute('accueil');
}
in twig in both cases you do something like this
{% for medecin in medecins %}
<fieldset>
<h4>
<i class="fa fa-star"></i>
Dr. {{ medecin.nom }} {{ medecin.prenom }}
</h4>
</fieldset>
{% endfor %}
If your code is how you've pasted it, then I think the issue lies in a typo - in the afficherMedecinFavoris() action you pass medicins to the view, and inside the view you try to access the id of medicin. So either remove the extra s in the afficherMedecinFavoris() or add that extra s to the path id parameter value.
Aside from that, I believe that this should work (under the condition that the identity field for Medecin entity is id):
/**
* #Route("/patient/ajouter-medecin-favoris/{id}", name="meet_my_doc_ajouter_medecin_favoris")
*/
public function ajouterMedecinFavoris(Medecin $medecin)
{
$patient = $this->getUser();
$patient->addMedecinsFavori($medecin);
$manager = $this->getDoctrine()->getManager();
$manager->persist($patient);
$manager->flush();
return $this->RedirectToRoute('accueil');
}
To know how the Medecin entity is available as an argument for the controller read this
I'm sorry to post the answer just now, but this how it worked for me : medecin inherit from User, so when I'm creating a medecin, it is creating also an User, both with the same Id, and if I want to use the medecin object's Id, I need to edit the getId() and replace the code by parent::getId(), this is the only way I've found to solve this problem.

How to generate routes ignoring extra passed parameters

Lets say that i have the following route annotations for a controller action:
* #Route("/post/{post_id}/", name="post.view")
* #Route("/post/", name="post.current_view")
And I want to use twig to generate the url for this:
{{ url(basePath~'view', {'post_id':post.postId}) }}
//basePath will either be "post." or "post.current_"
What i currently get is:
domain.com/post/1/
domain.com/post/?post_id=1
What i want though is for the second route to be generated ignoring any "EXTRA" parameters passed to it so that i would only get:
domain.com/post/
Does anyone know if this is something that can be natively accomplished? I know i could right a custom twig function that uses the router and then i can generate the routes and strip the query string but i want to avoid that if there is an easy toggle somewhere that i have missed.
Solution #1 you could just add an if clause
{% if BasePath == 'post.' %}
{{ url(BasePath~'view', {'post_id':post.postId}) }}
{% elseif BasePath == 'post.current_' %}
{{ url(BasePath~'view') }}
{% endif %}
maybe not the most elegant but should work.
Solution #2
spliting url with question marks and getting the first string
{% set myUrl = url(basePath~'view', {'post_id':post.postId}) %}
{{ myUrl|split("?")|first }}
Solution #3 Or you can override the url function by extending RoutingExtension class of twig.
Symfony\Bridge\Twig\Extension\RoutingExtension
can find an example here with path but url should be the same.
You should override this function
public function getUrl($name, $parameters = array(), $schemeRelative = false)
{
return $this->generator->generate($name, $parameters, $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL);
}
your function could look like this:
public function getUrl($name, $parameters = array(), $schemeRelative = false)
{
$yourUrl = parent::getUrl($name, $parameters = array(), $schemeRelative = false);
return strstr($yourUrl, '?' , true);
}
what id oes it removes everything afther the question mark.
To override the default class you have to add to the parameters
twig.extension.routing.class: MyNamespace\MyRoutingExtension
I guess not, you need a preg_replace filter and this isn't natively defined

How to get params in twig file

how can i use $_GET params in TWIG file like using PHP and alerting with JS.
URI-> ?comment=added...
in TWIG,
if($_GET['comment'] == "added"){
...echo '<script>alert("in TWIG file!");</script>';
}
hope it will help you
{% if app.request.get('comment') == "added" %}
<script>alert("in TWIG file!");</script>
{% endif %}
Depending on what you're really trying to achieve, the "Symfony way" of showing confirmation messages would be to use "Flash Messages":
YourController.php:
public function updateAction()
{
$form = $this->createForm(...);
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
// do some sort of processing
$this->get('session')->getFlashBag()->add(
'notice',
'Your changes were saved!'
);
return $this->redirect($this->generateUrl(...));
}
return $this->render(...);
}
Your TwigTemplate.twig:
{% for flashMessage in app.session.flashbag.get('notice') %}
<div class="flash-notice">
{{ flashMessage }}
</div>
{% endfor %}
This way you have multiple advantages:
Redirecting after action prevents form reloading.
Message cannot be triggered from outside.
Flash messages are only fetched once.
See the official documentation on this topic.
The "correct" solution would be use your controller to provide a function to Twig rather than switching on the querystring. This will be more robust and provide better security:
Controller:
function someAction()
{
$params = array('added' => false);
if( /* form logic post */ )
{
//some logic to define 'added'
$params['added'] = true;
}
$this->render('template_name', $params);
}
view:
{% if added %}
<script>alert('added');</script>
{% endif %}
The reasoning is that this is more secure (I can't trigger the alert by just browsing to the url), it maintains all business logic in the controller and you're also able to handle any errors - e.g. if you browse to foo.php?comment=added and there is an error wherein your comment isn't added, the user will still receive the alert.

How to load a controller function and render it in a twig tag using Symfony2?

I am using Symfony2 and Twig. I have a function (below) in my controller that returns a specific text. Is it possible to call that function directly from my template and change the {{text}} in my template to whatever the function returns, possibly via Ajax?
Here's my function:
public function generateCode($url) {
$url = $_SERVER['SERVER_NAME'] . '/embed/' . $url;
$return = '<iframe>'.$url.'</iframe>';
return $return;
}
Another controller function calls the function above and renders my template:
public function getCodeAction($url) {
$text = $this->generateCode($url);
return $this->render('MyMyBundle:User:code.html.twig', array('text' => $text));
}
In my template I am using:
{{ text }}
to display the value.
In Symfony 2.2, this was changed.
The render tag signature and arguments changed.
Before:
{% render 'BlogBundle:Post:list' with { 'limit': 2 }, { 'alt': BlogBundle:Post:error' } %}
After:
{% render controller('BlogBundle:Post:list', { 'limit': 2 }), { 'alt': 'BlogBundle:Post:error' } %}
or
{{ render(controller('BlogBundle:Post:list', { 'limit': 2 }), { 'alt': 'BlogBundle:Post:error'}) }}
Note: The function is the preferred way.
See https://github.com/symfony/symfony/blob/2.2/UPGRADE-2.2.md
You can use ajax if you have dynamic data, but as far as I can see from your brief info, you can always execute that controller function directly from your view:
{% render "MyMyBundle:User:generateCode" with { 'url': 'your url here' } %}
More Information on this available at:
http://symfony.com/doc/2.0/quick_tour/the_view.html, under Embedding other Controllers
For the record, in new versions you need to use the absolute URL:
{{ render url('my_route_id', {'param': value}) }}
{{ render(controller("AcmeDemoBundle:Demo:topArticles", {'num': 10})) }}
In Silex I solved it like this:
{{ render(url('route_name', {'param': value})) }}
If you do not have the route name, URL can be used:
{{ render(app.request.baseUrl ~ '/some-path/' ~ value) }}
If using URL we should always concat the baseUrl.
Symfony 2.6+
in twig:
{{ render(controller('AppBundle:PropertySearch:featuredProperties', {'limit': 15})) }}
controller:
/**
* featuredPropertiesAction
*
* #param Request $request
* #param int $limit
*
* #return Response
*/
public function featuredPropertiesAction(Request $request, $limit)
{
$search = $this->resultsHelper->featuredSearch($limit);
return $this->render('HASearchBundle::featured_properties.html.twig', [
'search' => $search,
]);
}

Resources