Same Table, Two entities (or class) in symfony - forms

I'd like to build two class that refer to the same table.
For example:
Table Invoice:
idInvoice, IdClient, idProduct.
I've got one entity that shows general information (idClient and idInvoice) and than I've got an entity that allows me to show a dynamically generated form (id Product).
For every invoice I need one or more product so I should be able to insert different row for different product.
How can I link this different entities on the same table?
When I submit the form I need to extract all data from Product information and all data from Client and then I need to insert on MySQL.
I have got ORDER TABLE and PRODUCT TABLE. They are in a one to many relationship (one order, more product.) I created two entities: Order and Product and these are the classes:
ORDER CLASS
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="ordini")
*/
class Ordine
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $idOrdine;
/**
*
* #ORM\OnetoMany(targetEntity="Prodotto", cascade={"persist"},
mappedBy="prodotto")
*
*/
protected $prodotti;
/**
* Get idOrdine
*
* #return integer
*/
public function getIdOrdine()
{
return $this->idOrdine;
}
public function __costruct()
{
$this->prodotti= new ArrayCollection();
}
public function setProdotti(ArrayCollection $prodotti)
{
$this->prodotti = $prodotti;
//return $this;
}
public function addProdotti(Prodotto $prod) {
$this->prodotti[] = $prod;
return $this;
}
public function addProdotto(Prodotto $prodotto) {
$this->prodotti[] = $prodotto;
$prodotto->setOrdine($this);
return $this;
}
public function getProdotti() {
return $this->prodotti;
}
}
PRODUCT CLASS
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="prodotto")
*/
class Prodotto
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $idProdotto;
/**
* #ORM\Column(type="string", length=100, nullable=TRUE)
*
*/
protected $modello;
/**
* #ORM\ManyToOne(targetEntity="Ordine", inversedBy="prodotti")
* #var AppBundle\Entity\Ordine
*/
protected $ordini;
/**
* Get idProdotto
*
* #return integer
*/
public function getIdProdotto()
{
return $this->idProdotto;
}
/**
* Set modello
*
* #param string $modello
* #return Prodotto
*/
public function setModello($modello)
{
$this->modello = $modello;
return $this;
}
/**
* Get modello
*
* #return string
*/
public function getModello()
{
return $this->modello;
}
}
Then I created a Form that allows me to create an order (invoice) and I can dynamically generate Product Field.
I should be on the right way but now it gives me an error:
Expected argument of type "Doctrine\Common\Collections\ArrayCollection",
"array" given
My OrderForm is:
namespace AppBundle\Form\Documentofiscale;
use Symfony\Component\Form\TextField;
use Symfony\Component\Form\Checkbox;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use AppBundle\Entity\Prodotto;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class FormDocumentoFiscale extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// $builder->add('idCliente');
//$builder->add('save', 'submit', array('label' => 'INSERISCI
DOCUMENTO'));
$builder->add('prodotti', 'collection', array(
'type' => new FormAggiuntaProdotti(),
'allow_add' => true,
'by_reference' => false,
)
);
$builder->add('save', 'submit', array('label' => 'INSERISCI
DOCUMENTO'));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Ordine',
));
}
public function getName()
{
return 'documento';
}
}
And my dynamically generated Form is:
namespace AppBundle\Form\Documentofiscale;
use Symfony\Component\Form\TextField;
use Symfony\Component\Form\Checkbox;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use AppBundle\Entity\Prodotto;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class FormAggiuntaProdotti extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('modello');
//// $builder->add('idCliente');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Prodotto',
));
}
public function getName()
{
return 'prodotto';
}
}

Related

Symfony, How to properly set up a form that uses multiple entities?

