symfony2 form is not displaying - symfony

I am learning symfony2 and i have created one form in controller which is as bellow.
the controller file as DefaultController.php
namespace Banner\TestBundle\Controller;
use Banner\TestBundle\Entity\Contact;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Banner\TestBundle\Entity\Task;
use Symfony\Components\HttpFoundation\Request;
class DefaultController extends Controller
{
public function newAction(Request $request)
{
echo "The controller is called ";
$task = new Task();
$task->setTask("Write a blog Post ");
$task->setDueDate(new DateTime('tomorrow'));
$form = $this->createFormBuilder($task)
->add('task','text ')
->add('duedate','date')
->getForm();
return $this->render('BannerTestBundle:default:zoo.html.twig',array('form'=>$form->createView()));
}
}
my routing file is as below.
routing.yml
task_new:
pattern: /task/{request}
defaults: { _controller: BannerTestBundle:Default:new}
and the zoo.html.twig file is as bellow.
{% extends '::page.html.twig' %}
{% block title %}The Zoo{% endblock %}
{% block content %}
<form action="{{ path('task_new') }}" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<input type="submit">
</form>
{% endblock %}
as i am passing the "task/GET" in my url it will shows the 'Request does not exist. error 500'.
what i basically want to do is when i pass the url the zoo.html.twig will be called. i want to display the form in zoo.html.twig.

You don't need to pass $request to your action and you don't need to put it in your route. The request object is available in global context. So
task_new:
pattern: /task-new
defaults: { _controller: BannerTestBundle:Default:new}
You can either leave it this way
public function newAction(Request $request)
or have
public function newAction()
and access request object like this
$request = $this->getRequest();

Your routing file is not properly indented so the yaml will not parse properly. Try:
task_new:
pattern: /task/{request}
defaults: { _controller: BannerTestBundle:Default:new}
Also:
It is probably a bad idea to override request that way
You don't appear to be doing anything with request in your controller

Related

How to generate right route in twig with dynamic sub-domain?

I have a multi-tenant APP based on sub-domain that's working properly but I have an issue with generated link in Twig templates.
All the generated link are on the default sub-domain and not the current one
routes.yaml
app_customer:
resource: '../src/Controller/Customer/'
host: "{subdomain}.domain.com"
defaults:
subdomain: tenant1
requirements:
subdomain: tenant1|tenant2
SecurityController.php
class SecurityController extends AbstractController
{
/**
* #Route("/login", name="app_login", methods={"GET","POST"})
*/
public function login(AuthenticationUtils $authenticationUtils)
{
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
]);
}
}
login.html.twig
<form action="{{ path('app_login') }}" method="post">...</form>
will always generate https://tenant1.domain.com/login however the current url is tenant2.domain.com
I believe it is always generating https://tenant1.domain.com/login because you have "tenant1" set as the default value for subdomain and you do not pass a different value when you call path()
Try this :
{% set currsubdomain = app.request.getHttpHost()|split('.')|first %}
<form action="{{ path('app_login', {subdomain: currsubdomain }) }}" method="post">...</form>
Or maybe just pass the whole host:
<form action="{{ path('app_login', {host: app.request.getHttpHost}) }}" method="post">...</form>

reusable dynamic sidebar in Symfony 4 (Twig)?

