How does it changed this syntax in Symfony 3.0.4 - symfony-forms

This syntax work correctly in symfony2 but it changed in Symfony3.
How could I retrieve parameter $em in FormType with symfony3?
$em=$this->getDoctrine()->getManager();
$form=$this->createForm(EtMenusType($em),$menu);
class EtMenusType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function __construct($em)
{
$this->em=$em;
}

http://symfony.com/doc/current/book/forms#defining-your-forms-as-services
Your form type might have some external dependencies. You can define your form type as a service, and inject all dependencies you need.
You might want to use a service defined as app.my_service in your form type. Create a constructor to your form type to receive the service:
// src/AppBundle/Form/Type/TaskType.php
namespace AppBundle\Form\Type;
use App\Utility\MyService;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class TaskType extends AbstractType
{
private $myService;
public function __construct(MyService $myService)
{
$this->myService = $myService;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// You can now use myService.
$builder
->add('task')
->add('dueDate', null, array('widget' => 'single_text'))
->add('save', SubmitType::class)
;
}
}
Define your form type as a service.
# src/AppBundle/Resources/config/services.yml
services:
app.form.type.task:
class: AppBundle\Form\Type\TaskType
arguments: ["#app.my_service"]
tags:
- { name: form.type }

Related

Zend\Mvc\Controller\PluginManager::get was unable to fetch or create an instance for translate

I have written a controller plugin to get the MVC translator using ZF 2.5.
Here is my translate controller plugin
namespace Freedom\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\I18n\Translator\Translator;
/**
* Translate
*
*/
class Translate extends AbstractPlugin
{
/**
*
* #var Translator
*/
private $translator;
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
/**
* Translate message
* #param string $message
* #param string $textDomain
* #param string $locale
* #return string
*/
public function __invoke($message, $textDomain = 'default', $locale = null)
{
return $this->translator->translate($message, $textDomain, $locale);
}
/**
*
* #return Translator
*/
function getTranslator()
{
return $this->translator;
}
}
and is factory
namespace Freedom\Controller\Plugin\Service;
use Zend\ServiceManager\FactoryInterface;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Freedom\Controller\Plugin\Translate;
/**
* TranslateFactory
*
*/
class TranslateFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return new Translate($container->get('translator'));
}
public function createService(ServiceLocatorInterface $container)
{
return $this($container->getServiceLocator(), Translate::class);
}
}
and finally in my module.config
'controller_plugins' => [
'factories' => [
'checkRedirect' => 'Freedom\Controller\Plugin\Service\CheckRedirectFactory',
'translate' => 'Freedom\Controller\Plugin\Service\TranslateFactory',
],
],
The problem I have is that I am getting this error and I can't understand why.
Zend\Mvc\Controller\PluginManager::get was unable to fetch or create an instance for translate
As you can see I have registered the plugin in my module.config but the plugin manager can't find it. I have checked that the controller_plugins key exists in the config and that my namespacing is correct. I also have another plugin called checkRedirect that produces the same error.
I simply can't figure out what is going on, please can someone tell me what I have missed, many thanks.
I finally found the problem, I was calling the plugin from the controllers constructor which does not work. Calling from an action, everything ok.
please try this, may be it help you.
I think all is ok, but you need to update this,
'controller_plugins' => [
'factories' => [
'checkRedirectPlugin' => 'Freedom\Controller\Plugin\Service\CheckRedirectFactory',
'translatePlugin' => 'Freedom\Controller\Plugin\Service\TranslateFactory',
],
'aliases' =>[
'checkRedirect' => 'checkRedirectPlugin',
'translate' => 'translatePlugin'
]
],
now use aliases, you can now access from your controller as,
$this->translate()

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 pass parameter to FormType constructor from controller

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)

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.

how to inject class dependency in Yii2 configuration?

I am learning Yii2. Here is one situation I have not googled the answer.
I register a component called scraper in config/console.php's $config['components'] array,
this scraper class has a public property $_client which is a Goutte\Client class.
I tried to use the following way to set up scraper component, but it is not working, Yii2 did not instantiate $_client as a Goutte\Client object.
$config = [
'scraper' => [
'class' => 'app\models\Scraper',
'_pageSize' => 10,
'_client' => [ //not working. can not instantiate this property as an object
'class' => 'Goutte\Client'
],
],
//...
]
Question: What would be working way to inject the dependency in the configuration?
Yii2 will not instantiate objects beyond the first level in your config array. In other words, scraper will get instantiated as an object, but it's property _client will be instantiated as an array ['class' => 'Goutte\Client'].
You should implement this logic yourself:
class Service extends Component
{
private $_client = null;
public $clientClass;
public function getClient()
{
if (null !== $this->_client) {
return $this->_client;
}
$this->_client = new $clientClass;
return $this->_client;
}
}
Alternatively, you can register Goutte\Client as a separate component, then Yii will properly instantiate it.
UPDATE:
To clarify, instantiating objects from config is done with yii\base\Configurable interface which is implemented in yii\base\Object class. Eventually, this implementation executes Yii::configure:
public static function configure($object, $properties)
{
foreach ($properties as $name => $value) {
$object->$name = $value;
}
return $object;
}
As you see, all properties will be assigned their respective values, so _client will become an array, not an object.
Found another approach in the guide itself: The property targets of the class yii\log\Dispatcher can be initialized with a class names or an objects. To make it working as one expects the init method is overwritten:
/**
* {#inheritdoc}
*/
public function init()
{
parent::init();
foreach ($this->targets as $name => $target) {
if (!$target instanceof Target) {
$this->targets[$name] = Yii::createObject($target);
}
}
}
This allows configuration/initialization of the log component like this:
'log' => [
'class' => 'yii\log\Dispatcher',
'targets' => [
[
'class' => 'yii\log\FileTarget',
],
],
],
Note: targets is an array here. But it can be done with a single class/object as well.
So in your case this should be a solution:
namespace app\models;
class Scraper extends ActiveRecord // or extends from anything that actually implements yii\base\Configurable
{
public $_client;
/**
* {#inheritdoc}
*/
public function init()
{
parent::init();
if (!$this->_client instanceof Goutte\Client) {
$this->_client = Yii::createObject($this->_client);
}
}
}
btw: usually underscore prefix in variable names is used for private properties.

Resources