I have a user creation page that uses User entity form with Username and Email attributes.
I would like when creating a user to be able to choose the tools he will have access to. To do this, retrieve all the tools and display them in a checkbox. Thus, once the form has been validated, the user obtains a username, an email and the tools to which he has access.
In my User class I can add a tool from the Tool entity using the AddTool() method.
How can I integrate the tools into my user creation form? I don't see how to do I'm lost.
Class User :
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity(repositoryClass=UserRepository::class)
*/
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180)
*/
private $username;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #var string The hashed password
* #ORM\Column(type="string")
*/
private $password;
/**
* #ORM\ManyToMany(targetEntity=Tool::class, mappedBy="users", fetch="EAGER")
*/
private $tools;
/**
* #ORM\Column(type="string", length=125, unique=true)
*/
private $email;
public function __construct()
{
$this->tools = new ArrayCollection();
}
// SOME FUNCTIONS
/**
* #return Collection|Tool[]
*/
public function getTools(): Collection
{
return $this->tools;
}
public function addTool(Tool $tool): self
{
if (!$this->tools->contains($tool)) {
$this->tools[] = $tool;
$tool->addUser($this);
}
return $this;
}
public function removeTool(Tool $tool): self
{
if ($this->tools->removeElement($tool)) {
$tool->removeUser($this);
}
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
}
UserType :
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('username')
->add('email')
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
In Your UserType buildForm function add something like:
$builder
->add('username')
->add('email')
->add('Tools', EntityType::class, [
'class' => Tool::class,
'multiple' => true
])
;
And You need to create a function in Tool::class definition that will allow display it as a string:
#[Pure] public function __toString(): string
{
return ''.$this->getFullName();
}
It should allow You to select Tools entities during the generation of User forms.

How to extend register form by additional fields in FOSUserBundle and Symfony3

I'm learning Symfony3 framework. At now I've build project that contain two Entities: User and Catalog. Catalog entity is for additional Company data (company name, address, and so on) - it's like a business card. Single User (but not all) is connected only with one Business Card (Catalog entity) and that's why I've decided to use two separate entities. Some users have access e.g. to backend and other ones have possibility to add and manage their one Business Card.
I want to allow user for fill in his Company details while he is registering. I'm using FOSUserBundle for User entity. At now I have registration form working but I'm stuck and need Your help with embed my CatalogType form.
CatalogType form:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CatalogType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('address')
->add('city')
->add('province')
->add('postcode')
->add('telephone')
->add('fax')
->add('mobile')
->add('email')
->add('webpage')
->add('misc')
;
}
public function getParent()
{
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'app_user_registration';
}
}
User entity:
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User extends BaseUser
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var
*
* #ORM\OneToMany(targetEntity="Comment", mappedBy="user")
*/
private $comments;
/**
* #var
*
* #ORM\OneToOne(targetEntity="Catalog", mappedBy="user")
*/
private $catalog;
public function __construct()
{
parent::__construct();
$this->comments = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Add comment
*
* #param \AppBundle\Entity\Comment $comment
*
* #return User
*/
public function addComment(\AppBundle\Entity\Comment $comment)
{
$this->comments[] = $comment;
return $this;
}
/**
* Remove comment
*
* #param \AppBundle\Entity\Comment $comment
*/
public function removeComment(\AppBundle\Entity\Comment $comment)
{
$this->comments->removeElement($comment);
}
/**
* Get comments
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getComments()
{
return $this->comments;
}
/**
* Set catalog
*
* #param \AppBundle\Entity\Catalog $catalog
*
* #return User
*/
public function setCatalog(\AppBundle\Entity\Catalog $catalog = null)
{
$this->catalog = $catalog;
return $this;
}
/**
* Get catalog
*
* #return \AppBundle\Entity\Catalog
*/
public function getCatalog()
{
return $this->catalog;
}
}
Catalog entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Catalog
*
* #ORM\Table(name="catalog")
* #ORM\Entity(repositoryClass="AppBundle\Repository\CatalogRepository")
*/
class Catalog
{
// ... definition a lot of private variables ...
/**
* #var
*
* #ORM\OneToOne(targetEntity="User", inversedBy="catalog")
* #ORM\JoinColumn(name="user_id", nullable=true)
*/
private $user;
// ...
/**
* Set user
*
* #param \AppBundle\Entity\User $user
*
* #return Catalog
*/
public function setUser(\AppBundle\Entity\User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \AppBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
Services config file:
// app/config/services.yml
services:
app.form.registration:
class: AppBundle\Form\CatalogType
tags:
- { name: form.type, alias: app_user_registration }
When I'm trying to display register form under http://localhost:8000/register/
I'm getting an error:
Neither the property "name" nor one of the methods "getName()",
"name()", "isName()", "hasName()", "__get()" exist and have public
access in class "AppBundle\Entity\User".
I know where is the problem, but I don't know how to properly solve it so it would be great if somebody can help me where I should looking for solutions or how it should be solved.
Thanks.
CatalogType should not extend RegistrationFormType. It should be form type for AppBundle\Entity\Catalog.
You should make form type, that is based on User class (subclass of FOS\UserBundle\Model\User), and that embeds CatalogType form.
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('...', ...)
->add('tasks', CollectionType::class, array(
'entry_type' => CatalogType::class,
...
));
...
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User',
));
}
public function getBlockPrefix()
{
return 'app_user_registration';
}
}
class CatalogType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('address')
->add('city')
->add('province')
->add('postcode')
->add('telephone')
->add('fax')
->add('mobile')
->add('email')
->add('webpage')
->add('misc')
;
}
...
}
More on embedding forms: https://symfony.com/doc/current/form/form_collections.html
More on overriding FosUserBundle forms: http://symfony.com/doc/current/bundles/FOSUserBundle/overriding_forms.html

