I'm writing an API that will take in a JSON string, parse it and return the requested data. I'm using Symfony's Validation component to do this, but I'm having some issues when validating arrays.
For example, if I have this data:
{
"format": {
"type": "foo"
}
}
Then I can quite easily validate this with PHP code like this:
$constraint = new Assert\Collection(array(
"fields" => array(
"format" => new Assert\Collection(array(
"fields" => array(
"type" => new Assert\Choice(["foo", "bar"])
)
))
)
));
$violations = $validator->validate($data, $constraint);
foreach ($violations as $v) {
echo $v->getMessage();
}
If type is neither foo, nor bar, then I get a violation. Even if type is something exotic like a DateTime object, I still get a violation. Easy!
But if I set my data to this:
{
"format": "uh oh"
}
Then instead of getting a violation (because Assert\Collection expects an array), I get a nasty PHP message:
Fatal error: Uncaught Symfony\Component\Validator\Exception\UnexpectedTypeException: Expected argument of type "array or Traversable and ArrayAccess", "string" given [..]
If there a neat way to handle things like this, without needing to try / catch and handle the error manually, and without having to double up on validation (e.g. one validation to check if format is an array, then another validation to check if type is valid)?
Gist with the full code is here: https://gist.github.com/Grayda/fec0ed7487641645304dee668f2163ac
I'm using Symfony 4
As far as I can see, all built-in validators throw an exception when they are expecting an array but receive something else, so you'll have to write your own validator. You can create a custom validator that first checks if the field is an array, and only then runs the rest of the validators.
The constraint:
namespace App\Validation;
use Symfony\Component\Validator\Constraints\Composite;
/**
* #Annotation
* #Target({"PROPERTY", "METHOD", "ANNOTATION"})
*/
class IfArray extends Composite
{
public $message = 'This field should be an array.';
public $constraints = array();
public function getDefaultOption()
{
return 'constraints';
}
public function getRequiredOptions()
{
return array('constraints');
}
protected function getCompositeOption()
{
return 'constraints';
}
}
And the validator:
namespace App\Validation;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class IfArrayValidator extends ConstraintValidator
{
/**
* {#inheritdoc}
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof IfArray) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\IfArray');
}
if (null === $value) {
return;
}
if (!is_array($value) && !$value instanceof \Traversable) {
$this->context->buildViolation($constraint->message)
->addViolation();
return;
}
$context = $this->context;
$validator = $context->getValidator()->inContext($context);
$validator->validate($value, $constraint->constraints);
}
}
Note that this is very similar to the All constraint, with the major difference being that if !is_array($value) && !$value instanceof \Traversable is true, the code will add a violation instead of throwing an exception.
The new constraint can now be used like this:
$constraint = new Assert\Collection(array(
"fields" => array(
"format" => new IfArray(array(
"constraints" => new Assert\Collection(array(
"fields" => array(
"type" => new Assert\Choice(["foo", "bar"])
)
))
)),
)
));
Related
I know this topic was discussed 2 years ago. But I stuck on difficulty that I would like to solve.
I would like to create a zf3 form which contains more than two Select Element.
I would like to populate them with data coming from different repositories (options values of each Select item come from distinct repository).
First all, I tried to pass the service manager (from where I can access to my repositories) in the constructor of my form but I heard that this solution is not suitable at all.
So how can I include multiples repositories in my form to populate my Select Elements?
Short answer:
Create a class that extends the Select
Create a factory for such class
Add this custom element in your module configuration (module.config.php)
Use this class as type for your form elements
Retrieve the form through the form manager
Example, for controllers, adapt the controller's factory
Detailed answer:
Create a class that extends the Select, like BrandSelect
namespace MyModule\Form\Element;
use Laminas\Form\Element\Select;
class BrandSelect extends Select {
protected $repository;
public function __construct($repository, $name = null, $options = []) {
parent::__construct($name, $options);
$this->repository = $repository;
}
/**
* Initialize the element
*
* #return void
*/
public function init() {
$valueOptions = [];
foreach ($this->repository->fetchBrands() as $brand) {
$valueOptions[$brand->getBrandId()] = $brand->getName();
}
asort($valueOptions);
$this->setValueOptions($valueOptions);
}
}
Create a factory for such class
namespace MyModule\Form\Element;
use Laminas\ServiceManager\Factory\FactoryInterface;
use Interop\Container\ContainerInterface;
use MyModule\Db\Repository;
class BrandSelectFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, $requestedName, $options = null): BrandSelect {
$repository = $container->get(Repository::class);
return new BrandSelect($repository);
}
}
Add this custom element in your module configuration (module.config.php)
namespace MyModule;
return [
// ..
// Other configs
// ..
'form_elements' => [
'factories' => [
Form\Element\BrandSelect::class => Form\Element\BrandSelectFactory::class
]
]
];
Use this class as type for your form elements.
It is really important to add all elements in the init() method, otherwise it will not work. I also added the InputFilterProviderInterface.
In this case, form doens't require any other element its constructor. If needed, you must create a factory for the form and pass all params you need. The form factory must be added in module.config.php configuration, always under the form_elements key (as did for the BrandSelect):
namespace MyModule\Form;
use Laminas\Form\Form;
use Laminas\InputFilter\InputFilterProviderInterface;
class BrandForm extends Form implements InputFilterProviderInterface {
public function __construct($name = null, $options = []) {
parent::__construct($name, $options);
}
// IT IS REALLY IMPORTANT TO ADD ELEMENTS IN INIT METHOD!
public function init() {
parent::init();
$this->add([
'name' => 'brand_id',
'type' => Element\BrandSelect::class,
'options' => [
'label' => 'Brands',
]
]);
}
public function getInputFilterSpecification() {
$inputFilter[] = [
'name' => 'brand_id',
'required' => true,
'filters' => [
['name' => 'Int']
]
];
return $inputFilter;
}
}
Retrieve the form through the form manager
Form must be retrieved using correct manager, which isn't the service manager, but the FormElementManager.
For example, if you need the form inside BrandController:
<?php
namespace MyModule\Controller;
use Laminas\Form\FormElementManager;
use Laminas\Mvc\Controller\AbstractActionController;
use Laminas\View\Model\ViewModel;
class BrandController extends AbstractActionController {
private $formManager;
public function __construct(FormElementManager $formManager) {
$this->formManager = $formManager;
}
public function addBrandAction() {
$form = $this->formManager->get(\MyModule\Form\BrandForm::class);
// Do stuff
return new ViewModel([
'form' => $form
]);
}
}
Finally, you'll have to adapt the controller's factory:
namespace MyModule\Controller;
use Laminas\ServiceManager\Factory\FactoryInterface;
use Interop\Container\ContainerInterface;
class BrandControllerFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, $requestedName, $options = null): BrandController {
$formManager = $container->get('FormElementManager');
return new BrandController($formManager);
}
}
which must be configured under controllers key in module.config.php:
namespace MyModule;
return [
// ..
// Other configs
// ..
'controllers' => [
'factories' => [
Controller\BrandController::class => Controller\BrandControllerFactory::class
],
],
'form_elements' => [
'factories' => [
Form\Element\BrandSelect::class => Form\Element\BrandSelectFactory::class
]
]
];
I'm trying to do some stuff while submitting a form in a custom module. Some of that is done by calling a function from a controller. That's when i get:
Error: Class 'Drupal\ice_cream\Controller\OrderController' not found in Drupal\ice_cream\Form\OrderForm->submitForm() (line 77 of modules\custom\ice_cream\src\Form\OrderForm.php).
As far as I can tell the namespaces aren't wrong? Or is that not related to this error?
This is how my OrderForm.php and submitForm() looks like:
<?php
namespace Drupal\ice_cream\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\ice_cream\Controller\OrderController;
/**
* Implements the order form.
*/
class OrderForm extends FormBase {
... (omitted code for getFormid and buildForm)
public function submitForm(array &$form, FormStateInterface $form_state) {
//Check if the order is ice or waffles.
if($form_state->getValue('foodType') == 'ice'){
//Save order to the DB.
OrderController::saveOrder($form_state->getValue('foodType'), $form_state->getValue('taste'));
... (more code)
}
}
}
This is how the controller looks like:
<?php
namespace Drupal\ice_cream\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Database;
/**
* Order controller for interacting (insert, select,...) with the ice cream table in the DB.
*/
class OrderController extends ControllerBase {
/**
* Saves an order for either Ice or Waffles with their options (tast or toppings).
*/
public function saveOrder($foodType, $options) {
$connection = Database::getConnection();
//Check if ice or waffles (to only insert the right field with $options).
if($foodType == "ice"){
$result = $connection->insert('ice_cream')
->fields([
'foodType' => $foodType,
'taste' => $options,
'toppings' => "",
])
->execute();
return true;
}elseif($foodType == "waffles"){
$result = $connection->insert('ice_cream')
->fields([
'foodType' => $foodType,
'taste' => "",
'toppings' => $options,
])
->execute();
return true;
}
}
}
Try using the below code:
$obj= new OrderController;
$obj->saveOrder($form_state->getValue('foodType'), $form_state->getValue('taste'));
Solved:
Just solved it with my mentor. The code was more or less correct, still needed to make my functions static in the OrderController and also I made a stupid error of forgetting the .php extension in my filename when I created it with the 'touch' terminal command...
I have this scenario:
a entity Person, a repository for Person and a select form type.
I should take in my select form only active person.
In the entity there is a public method "isActive" that check if a Person have the permission to access in my private area. This method return true or false and is not a column in the db table, it is calculated.
I need to access this flag from my querybuilder in the Person repository.
Is it possible?
below the code of my querybuilder in the repository.
public function getQueryBuilderForEventRegistration()
{
$queryBuilder = $this->createQueryBuilder('e')->orderBy('e.surname', 'asc')->addOrderBy('e.name', 'asc');
return $queryBuilder;
}
and the public method in the entity Person tha t i have to access:
public function getIsActive()
{
if (empty($this->getUser()))
{
return false;
}
if (!$this->getUser()->isEnabled())
{
return false;
}
/* #var $service \Siderweb\SubscriptionBundle\Entity\UserService */
foreach ($this->getUser()->getServices() as $service)
{
if (!$service->getIsExpired())
{
return true;
}
}
return false;
}
and my type:
$builder->add('personExist', 'entity', array(
'class' => 'MyAppUserBundle:Person',
'property' => 'name',
'required' => false,
'multiple' => false,
'mapped' => false,
'empty_value' => '-- New person --',
'query_builder' => function(PersonRepository $repo) use ($options) {
return $repo->getQueryBuilderForEventRegistration();
}
))
as suggested I edit my repository like this:
public function getQueryBuilderForEventRegistration(Company $company = null, Event $event = null, $emailFilter = null)
{
$queryBuilder = $this->createQueryBuilder('e')->orderBy('e.surname', 'asc')->addOrderBy('e.name', 'asc');
$people = $queryBuilder->getQuery()->execute();
$peopleToShow = array();
foreach ($people as $person)
{
if ($person->getIsActive())
{
array_push($peopleToShow, $person);
}
}
return $peopleToShow;
}
but now I don't know how to put this array in my typeForm. Any idea?
Okay, so you can't call entity method on the query builder, but you can on the query results. Doctrine will hydrate your entity objects with the data that is returned from the database.
Once you have your results you can call the isActive() method on the hydrated entities.
The way that you're trying to implement this (getting the query builder for the form), you will need an isActive column in your database table and add a 'where' clause like so:
public function getQueryBuilderForEventRegistration()
{
$queryBuilder = $this->createQueryBuilder('e')
->where('e.isActive', true)
->orderBy('e.surname', 'asc')
->addOrderBy('e.name', 'asc');
return $queryBuilder;
}
It is not possible to call a Custom PHP method a SQL query, so Doctrine's QueryBuilder does not allow that. You will need either to have isActive as a database field (or custom method), or to reproduce you getIsActive method with QueryBuilder conditions.
You could use the filter function to filter the array on the template.
In my case I have an entity that has a canBeProcessed method ( which depends on many things ).
So, when I want to show in the index method of the controller only those records that
"canBeProcessed" I use this :
{% for emitted_note in pagination|filter( emitted_note => emitted_note.canBeProcessed == true ) %}
<tr><td>{{emitted_note.data}}</td><tr>
{% endfor %}
Here's the twig documentation.
https://twig.symfony.com/doc/2.x/filters/filter.html
Regards.
I'm using symfony 2.8 and FOSUserBundle. I want to allow admins to edit users' usernames and emails. If the new username or email is already taken then the database gives an error
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry
which is good, but I don't know how to communicate that back to the admin who tried to change it to tell them what went wrong (the production version of the app will just give an error 500). What I want to do is show an error message of some kind (preferable like the one FOSUserBundle has in its forms) to say the username (or email) is taken.
The relevant portions of the form is built here:
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->findUserBy(array('id' => $id));
$form = $this->createFormBuilder()
->add('username', TextType::class, array(
'label' => 'Username',
'data' => $user->getUsername(),
))
->add('email', EmailType::class, array(
'label' => 'Email',
'data' => $user->getEmail(),
))
->getForm();
and the database is handled here:
if ($form->isSubmitted() and $form->isValid()) {
// set new username if different
$newUsername = $form['username']->getData();
if ($user->getUsername() !== $newUsername) {
$user->setUsername($newUsername);
}
// set new email if different
$newEmail = $form['email']->getData();
if ($user->getEmail() !== $newEmail) {
$user->setEmail($newEmail);
}
$userManager->updateUser($user);
}
I have tried a number of things, like also setting username_canonical and email_canonical, or adding #UniqueEntity in my User.php class, but they haven't helped (which makes sense since the error is correct - I just can't translate it into a useful message).
If you doesn't want override anymore for make some validation, you need to implement an EventListener that catch the exceptions of your need by listening on the onKernelResponse event.
DBALExceptionResponseListener.php
// src/AcmeBundle/EventListner/DBALExceptionResponseListener.php
<?php
namespace AcmeBundle\EventListener;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Doctrine\DBAL\DBALException;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\Router\RouterInterface;
class DBALExceptionResponseListener
{
public function __construct(SessionInterface $session, RouterInterface $router)
{
$this->session = $session;
$this->router = $router;
}
/**
* #param GetResponseForExceptionEvent $event
*/
public function onKernelResponse(GetResponseForExceptionEvent $event)
{
$request = $event->getRequest();
$exception = $event->getException();
$message = $exception->getMessage();
// Maybe some checks on the route
if ($request->get('_route') !== 'your_route' || $request->headers->get('referer') !== 'your_referer') {
return;
}
// Listen only on the expected exception
if (!$exception instanceof DBALException) {
return;
}
// You can make some checks on the message to return a different response depending on the MySQL error given.
if (strpos($message, 'Integrity constraint violation')) {
// Add your user-friendly error message
$this->session->getFlashBag()->add('error', 'SQL Error: '.$message);
}
// Create your custom response to avoid the error page.
$response = new RedirectResponse($this->router->generate('your_route'));
// Update the Event Response with yours
$event->setResponse($response);
}
}
services.yml
# app/config/services.yml
services:
acme.kernel.listener.dbal_exception_response_listener:
class: AcmeBundle\EventListener\DBALExceptionResponseListener
tags:
- {name: kernel.event_listener, event: kernel.exception, method: onKernelResponse}
arguments:
session: "#session"
router: "#router"
By looking more at the Exception::$message, you can easily find which property causes the problem.
The most common message contains something like :
... column 'propertyname' cannot be null ...
I am new to Symfony2 but read about it very much.
First of all, I am using symfony 2.1.7. And FOSUserBundle for user settings. I have already override fos_user-login template, with username and password. But I want to add a captcha for log in. I have seen GregwarCaptchaBundle, and according to document, new field should be added to FormType. And my question comes: Where is the symfony or FOSUserBundle login form type, that i can add this new field, or override it? There exists ChangePasswordFormType, ProfileFormType... etc. but no LoginFOrmType. May be it is so obvious but i did not get the point, Any help is welcomed please
QUESTION IS EDITED WITH A SOLUTION SOMEHOW
Take a look at the comments below that Patt helped me.
I have created a new form type with _username, _password and captcha fields. When naming for username and password begins with an underscore is enough for 'login_check' routing and Symfony authentication. However Symfony uses a listener for login process.
Which is UsernamePasswordFormAuthenticationListenerclass. Although i've added captcha field in the Form type, it is always ignored during login process.(It is rendered on the page, but the field is never validated, it is simply ignored.)
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('_username', 'email', array('label' => 'form.username', 'translation_domain' => 'FOSUserBundle')) // TODO: user can login with email by inhibit the user to enter username
->add('_password', 'password', array(
'label' => 'form.current_password',
'translation_domain' => 'FOSUserBundle',
'mapped' => false,
'constraints' => new UserPassword()))
->add('captcha', 'captcha');
}
As i mentioned above UsernamePasswordFormAuthenticationListener class gets the form input values and then redirects you:
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null)
{
parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array(
'username_parameter' => '_username',
'password_parameter' => '_password',
'csrf_parameter' => '_csrf_token',
'captcha' => 'captcha',
'intention' => 'authenticate',
'post_only' => true,
), $options), $logger, $dispatcher);
$this->csrfProvider = $csrfProvider;
}
captcha field is added.
protected function attemptAuthentication(Request $request)
{
if ($this->options['post_only'] && 'post' !== strtolower($request->getMethod())) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('Authentication method not supported: %s.', $request->getMethod()));
}
return null;
}
if (null !== $this->csrfProvider) {
$csrfToken = $request->get($this->options['csrf_parameter'], null, true);
if (false === $this->csrfProvider->isCsrfTokenValid($this->options['intention'], $csrfToken)) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
}
// check here the captcha value
$userCaptcha = $request->get($this->options['captcha'], null, true);
$dummy = $request->getSession()->get('gcb_captcha');
$sessionCaptcha = $dummy['phrase'];
// if captcha is not correct, throw exception
if ($userCaptcha !== $sessionCaptcha) {
throw new BadCredentialsException('Captcha is invalid');
}
$username = trim($request->get($this->options['username_parameter'], null, true));
$password = $request->get($this->options['password_parameter'], null, true);
$request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);
return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
}
Now, i have captcha on login screen.
Playing with symfony code is not a good way, i know. If i find out some way to override and call my own function, i'll post it.
ANOTHER USEFUL ANSWER
I found another answer that might be useful
[link]Is there any sort of "pre login" event or similar?
Following this solution, I have simply override UsernamePasswordFormAuthenticationListenerclass and override security listener security.authentication.listener.form.class parameter. Here goes the code:
namespace TCAT\StaffBundle\Listener;
use Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener as BaseListener; use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Log\LoggerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException;
class StaffLoginFormListener extends BaseListener
{
private $csrfProvider;
/**
* {#inheritdoc}
*/
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options
= array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null)
{
parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array(
'username_parameter' => '_username',
'password_parameter' => '_password',
'csrf_parameter' => '_csrf_token',
'captcha' => 'captcha',
'intention' => 'authenticate',
'post_only' => true,
), $options), $logger, $dispatcher);
$this->csrfProvider = $csrfProvider;
}
/**
* {#inheritdoc}
*/
protected function attemptAuthentication(Request $request)
{
if ($this->options['post_only'] && 'post' !== strtolower($request->getMethod())) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('Authentication method not supported: %s.', $request->getMethod()));
}
return null;
}
if (null !== $this->csrfProvider) {
$csrfToken = $request->get($this->options['csrf_parameter'], null, true);
if (false === $this->csrfProvider->isCsrfTokenValid($this->options['intention'], $csrfToken)) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
}
// throw new BadCredentialsException('Bad credentials');
$userCaptcha = $request->get($this->options['captcha'], null, true);
$dummy = $request->getSession()->get('gcb_captcha');
$sessionCaptcha = $dummy['phrase'];
if ($userCaptcha !== $sessionCaptcha) {
throw new BadCredentialsException('Captcha is invalid');
}
$username = trim($request->get($this->options['username_parameter'], null, true));
$password = $request->get($this->options['password_parameter'], null, true);
$request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);
return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
}
}
and add security.authentication.listener.form.class: TCAT\StaffBundle\Listener\StaffLoginFormListener line to the app/config/paramaters.yml
BTW i can check my captcha value. I hope it all work for you.
Adding Captcha to Symfony2 Login Page
I am not sure this is a great idea. But it's doable.
Where is the symfony or FOSUserBundle login form type?
There is no form type for the login. The form is directly embed in the template as you can see in login.html.twig.
How could you do it?
You could totally create one but you would have to customize the SecurityController so that you send your form to the template.
The procedure would be something like that:
1. Create your custom loginFormType (that's where you can add your captcha in the builder).
2. Override the SecurityController (you could take a look here to see something similar). You need to override the loginAction method so that you can pass the form to your template here.
3. Override login.html.twig to render the form passed from your controller
Edit: Answer to your comment
How can you access to your form in a controller that extends
ContainerAware?
I highly recommend this reading to see how you can move away from the base controller. Now, how can you do this?
Well, you have 2 options:
OPTION 1: EASY WAY
$form = $this->createForm(new LoginFormType(), null);
becomes:
$form = $this->get('form.factory')->create(new LoginFormType(), $null);
OPTION 2: REGISTER FORM AS A SERVICE
1. Create your formType (normal procedure): loginFormType
2. Define your form as a service acme_user.login.form. You have a great example here (In the 1.2 version of FOSUserBundle, both registration and profile forms were registered as services, so this gives you a perfect example of how it's done).
3. You can now use your form inside your controller extending ContainerAware. See here.
$form = $this->container->get('acme_user.login.form');
In response to : Playing with symfony code is not a good way, i know. If i find out some way to override and call my own function, i'll post it.
To override the "UsernamePasswordFormAuthenticationListenerclass" you must copy the listner file in your bundle and change the config.yml file to load th new one :
parameters:
security.authentication.listener.form.class: Acme\YourBundle\Security\UsernamePasswordFormAuthenticationListener
Also the namespace in the copied file must be changed to the correct one :
namespace Acme\YourBundle\Security;
The last thing is adding "AbstractAuthenticationListener" in the use part to be loaded correctly :
use Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener;