How to pass parameter to FormType constructor from controller - symfony-forms

In Symfony2.7 i was able to pass parameter to Form Type constructor directly from controller while creating the form, however in Symfony3 i'm not able to do it!
Before in Symfony2.7
$postedBy = $this->getUser()->getFullname();
$form = $this->createForm(new NewsType($postedBy));
After in Symfony3
$form = $this->createForm(NewsType::class); // no idea how to pass parameter?
Update:
I also wanted to access it from:
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
// how to access posted_by_name here which is sent from controller
}
Any help will be highly appreciated..

Thanks for your time! i resolved this myself:
I removed parameter from NewsType constructor and added data to postedBy form field using $options array, and passed data to $options array from controller, please check following:
NewsType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('postedBy', HiddenType::class, array(
'data' => $options['postedBy']
)
)
;
}
// WARNING: this is a MANDATORY block! Only options described here will be allowed to be passed.
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'postedBy' => null,
));
}
Controller
$form = $this->createForm(NewsType::class, $news, array(
'postedBy' => $this->getUser()->getFullname(),
);
UPDATE:
Please use below code if you want to access $options array from addEventListener:
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$postedBy = $event->getForm()->getConfig()->getOptions()['postedBy'];
}
Hope it helps somebody!

You need to define your form as service.
namespace AppBundle\Form\Type;
use App\Utility\MyCustomService;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class NewsType extends AbstractType
{
private $myCustomService;
private $myStringParameter;
public function __construct(MyCustomService $service, $stringParameter)
{
$this->myCustomService = $service;
$this->myStringParameter = $stringParameter;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Your code
}
}
Add to your service configuration:
#src/AppBundle/Resources/config/services.yml
services:
app.form.type.task:
class: AppBundle\Form\Type\NewsType
arguments:
- "#app.my_service"
- "posted_by_name"
tags:
- { name: form.type }

You are both right.
#Muzafar and #jkucharovic, the question is when to use which...
As Bernard Schussek shows in Symfony Forms 101:
1 Don't pass Dynamic Data to constructor..
2 ... but use Custom Options instead
3 Do pass Global Settings to constructor (or services)

Related

How to get entity manager in form type

How could I get entity manager when building forms?
I would like to search results from the database and build the choices for choicetype.
I know I could use entitytype instead but in this situation I want to record string in database than an object.
And also I need to add some more options as well.
Thank you.
In Symfony 3.2 (and possibly others, I'm not sure about 3.1, but it is probably the same), the $this->createForm() method needs a string as the first parameter, and cannot take a form object.
Add a configureOptions method to your form class:
class YourFormType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'entityManager' => null,
]);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Entity Manager is set in: $options['entityManager']
}
}
Then get the form in your controller like so, passing in the Entity Manager:
$form = $this->createForm(
YourFormType::class,
$yourEntity,
[
'entityManager' => $this->getDoctrine()->getManager(),
]
);

ZF2 nested data validation

I'm trying make to work my validation. I have data posted to controller in the format like this:
[
'property' => 'value',
'nested_property' => [
'property' => 'value',
// ...
]
]
I have divided fields/filters and form into different classes and just gather it together in the Form's controller that looks like that:
public function __construct($name, $options)
{
// ...
$this->add(new SomeFieldset($name, $options));
$this->setInputFilter(new SomeInputFilter());
}
But it doesn't work properly, looks like it just ignores nested array (or ignores everything). What have I missed?
Thank you.
You need to set up your inputfilter like the way you've setup your forms including the fieldsets if you use the InputFilter class.
So when you've got a structure like:
MyForm
1.1 NestedFieldset
1.2 AnotherFieldset
Your inputfilters need to have the same structure:
MyFormInputFilter
1.1 NestedFielsetInputFilter
1.2 AnotherFieldsetInputFilter
Some example code:
class ExampleForm extends Form
{
public function __construct($name, $options)
{
// handle the dependencies
parent::__construct($name, $options);
$this->setInputFilter(new ExampleInputFilter());
}
public function init()
{
// some fields within your form
$this->add(new SomeFieldset('SomeFieldset'));
}
}
class SomeFieldset extends Fieldset
{
public function __construct($name = null, array $options = [])
{
parent::__construct($name, $options);
}
public function init()
{
// some fields
}
}
class ExampleInputFilter extends InputFilter
{
public function __construct()
{
// configure your validation for your form
$this->add(new SomeFieldsetInputFilter(), 'SomeFieldset');
}
}
class SomeFieldsetInputFilter extends InputFilter
{
public function __construct()
{
// configure your validation for your SomeFieldset
}
}
So the important part of configuring your inputFilter for these situations is that you need to reuse the name of your fieldset when using: $this->add($input, $name = null) within your InputFilter classes.

How to use a custom FormType in Symfony 3

I m training but I'm under symfony 3
i have problem i get this error
Expected argument of type "string",
"Test\FrontBundle\Form\Type\SheetType" given
the code on SheetType.php is
<?php
namespace Test\FrontBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
class SheetType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name',null,array('label'=>'Titre de l\'album'))
->add('type')
->add('artist')
->add('duration')
->add('released', DateType::class)
;
}
}
and on my SheetController.php i do that form my controller
i dont know how i can solve this all time i try else i got error
public function createAction(Request $request)
{
$form = $this->createForm(new SheetType());
$form->handleRequest($request);
if($request->isMethod('post') && $form->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($form->getData());
$em->flush();
return $this->redirect($this->generateUrl('test_front_sheet_list'));
}
return $this->render('TestFrontBundle:Sheet:create.html.twig', array('form' => $form->createView()));
}
Since symfony 2.8 you have to pass a full qualified class name instance as argument when create a form or form builder, it does not take an instance of FormTypeInterface anymore.
see https://github.com/symfony/symfony/blob/2.8/UPGRADE-2.8.md
So you should use $form = $this->createForm(SheetType::class); instead.

Zend Framework 2 Custom elements using ServiceManager not work

I want create a custom element and use the short name for add the element into Form, using the new ServiceManager tecnique for ZF2 V.2.1+
I am try to copy the same sample of the zend documentation step to step but it not works.
When I use the service writting the short name, it raises a exception because service not found:
Zend\ServiceManager\Exception\ServiceNotFoundException
File:
Zend\ServiceManager\ServiceManager.php:456
Message:
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Test
I think I have all classes identically, see follows
This is my custom element:
namespace SecureDraw\Form\Element;
use Zend\Form\Element\Text;
class ProvaElement extends Text {
protected $hola;
public function hola(){
return 'hola';
}
}
This is my Module.php I have my invokable service be able to use short name:
class Module implements FormElementProviderInterface {
//Rest of class
public function getFormElementConfig() {
return array(
'invokables' => array(
'Test' => 'SecureDraw\Form\Element\ProvaElement'
)
);
}
}
In my form I use for add the element, the commented line works ok, but with short name not works:
$this->add(array(
'name' => 'prova',
//'type' => 'SecureDraw\Form\Element\ProvaElement',
'type' => 'Test', //Fail
));
In my action:
$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('SecureDraw\Form\UserForm');
$prova = $form->get('prova');
echo $prova->hola();
The problem is that the elements created via FormElementManager have to be created into init method instead __Construct method how it can see in this page.
The zend documentation is badly explained
Workaround:
In your own module, create the following two files:
FormElementManagerConfig with the invokables short names of your custom form elements;
Subclass Form\Factory and override getFormElementManager and pass the config to the FormElementManager constructor;
You then use your own Factory to create your Form, like this (you can pass a very rudimentary, e.g. empty array, or a more or less full-fledged $spec to $factory->createForm()):
$factory = new Factory();
$spec = array();
$form = $factory->createForm($spec);
FormElementManagerConfig.php:
class FormElementManagerConfig implements ConfigInterface
{
protected $invokables = array(
'anything' => 'MyModule\Form\Element\Anything',
);
public function configureServiceManager(ServiceManager $serviceManager)
{
foreach ($this->invokables as $name => $service) {
$serviceManager->setInvokableClass($name, $service);
}
}
}
MyFactory.php:
class Factory extends \Zend\Form\Factory
{
public function getFormElementManager()
{
if ($this->formElementManager === null) {
$this->setFormElementManager(new FormElementManager(new FormElementManagerConfig()));
}
return $this->formElementManager;
}
}

ZF2 Service Locator