Symfony Entity Field Type where clause in Query Builder not working on One-To-Many self referencing association

I have an Entity called "InterestGroup" that has a self referencing association (adjacency list) in the form of properties "children" (one to many) and "parent" (many to one).
In setting up a form type for InterestGroup, I'm attempting to provide a select list for the property parent, that has choices of all 'top level' interest groups (those whose 'parent' property is null). When I add the the where clause and null parameter to the query_builder for the EntityType field, it always returns nothing, even when I have several top level (parent is null) interest groups persisted. If I remove the where clause it will return all InterestGroups in the table. I'm having a difficult time understanding why the where clause is not working.
This is the field in question:
->add('parent',EntityType::class,
array(
'placeholder' => 'Top Level (No Parent)',
'required' => false,
'class' => 'Common\ContentBundle\Entity\InterestGroup',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('ig')
->where('ig.parent = :n')
->setParameter('n',null)
->orderBy('ig.title', 'ASC');
},
'choice_label' => 'title'
)
)
The above will return an empty select menu. By removing the where clause and the setparameter, I get all the InterestGroup Entities including all those that have null parents.
Following is the Entity Class for InterestGroup
<?php
namespace Common\ContentBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* InterestGroup
*
* #ORM\Table(name="interest_group")
* #ORM\Entity(repositoryClass="Common\ContentBundle\Repository\InterestGroupRepository")
*/
class InterestGroup
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255, unique=true)
* #Assert\NotBlank(message="This is a required field.")
*/
private $title;
/**
* #ORM\OneToMany(targetEntity="InterestGroup", mappedBy="parent")
*/
private $children;
/**
* #ORM\ManyToOne(targetEntity="InterestGroup", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
private $parent;
/**
* #Gedmo\Slug(fields={"title"})
* #ORM\Column(length=128, unique=true)
*/
private $slug;
// ...
/**
* #ORM\ManyToMany(targetEntity="Product", mappedBy="interestGroups")
*/
private $products;
/**
* InterestGroup constructor.
*/
public function __construct()
{
$this->children = new ArrayCollection();
$this->products = new ArrayCollection();
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* #param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* #return mixed
*/
public function getSlug()
{
return $this->slug;
}
/**
* #return mixed
*/
public function getChildren()
{
return $this->children;
}
/**
* #param mixed $children
*/
public function setChildren($children)
{
$this->children = $children;
}
/**
* #return mixed
*/
public function getParent()
{
return $this->parent;
}
/**
* #param mixed $parent
*/
public function setParent($parent)
{
$this->parent = $parent;
}
/**
* #return mixed
*/
public function getProducts()
{
return $this->products;
}
}
And the Form Type Class:
<?php
namespace Common\ContentBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
class InterestGroupType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title',TextType::class)
->add('parent',EntityType::class,
array(
'placeholder' => 'Top Level (No Parent)',
'required' => false,
'class' => 'Common\ContentBundle\Entity\InterestGroup',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('ig')
->where('ig.parent = :n')
->setParameter('n',null)
->orderBy('ig.title', 'ASC');
},
'choice_label' => 'title'
)
)
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Common\ContentBundle\Entity\InterestGroup'
));
}
}
Thanks in advance!
Check you code here:
return $er->createQueryBuilder('ig')
->where('ig.parent = :n') // <---
->setParameter('n',null) // <---
->orderBy('ig.title', 'ASC');
It is the same to:
... WHERE ig.parent = NULL ...
So, this query always returns null dataset.
Right code:
return $er->createQueryBuilder('ig')
->where('ig.parent IS NULL')
->orderBy('ig.title', 'ASC');
Use IS NULL for checking null values.
This problem is related to what is "=null" and " IS NULL"

