I have 2 entities A and B that share common fields, I used a trait to setup those common fields based on (Doctrine inheritance for entities common fields) because I don't want to use a MappedSuperClass.
Setting up a restful post route for entity B, I instantiate a FormBType which data_class maps to B::class, that extends FormCType (contains common fields and 'data_class' maps to nothing).
I tried to use the inherit_data approach with https://symfony.com/doc/current/form/inherit_data_option.html but I don't want that extra key/nested layer in my form (I want a flattened one).
My problem is that validation for the common fields which are in the trait using Assert aren't taken into account and form passes validation with empty strings.
class B {
use CTrait;
}
//trait that has the common fields with ORM mapping and Assert
trait CTrait {
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string")
* #ORM\Assert\Length(min="2")
*/
private $name;
}
//Common fields formType
class CType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
}
}
//Form using the common fields formType
class BType extends CType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => B::class,
'csrf_protection' => false,
]);
}
}
After further checking on Length I realized empty strings are considered valid values and it was still passing through validation using name: "" even though I had Assert\Length(min=2), after adding NotBlank the validation worked.
Related
I wanted to set the password field in registration form hidden since I don't need the password in my first step registration. The field is right now not showing since I have overridden the form, But still I could not submit the form because it says password should not be blank.
So I have tried this and its not working
class RegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstname')
->add('lastname')
->add('username')
->add('plainPassword',HiddenType::class)
->add('sex')
->add('email')
->add('city')
->add('zip_code')
->add('age');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class'=>'AppBundle\Entity\User'
]);
}
public function getBlockPrefix()
{
return 'app_bundle_registration_type';
}
}
Any help would be appreciated.
Thanks.
$builder
->add('password', HiddenType::class, [
'required' => false,
'validation_groups' => false
]);
You have a third parameter to provide options, you can use this to make it optional.
To modify the NotBlank constraint you could override the FOSUserBundle/Resources/config/validation.xml. Copy it to MyBundle/Resources/config/validation.xml and make changes you want.
So however managed to solve this for now with the help of OlivierC!
So I used life cycle callbacks to persist the data in the entity.
http://symfony.com/doc/current/doctrine/lifecycle_callbacks.html
Entity:
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* #ORM\PrePersist()
*/
public function setPassword($password)
{
$this->password = 'abcdef';
}}
Now it doesn't show as blank and for registration with password, I would need to update this field.
I have created a FormType that get's a data_class set in the defaults in the configureOptions class. It looks a bit like this
<?php
namespace tzfrs\AppBundle\Form\Type;
use tzfrs\AppBundle\Model\Car;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class CarType
* #package tzfrs\AppBundle\Form\Type
*/
class CarType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('specification', ChoiceType::class, [
'choices' => [1 => 1, 2 => 2, 3 => 3]
]);
}
/**
* #inheritdoc
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Car::class
]);
}
}
When I now create the form like this
$carForm = $this->createForm(CarType::class, $car);
and want to use it I get the following error:
The value of type "object" cannot be converted to a valid array key.
I think the reason for this, is because my Car class has a property specification with getters/setters and therefore returns a Specification object. However, my Specification object has a __toString() method.
Car.php
/**
* #var Specification
*/
protected $specification;
public function getSpecification(): Specification
{
return $this->specification;
}
public function setSpecification(Specification $specification): Car
{
$this->specification = $specfification;
return $this;
}
Specification.php
/**
* Returns a string implementation of the current class
*
* #return string
*/
public function __toString(): string
{
return sprintf(
'HP: %s,CCM: %s',
$this->getHorsePower(),
$this->getCapacity()
);
}
How would I achieve overriding my choices for a form field which uses a custom Model? I can't use the EntityType because it's not a Doctrine Model.
When I build my form like this
$carForm = $this->createForm(Car::class);
then it works, but I need the Car object inside the Form, because of another field I'm adding in the buildForm method
I have this configuration :
ModuleModelType
| - title
| - created
| - subform
| - DevField1
| - DevField2
| - etc...
What I want to have is something like this :
ModuleModelType
| - title
| - created
| - subform
| | - DevField1
| | - DevField2
| | - etc...
But the problem is that i don't know what users will add to the form, as I'm creating a reusable bundle and I don't want name conflicts.
This is my code, how I can make this easily ?
I want a developer can create a class and append his own form in an existing form.The following existing code append the dev's form into the first one and can replace some existing keys if keys are the same.
Actually, I want to append all dev's fields into and subform to avoid this.
<?php
// Into a controller
/** #var ModuleInterface $instance */
$instance = ... ;
$form = $this->createForm(ModuleModelType::class);
$subform= $this->createForm(new FormType());
$instance->buildForm($subform);
$form->add('subform',$subform); // I want to make something like that
interface ModuleInterface
{
public function buildForm(Form &$form);
}
class Foo implements ModuleInterface
{
public function buildForm(Form &$builder)
{
$builder->add("DevField1", /* type */);
$builder->add("DevField2", /* type */);
}
}
class ModuleModelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('created')
->add('subform')
;
}
}
We will assume some facts :
You want to have an entity with "data" field
This "data" field must be an array
A form must be changed to handle this "data" field
Obviously, in the entity, we can load and save "data" informations, as an array
This behavior can be easily done passing the subform by constructor.
Your problem happen because you're fighting against Symfony, please considere a standard way :
Have an entity with "array" field type
Have two forms Type : First one to handle your entity, second one to handle the embedded form.
Use dependency injection to embed the right form. (Assume two services by Module : first one for the embedded form, second one for you.
Your entity : (Think to generate getters & setters with generate:doctrine:entities)
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Foo
*
* #ORM\Table(name="foo")
* #ORM\Entity(repositoryClass="AppBundle\Repository\FooRepository")
*/
class Foo
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="some_informations", type="string", length=255)
*/
private $someInformations;
/**
* #var string
*
* #ORM\Column(name="Data", type="string", length=255)
*/
private $data;
/**
* #var string
*
* #ORM\Column(name="data", type="array")
*/
private $data;
Your form :
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class FooType extends AbstractType
{
protected $dataFormName;
public function __construct($dataFormName)
{
$this->dataFormName = $dataFormName;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('someInformations')
->add('data', $this->dataFormName)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Foo'
));
}
}
Your subform :
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class SubformType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('content')
->add('some_data')
;
}
}
At this point, you should be able to generate your form with :
$foo = new Foo();
$form = $this->createForm('service_name_for_the_form_with_second_embedded', $foo);
Old answer :
In this case, you should assume that ModuleInterface extends FormTypeInterface.
To be used as FormType in a $form->add, your class should look like a form.
At this point, use ModuleInterface seems to be useless.
I can purpose that way for you :
<?php
// Into a controller
$instance = new Foo();
$form = $this->createForm(new ModuleModelType($instance));
class Foo extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add("DevField1");
$builder->add("DevField2");
}
}
class ModuleModelType extends AbstractType
{
protected $subform;
function __construct(FormTypeInterface $subform)
{
$this->subform = $subform;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('created')
->add('subform', $this->subform)
;
}
}
You can also use dependency injection to inject $instance through __construct.
Take care about naming convention for "Foo" class, should be FooType ;)
If you do something like that, your expected form will be OK.
Have a nice day,
Gaƫl
I hope you already read this careful: http://symfony.com/doc/current/book/forms.html
In your case, you can do by:
use path/of/your/formType/to/embed;
class ModuleModelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('created')
->add('subform', Foo::class)
;
}
}
Don't worry about interface. Interface just must to implement, I hope you already know that.
I dont know why you want add in controller, because you have already form class, you just need modify to make clean your code.
I you want subform as add by user, you can do by collection form embed: http://symfony.com/doc/current/cookbook/form/form_collections.html
Or you want by data (show form if submitted) : http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html
NOTE: Symfony ver 3.0 are different with 2.~.
I am using Symfony2 for a PHP project, but I have a question about Entity inheritance.
I have a database model that requires to make the difference between different types of employees.
Here is an extract of the modelisation :
Employee
civility
name
firstname
hired_at
Secretary extends Employee
employee [Employee entity]
roles [another entity]
Seller extends Employee
employee [Employee entity]
section [another entity]
I have to separate it because I have another entity, called Message, that each employees can send to other ones.
Message
author [Employee entity]
recipient [Employee entity]
title
content
sent_at
In my application, I would like to be able to create a new "Secretary" for instance, and set up its "Employee" properties in the same form, rather than creating the Employee entity then link it to the new Secretary one...
What is the proper way to do it with Symfony2 ?
I know that I could add the properties to the form and set the entities manually, but I really think there should be a cleaner way to do that...
Is it possible to use the FormBuilder ?
Quite easy in fact, I did not know it was possible to add another FormType as a field type in Symfony2.
The working way, just in case :
namespace MyAdminBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class SecretaryType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('employee', new EmployeeType(), array('label' => 'Employee', 'required' => true))
->add('password', 'password', array('label' => 'Password', 'required' => true))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MyCoreBundle\Entity\Secretary'
));
}
/**
* #return string
*/
public function getName()
{
return 'my_adminbundle_secretary';
}
}
I am trying to build a form for multiple entities. Let me first introduce some sample classes:
For clarity I do not show all annotations or abbreviate them, and I do not show the use or namespace commands.
/**
* The Entity class
* #ORM ... mapping to ORM
*/
class EntityA {
/**
* #var ModelArray
* #ORM\Column(name="...", type="object")
*/
private $modelArray;
// Getters and Setters, default constructor
}
/**
* An array Wrapper, keeping the array always unique, sorting it by criteria etc.
* #ORM(...)
*/
class ModelArray {
/**
* #var array<A>
*/
private $array;
// Getter, Adder, Remover, Constructor, other private logic
}
Notice especially that the class ModelArray stores only objects of a given Type A:
/**
* One more Model
*/
class A {
/**
* #var boolean
*/
private $bool;
/**
* #var string
*/
private $string;
// Getters, Setters
}
I chose this data structure, because I never need objects of A to exist outside the EntityA class, and to keep the logic out of my Entity, I chose to implement the ModelArray class in between. The EntityA class is persisted to a database, and with it the child objects.
Now I want to create a form, where I can edit the $->bool attributes of all A instances of an Array of EntityA at once.
So what I provide is array<EntityA>.
I would then proceed as follows:
//In a controller action
$data = array(
'as' => $arrayOfEntityA,
);
$form = $this->createForm(new GridType(), $data);
// Create view, render
My form type would look like this:
class GridType extends AbstractType {
public function getName() {
return 'a_grid';
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('as', 'collection', array(
'type' => new GridAType(),
));
}
}
And use this form type
class GridAType extends AbstractType {
public function getName() {
return 'a_grid_a';
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('modelArray', new ModelArrayType());
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'EntityA',
);
}
}
With the model array type
class ModelArrayType extends AbstractType {
public function getName() {
return 'model_array';
}
public function buildForm(FormBuilderInterface $builder, array $options) {
// ???
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'ModelArray',
);
}
}
Notice the // ???: I would like to only edit the boolean attribute of the As strored in the ModelArray, and I think the appropriate form type to continue would be a 'collection'. But I can't really find out how to use the array ($modelArray->array)here as a collection.
How should I do this?
An then, I'm not entirely sure if it is good practice to implement such a lot of form types just to achieve one usable form. Is there a different and better way?