I recently started using Symfony 4 and I am creating my first website with this wonderful framework right now.
I have a sidebar that should be displayed in about half of my routes and the content of the sidebar should be filled with some data from a database.
Currently I use DI in all this routes and pass the result of the injected repository to the template (which includes my sidebar.html.twig) for the route.
public function chalupaBatman(FancyRepository $repository)
{
$sidebarObjects = $repository->getSidebarObjects();
$this->render('controllername/chalupabatman.html.twig', [
'sidebarObjects' => $sidebarObjects,
]);
}
I am wondering if there is a way to avoid this for every route I define in my controllers.
So far I found this topic on stackoverflow.
The User Mvin described my problem in a perfect way and also provided some solutions.
However there is still no answer to "what is the best practice" part also the topic is from 2017; therefor, the way to solve this may have changed in Symfony 4.
I ended up with a TwigExtension solution. I'll describe how to achieve it and it would be great if you guys could provide some feedback.
Let me know if I produce massive overhead or miss something essential ;-)
Alright, first of all I created a TwigExtension via command-line
php bin/console make:twig-extension AppExtension
And then I modified the class to look like this:
<?php
namespace App\Twig;
use App\Repository\ArticleRepository;
use Psr\Container\ContainerInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class AppExtension extends AbstractExtension implements ServiceSubscriberInterface
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getFunctions(): array
{
return [
new TwigFunction('article_sidebar', [$this, 'getArticleSidebar'], ['needs_environment' => true, 'is_safe' => ['html']]),
];
}
public function getArticleSidebar(\Twig_Environment $twig)
{
$articleRepository = $this->container->get(ArticleRepository::class);
$archive = $articleRepository->myAwesomeLogic('omegalul');
return $twig->render('/article/sidebar.html.twig', [
'archive' => $archive,
]);
}
public static function getSubscribedServices()
{
return [
ArticleRepository::class,
];
}
}
In order to activate Lazy Performance so our Repository and the additional Twig_Environment doesn't get instantiated everytime when we use Twig
we implement the ServiceSubscriberInterface and add the getSubscribedServices-method.
Therefor, our Repo and Twig_Environment only gets instantiated when we actually call {{ article_sidebar() }} inside a template.
{# example-template article_base.html.twig #}
{% extends 'base.html.twig' %}
{% block body %}
<div class="row">
<div class="col-10">
{% block article_body %}{% endblock %}
</div>
<div class="col-2">
{{ article_sidebar() }}
</div>
</div>
{% endblock %}
Now I am able to define my templates for the article-routes like this:
{# example-template /article/show.html.twig #}
{% extends 'article_base.html.twig' %}
{% block article_body %}
{# display the article here #}
{% endblock %}

How to include a reusable widget in Symfony (Twig)?

So, I'm still fairly new to Symfony and Twig. I was wondering how to best include/create a snippet of reusable code in the templates. Say, for example, that you have a sidebar that you want to show on every page.
{% extends 'AppBundle::base.html.twig' %}
{% block body %}
<div id="wrapper">
<div id="content-container">
{# Main content... #}
</div>
<div id="sidebar">
{% include 'sidebar.html.twig' %}
</div>
</div>
{% endblock %}
And that in that sidebar are a couple of widgets that all do their own logic. How you do go about creating/including those widgets?
So far, I've come across several solutions.
As a controller
The first was to embed the widget as a controller(s) in Twig.
class WidgetController extends Controller
{
public function recentArticlesWidgetAction()
{
// some logic to generate to required widget data
// ...
// Render custom widget template with data
return $this->render('widgets/recentArticles.html.twig', array('data' => $data)
);
}
public function subscribeButtonWidgetAction()
{
// ...
return $this->render('widgets/subscribeButton.html.twig', array('data' => $data)
}
// Many more widgets
// ...
}
And include that in 'sidebar.html.twig' like so
<div id="sidebar">
{# Recent Articles widget #}
{{ render(controller('AppBundle:Widget:recentArticlesWidget' )) }}
{# Subscribe-Button widget #}
{{ render(controller('AppBundle:Widget:subscribeButtonWidget' )) }}
{# and so on #}
</div>
As a service
I've also seen some people register widgets as services (that can be used in Twig directly). With the widget main class
// src/AppBundle/Service/RecentArticlesWidget.php
namespace AppBundle\Service;
use Symfony\Component\DependencyInjection\ContainerInterface;
class RecentArticlesWidget
{
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getRecentArticles()
{
// do some logic (use container for doctrine etc.)
}
}
that is then registered as a service,
# src/AppBundle/Resources/config/services.yml
services:
recentArticlesWidget:
class: AppBundle\Service\RecentArticlesWidget
arguments: ["#service_container"]
passed to the template in the controller,
namespace AppBundle\Controller;
class SidebarController {
public function showAction($request) {
// Get the widget(s)
$recentArticlesWidget = $this->get('recentArticlesWidget');
// Pass it (them) along
return $this->render('sidebar.html.twig', array('recentArticlesWidget' => $recentArticlesWidget));
}
}
so it can simply be used like this in Twig
{# sidebar.html.twig #}
{{ recentArticlesWidget.getRecentArticles()|raw }}
Alternatively, you can also add your service to the Twig global variables directly by adding it to the Twig config. This way, it won't need to be passed into the view by the controller.
#app/config/config.yml
twig:
globals:
# twig_var_name: symfony_service
recentArticlesWidget: "#recentArticlesWidget"
As a Twig Extension
This one is very similar to using a service above (see the documentation). You create an a twig extension class that is almost identical to the service shown previously
// src/AppBundle/Twig/RecentArticlesWidgetExtension.php
namespace AppBundle\Twig;
class RecentArticlesWidgetExtension extends \Twig_Extension
{
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getFunctions()
{
return array(
"getRecentArticles" => new Twig_Function_Method($this, "getRecentArticles")
// register more functions
);
}
public function getRecentArticles()
{
// do some logic (use container for doctrine etc.)
}
// Some more functions...
public function getName()
{
return 'WidgetExtension';
}
}
Register that as a service with an added tag
# src/AppBundle/Resources/config/services.yml
services:
recentArticlesWidget:
class: AppBundle\Twig\RecentArticlesWidgetExtension
arguments: [#service_container]
tags:
- { name: twig.extension }
and simply use it like a global function in Twig
{# sidebar.html.twig #}
{{ getRecentArticles() }}
Thoughts
One thing I noticed is that with the last two methods is that the logic and the view don't seem to be seperated at all anymore. You basically write a widget function and have that function output the complete html for the widget. This seems to go against the modularity and patterns Symfony tries to enforce.
On the other hand, calling a distinct controller or controller action (with their own twig renders) for every single widget seems like it could take more processing than might be needed. I'm not sure if it actually slows anything down, but I do wonder if its excessive.
Long story short, is there a best practice for using reusable widgets in Symfony? I'm sure some of these methods can also be mixed, so I was just wondering how to best go about this.
Twig extension and Twig macro should point you in the right direction.
Use the macro for the view and extension for the business logic.
On a side note in your Twig extension example, it's probably a good idea to only pass in services that you are using instead of the whole service container.
I would rather use blocks and a parent template. Simply put, insert the side bar in the main layout and have all other templates that require the side bar
inherit from it.
Something like this:
layout.html.twig will be something like this:
{% block title}
// title goes here
{%endblock%}
<div id="wrapper">
<div id="content-container">
{% block pageContent %}
{% endblock %}
</div>
<div id="sidebar">
// Side bar html goes here
</div>
</div>
Now all pages will inherit from this layout.html.twig. Say for example a page called home.html.twig will be:
home.html.twig
{% extends 'AppBundle::layout.html.twig' %}
{% block title%}
// this page title goes here
{% endblock %}
{% block pageContent %}
//This page content goes here
{% endblock %}
You can add as many blocks as needed, for example css and js blocks for each page.
Hope this helps!
I think the simplest way is defining a block in a template and then extending that template to render blocks like so:
#reusable.html.twig
{% block reusable_code %}
...
{% endblock %}
And
#reused.html.twig
{% extends 'reusable.html.twig' %}
{{ block('reusable_code') }}
If you want more reusability than that or your block contains business logic or model calls a twig extension is the way to go

No more routes available after Twig's render() function

I’m facing a weird behavior from Symfony 2.5.5 (PHP 5.6.1), more specifically Twig. Here is a fragment of my template layout:
<nav>
{% render controller('SGLotteryGameBundle:Home:lastDraw') %}
<ol class="breadcrumb">
<li>{{ 'SuperWinner'|trans }}</li>
{% block bc %}{% endblock %}
</ol>
</nav>
This template worked fine until I added the render call. After that, Symfony reported:
An exception has been thrown during the rendering of a template
("Unable to generate a URL for the named route "sg_lottery_home" as such route does not exist.")
in /home/kevin/Prog/PHP/SG2/src/SG/Lottery/GameBundle/Resources/views/layout.html.twig at line 70.
Of course, the sg_lottery_home is defined and works well without the render block. If I comment the path generation of this route, the immediate next one fails. Routes before the tag are rendered without any issue.
Here is the SGLotteryGameBundle:Home controller:
<?php
namespace SG\Lottery\GameBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Response;
class HomeController extends Controller
{
/**
* #Template
*/
public function indexAction()
{
return [];
}
public function lastDrawAction()
{
return new Response('Dummy');
}
}
I tried replacing {% render ... %} by {{ render(...) }}, without any change.
Important note: it only happens when I’m logged in.
Apparently, it was caused by JMSI18nRoutingBundle generating an error while retrieving the user's locale: the available locales were en and fr and the user's locale fr_FR. I have no idea how the {{ render(...) }} call interacted with that.

Form not valid in Symfony2

I am trying to process a form in Symfony2, I get all the values from it in the controller, but when I call:
$form->isValid()
it returns false. I am trying to process the form in a different Controller.
The form is rendered in two pieces, first some personal data and then some more information, but in the submit event of the form, I join all values serializing the forms. This is how I render the form in the view:
<form id="myform" action="{{ path('_process_my_form') }}" {{ form_enctype(form) }} method="POST">
{{ form_errors(form) }}
{{ form_widget(form._token) }}
{{ form_widget(form.first_name, {'attr': {'class': 'form-control'}} ) }}
... // then I render the rest of the personal information widgets
<button type="submit" class="btn btn-info btn-block">Submit</button>
</form>
<form id="myform2">
{{ form_widget(form.job_name, {'attr': {'class': 'form-control'}} ) }}
... // then I render the rest of the widgets (not personal information)
</form>
Then, in the Ajax request, I join the two forms like this:
$("#my_form").submit(function(e) {
e.preventDefault();
$.ajax({
type: e.target.method,
url: e.target.action,
data: $("#my_form").serialize() + $("#my_form2").serialize(),
beforeSend: function( xhr ) {},
success: function(data) {
//do something...
}
});
});
My _process_my_form rule looks like this:
_process_my_form:
pattern: /ajax/process-my-form
defaults: { _controller: MyBundle:User:processMyForm }
This is the Controller who controls the Ajax request:
public function processMyFormAction(Request $request)
{
if ($request->isMethod('POST'))
{
$myEntity = new myEntity();
$form = $this->container->get('form.factory')->create(new myFormType(), $myEntity);
$form->bind($request);
if ($form->isValid())
{
//Process the form... this code is never excecuted :(
}
}
}
Am I doing something wrong???
Thanks!!!
As of Symfony 2.3 I believe they changed the form handle from
$form->bind($request);
to
$form->handleRequest($request);
However I'm not sure if the former is still supported? As far as I can see, the bind method not longer exists in the Form Interface
You can read more here Symfony Cookbook - Form Direct Submit

Resources