Symfony2 (2.7) Form Entity Data Transformer

I'm trying to customize a selection list's text while using the entity's ID. This is because I want the list options to be specific to the authenticated user. The database text values are Full Name, By City and State, and Anonymous, but I want it to actually display the user's full name (John Smith), User in Denver, CO, and Anonymous. I'm attempting to use a view data transformer to achieve this, but with no luck. I'd rather not use Javascript to achieve this if possible.
Here's my main form type:
<?php
namespace Members\MessagesBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\SecurityContext;
class MessageType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('viewability', 'viewability_entity', array(
'class' => 'MessagesBundle:Viewability',
'property' => 'name',
'required' => true,
))
->add('body', new MessageBodyType())
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Members\MessagesBundle\Entity\Message',
));
}
/**
* #return string
*/
public function getName()
{
return 'members_messages_message';
}
}
Here's my custom form type for Viewability (the entity which I would like to transform):
<?php
namespace Members\MessagesBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\SecurityContext;
use Members\MessagesBundle\Form\DataTransformer\MessageNameTransformer;
class ViewabilityType extends AbstractType
{
private $context;
/**
* #param SecurityContext $context
*/
public function __construct(SecurityContext $context)
{
$this->context = $context;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new MessageNameTransformer($this->context);
$builder->addViewTransformer($transformer);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'invalid_message' => 'The selected issue does not exist',
));
}
/**
* #return string
*/
public function getParent()
{
return 'entity';
}
/**
* #return string
*/
public function getName()
{
return 'viewability_entity';
}
}
Here's my service which defines the Viewability Type:
members.messages.form.type.viewability_entity:
class: Members\MessagesBundle\Form\ViewabilityType
tags:
- { name: form.type, alias: viewability_entity }
arguments: [#security.context]
Here's my Viewability Entity:
<?php
namespace Members\MessagesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity()
*/
class Viewability
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
public function __construct()
{
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
}
Finally, here's my data transformer:
<?php
namespace Members\MessagesBundle\Form\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface;
use Members\MessagesBundle\Entity\Viewability;
use Symfony\Component\Security\Core\SecurityContext;
class MessageNameTransformer implements DataTransformerInterface
{
private $user;
/**
* #param SecurityContext $context
*/
public function __construct(SecurityContext $context)
{
$this->user = $context->getToken()->getUser();
}
/**
* #param Viewability|null $viewability
* #return string
*/
public function transform($viewability)
{
if (null === $viewability) {
return '';
}
if($viewability === 'Full Name')
return sprintf('%s %s', $this->user->getInfo()->getFirstName(), $this->user->getInfo()->getLastName());
if($viewability === 2)
return sprintf('Lawyer in %s, %s', $this->user->getInfo()->getAddress()->getCity(), $this->user->getInfo()->getAddress()->getState());
if($viewability === 3)
return 'Anonymous';
}
/**
* #param Viewability $viewability
* #return Viewability
*/
public function reverseTransform($viewability)
{
return $viewability;
}
}
The data passed into transform() always seems to be null or "" (empty string).
Thanks for any help.
So I ended up taking a different approach to solving this. Originally I was trying to transform data coming from an entity. Fortunately this entity didn't really need to be a database entity after all and a simple choice type sufficed. This doesn't solve the specific issue of transforming an entity list, but it allows me to customize the drop down list values.
The viewability entity was removed and the relationship in the Message entity was changed to an integer field.
My main type is now as follows:
class MessageType extends AbstractType
{
private $user;
public function __construct($user)
{
$this->user = $user;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('body', new MessageBodyType())
->add('viewability', 'choice', array(
'choices' => array(
1 => $user->getFirstName(),
2 => $user->getAddress()->getCity(),
3 => 'Anonymous',
),
'multiple' => false,
'label' => 'Send Message As',
'data' => 0,
))
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Members\MessagesBundle\Entity\Message',
));
}
/**
* #return string
*/
public function getName()
{
return 'members_messages_message';
}
}

