I'm actually developping a personal project with Symfony2.
And i want to to do something but i don't know how to do that.
I have an entity Recette and in this entity i have a property ingredients
This ingredients property is a json_array type.
<?php
namespace sf2\RecetteBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Recette
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="sf2\RecetteBundle\Entity\RecetteRepository")
*/
class Recette
{
// ...
/**
* #var array
*
* #ORM\Column(name="ingredients", type="json_array")
*/
private $ingredients;
// ...
}
?>
In this json_array i just want to save a couple of information.
Ex :
["name":"potatoes","quantity":"5kg"]
Here you can find my Entity FormType :
class RecetteType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name','text',array('label' => "test","attr"=>array('class'=>'test')))
->add('completionTime')
->add('ingredients',
'collection',
array(
'type'=>'text',
'prototype'=>true,
'allow_add'=>true,
'allow_delete'=>true,
'options'=>array(
)
)
)
->add('preparation')
->add('recetteCategories')
->add('Ok','submit')
;
}
}
In my form i can add with jquery with any problem add an ingredient, but my problem is that i can't save the quantity information. i don't know how to display in my form two field instead one for an ingredient.
Currently when i save an ingredient i have this data in database :
["Potatoes"]
How i can display in my form two fields for an ingredient and how to save it in this format ?
["name":"potatoes","quantity":"5kg"]
Thanks.
Here is an example from doc How to Embed a Collection of Forms
Firstly you must create a Custom Form Field Type named IngredientType:
IngredientType
namespace Acme\RecetteBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class IngredientType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('quantity')
;
}
public function getName()
{
return 'ingredient';
}
}
services.yml
# src/Acme/RecetteBundle/Resources/config/services.yml
services:
acme_demo.form.type.ingredient:
class: Acme\RecetteBundle\Form\Type\IngredientType
tags:
- { name: form.type, alias: ingredient }
And change The field type in your collection to ingredient type.
RecetteType
->add('ingredients',
'collection',
array(
'type'=>'ingredient',
'prototype'=>true,
'allow_add'=>true,
'allow_delete'=>true,
'options'=>array(
)
)
)
Related
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 have entity User and entity Company.
Relations are Company (one) to User (many).
When I signup as a company I need both these objects to be created.
Both objects have same properties: $phone, $firstName, $lastName;
(for company those fields mean main contact person).
My registration form consists of 2 forms: user form and embedded company form.
Problem: Now when I render signup form I have duplicated fields:
phone, firstName, lastName rendered for each object.
Is there a way to combine them somehow and ask for those values from user only once but still to save them into database for both entities?
I've managed to solve this problem using event listeners on PRE_SUBMIT event:
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
/**
* Class CompanyRegFormType
* #package AppBundle\Form
*/
class UserFormType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstName', 'text')
->add('lastName', 'text')
->add('phone', 'text')
->add('company', new CompanyFormType())
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event){
$data = $event->getData();
$data['company']['phone'] = $data['phone'];
$data['company']['firstName'] = $data['firstName'];
$data['company']['lastName'] = $data['lastName'];
$event->setData($data);
}
);
}
/**
* #return string
*/
public function getName()
{
return 'app_user_registration';
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\User',
]);
}
}
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?