I'm quite new to zf2 and I'm experimenting with it. I have a view helper and I need it to access a table object. In my controller I can run:
$this->getServiceLocator();
But ideally I would run this inside my view helper. Unfortunately, I can't seem to access it from within my view helper. I tried passing it through the constructor, configuring a factory method in module.config.php, but when I try that, Zend will no longer pass a tablegateway object into one of my model objects created from a service factory method in the module's Module.php file. This seems to be because it no longer calls the factory method, and opts to run instantiate without any parameters.
I'm not certain I understand why the view factory methods would affect a different set of factory methods with different names.
Can anyone tell me what is wrong with what I'm doing? I can provide more details, but at this point I'm unclear on what details are actually important without supplying the entire codebase.
Thanks.
Crisp does provide a valid answer to your question, but I would suggest to take it one step further. The injection of the service locator makes your view helper tightly coupled to the framework and service locator pattern and vulnerable because every piece of code inside your application can modify every service in the service locator.
There are reasons to inject your dependency directly, so you only depend on your dependencies and you're not implementing this anti-pattern anymore. Let's assume your view helper depends on MyModule\Model\MyTable, then the constructor of your view helper would just look like this:
namespace MyModule;
use MyModule\Model\MyTable;
use Zend\View\Helper\AbstractHelper;
class MyViewHelper extends AbstractHelper
{
protected $table;
public function __construct(MyTable $table)
{
$this->table = $table;
}
}
As you pointed out, you just inject your MyTable now:
namespace MyModule;
class Module
{
public function getViewHelperConfig()
{
return array(
'factories' => array(
'MyViewHelper' => function($sm) {
$sm = $sm->getServiceLocator(); // $sm was the view helper's locator
$table = $sm->get('MyModule_MyTable');
$helper = new MyModule\View\Helper\MyHelper($table);
return $helper;
}
)
);
}
}
Note that inside a view helper factory your service manager is the view helper's service manager and not the "main" one where the table is registered (see also a blog post of I wrote earlier). The $sm->getServiceLocator() solves this for you.
I'm not certain I understand why the view factory methods would affect a different set of factory methods with different names.
It's not, so there is probably a bug in your code. If above does not work, please provide some more details on your service manager configuration so I can update my answer.
One of the great advantages of above approach is you make unit testing really easy for your view helper. You can mock the table gateway and focus on the complete behaviour of your view helper.
use MyModule\View\Helper\MyHelper;
public function testHelperusesTable
{
$mock = $this->getMock('MyModule\Model\MyTable');
$helper = new MyHelper($mock);
// Test your $helper now
}
You can inject the service locator into your view helper from the view helper config in Module.php
// Application/Module.php
public function getViewHelperConfig()
{
return array(
'factories' => array(
'myViewHelper' => function ($serviceManager) {
// Get the service locator
$serviceLocator = $serviceManager->getServiceLocator();
// pass it to your helper
return new \Application\View\Helper\MyViewHelper($serviceLocator);
}
)
);
}
In your view helper
<?php
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper,
Zend\ServiceManager\ServiceLocatorInterface as ServiceLocator;
class MyViewHelper extends AbstractHelper
{
protected $serviceLocator;
public function __construct(ServiceLocator $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
}
While working in Zend Framework,we often need custom helper,that make our work easy, In zf1 accessing database model from helper was easy,but i got stuck that how to access database model for any table in Custom View Helper, but as i was needing it i get around through the problem in unprofessional way by creatina new db adapter object in the view, which was never good way, but recently i came to know through very interesting way to access the database adapter in the view helper and there i have to execute any query on any table, it may be not so Zend F2 way, but very simple and short way to solve the issue.
Here is my Model Example...
<?php
namespace Application\Model;
use Zend\Db\TableGateway\TableGateway;
class SlideImageSubTable {
protected $tableGateway;
public $adapter;
public function __construct(TableGateway $tableGateway) {
$this->tableGateway = $tableGateway;
$this->adapter = $this->tableGateway->getAdapter();
}
public function fetchAll() {
$resultSet = $this->tableGateway->select();
return $resultSet;
}
public function getSlideImageSub($id) {
$id = (int) $id;
$rowset = $this->tableGateway->select(array('id' => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
return $row;
}
public function getImageMenu($id) {
$id = (int) $id;
$rowset = $this->tableGateway->select(array('slide_image_id' => $id));
$rows = array_values(iterator_to_array($rowset));
if (!$rows) {
throw new \Exception("Could not find row $id");
}
return $rows;
}
public function saveSlideImageSub(SlideImageSub $slideImageSub) {
$data = array(
'slide_image_id' => $slideImageSub->slide_image_id,
'title' => $slideImageSub->title,
'description' => $slideImageSub->description
);
$id = (int) $slideImageSub->id;
if ($id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getSlideImageSub($id)) {
$this->tableGateway->update($data, array('id' => $id));
} else {
throw new \Exception('Form id does not exist');
}
}
}
public function deleteSlideImageSub($id) {
$this->tableGateway->delete(array('id' => $id));
}
}
Just look at the 'public $adapter' public variable. And in the constructor i am going to initialize it by calling $this->tableGateway->getAdapter(); method, getAdapter() is available thorugh gateway object.
Then in my controller action view, i have to assign it to any variable and pass that variable to view page. like this..
public function equitiesAction() {
$image_id = $this->params('id');
$result = $this->getTable('SlideImageSub')->getImageMenu($image_id);
$adapter = $this->table->adapter;
$view = new ViewModel(array(
'result' => $result,
'adapter' => $adapter,
));
return $view;
}
And in the view i pass the 'adapter' object to custom view like this..
<?php echo $this->GetMenuProducts( $this->adapter); ?>
Now in custom view i can use this database adapter object and create select query on any table.
Hope this will help someone, i look around for using database access in custom view helper but the configurations methods provided was not working for me.
Thanks
$this->getView()->getHelperPluginManager()->getServiceLocator();

Resources