Symfony2 embed form with file into another form

I have an Item entity and an Image entity, which a OneToOne relation between them.
I also have an ItemType and ImageType forms.
Until now when I have a situation like this one I use the two forms separately (rendering them into a single html form) and setting the relation inside the controller or the form handler. Is there a -symfony- way to embed the ImageType form into the ItemType one?
A little code maybe can help.
Item:
namespace Company\ItemBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use NewZaarly\ImageBundle\Entity\Image;
/**
* #ORM\Table()
* #ORM\Entity()
*/
class Item
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
private $id;
/**
* #ORM\Column(type="text")
*/
private $title;
/**
* #var \NewZaarly\ImageBundle\Entity\ImageItem
* #ORM\OneToOne(targetEntity="NewZaarly\ImageBundle\Entity\ImageItem", mappedBy="item", cascade={"persist", "merge", "remove"})
*/
private $image;
//other fields. Setters and getters
}
Image:
<?php
namespace Company\ImageBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* #ORM\Table()
* #ORM\Entity()
* #ORM\HasLifecycleCallbacks
*/
class Image
{
/**
* #Assert\File(maxSize = "2M", mimeTypes = {"image/png", "image/jpg", "image/jpeg"})
*/
protected $file;
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="image", type="string", length=255, nullable=true)
*/
protected $image;
//other fields, setter and getters
}
ImageType
<?php
namespace Company\ImageBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class ImageType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('file', 'file', array('required' => true));
}
public function getName()
{
return 'image';
}
}
And the ItemType form
<?php
namespace Company\ItemBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Company\ImageBundle\Form\Type\ImageType;
class ItemType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title', 'text');
$builder->add('image', new ImageType()); // <-- this is what I'd like to do
}
public function getDefaultOptions(array $options)
{
return array('data_class' => 'Company\ItemBundle\Entity\Item');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Company\ItemBundle\Entity\Item'));
}
public function getName()
{
return 'item';
}
}
Doing it like this I get an error when binding the request because the image is an array an not an Image object, what is expected in the setImage method in the Item class.
My handle function is pretty simple:
public function handle(FormInterface $form, Request $request)
{
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
$item = $form->getData();
$this->itemManager->saveItem($item);
return true;
}
}
return false;
}
Any idea? I don't like the first way I specified for doing it.
I had the same problem and I solved by adding data_class to embedded field, in my case I define the upload form as service (app_document_upload)
//Document Class
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
//other fields here ...
->add('image', 'app_document_upload', array(
'data_class' => 'App\DocumentBundle\Entity\Document'
))
}