Symfony2: Inserting collection form data - symfony

There is a one-to-one relationship between User and Company entity.
When initializing (creating) the user's company, userID should be bind to Company user field as a foreign key. But instead of that, i get this error message:
Property "id" is not public in class
"Website\CompanyBundle\Entity\User". Maybe you should create the
method "setId()"?
Why Symfony wants to create new User when this form is about Company entity, and User entity is just a collection that should provide user's ID.
Here is my code:
Company.php entity:
namespace Website\CompanyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="Website\CompanyBundle\Entity\Repository\CompanyRepository")
* #ORM\Table(name="company")
*/
class Company
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToOne(targetEntity="User", inversedBy="company")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* #ORM\Column(type="string")
*/
protected $name;
}
CompanyType.php
class CompanyType extends AbstractType
{
private $security;
public function __construct(SecurityContext $security)
{
$this->security= $security;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$user = $this->securityContext->getToken()->getUser();
$builder
->add('user', new UserType($security))
->add('company_name')
->add('company_address')
...
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Website\CompanyBundle\Entity\Company'
));
}
public function getName()
{
return 'user';
}
}
UserRelationType.php
class UserRelationType extends AbstractType
{
private $user;
public function __construct(SecurityContext $security){
$this->user = $security->getToken()->getUser();
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id', 'hidden', array('data' => $this->user->getId()))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Website\CompanyBundle\Entity\User'
));
}
public function getName()
{
return 'user';
}
}
User.php entity
namespace Website\CompanyBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $name;
/**
* #ORM\OneToOne(targetEntity="Company", mappedBy="user")
*/
protected $company;
}

You map entity to form in UserRelationType. On save it try to set id on user entity. You must specify data transformer or use entity type if you need to select existed user.
If you want to set current user, better do it in event listener like pre_persist

your properties in your entities are protected but you have not created getters/setters for them. ( or you have not pasted your full code )
Therefore the form-builder is not able to access your user's properties.
At least the public function setId($id) in User.php is definitely missing!
That's why the exception is thrown saying:
Maybe you should create the method "setId()"?
Create getters and settes for every property using ...
app/console doctrine:generate:entities
or create them by hand...
User.php
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
return $this;
}
// ...

I solved this problem without adding collections and just using Doctrine transitive persistence like described in this page: https://doctrine-orm.readthedocs.org/en/latest/reference/working-with-associations.html#transitive-persistence-cascade-operations
CompanyController.php
public function createIndex(Request $request){
$user = $this->getId();
$company = new Company();
if($user instanceof User){
$company->setUser($user);
}
$request = $this->getRequest();
$createForm = $this->createForm(new CompanyType(), $company);
if('POST' === $request->getMethod())
{
$createForm->bindRequest($request);
if($createForm->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($company);
$em->flush();
}
}
}
Thank you all for your help

Related

How to create an entity with several dates in a single form with EasyAdmin

I am trying to develop my first symfony web-app and I have decided to use the bundle EasyAdmin.
In this web-app, I would like to define the following model : an Event with several dates.
In order to create this, I have create 2 entities with the help of the symfony console : Event and EventDate:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\EventRepository")
*/
class Event
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="App\Entity\EventDate", mappedBy="event", orphanRemoval=true)
*/
private $dates;
public function __construct()
{
$this->dates = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|EventDate[]
*/
public function getDates(): Collection
{
return $this->dates;
}
public function addDate(EventDate $date): self
{
if (!$this->dates->contains($date)) {
$this->dates[] = $date;
$date->setEvent($this);
}
return $this;
}
public function removeDate(EventDate $date): self
{
if ($this->dates->contains($date)) {
$this->dates->removeElement($date);
// set the owning side to null (unless already changed)
if ($date->getEvent() === $this) {
$date->setEvent(null);
}
}
return $this;
}
public function __toString(): String
{
return $this->name;
}
}
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\EventDateRepository")
*/
class EventDate
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="date")
*/
private $date;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Event", inversedBy="dates")
* #ORM\JoinColumn(nullable=false)
*/
private $event;
public function getId(): ?int
{
return $this->id;
}
public function getDate(): ?\DateTimeInterface
{
return $this->date;
}
public function setDate(\DateTimeInterface $date): self
{
$this->date = $date;
return $this;
}
public function getEvent(): ?Event
{
return $this->event;
}
public function setEvent(?Event $event): self
{
$this->event = $event;
return $this;
}
}
In order to be user-friendly, I would like to "customize" the form of an Event in order to allow the user to create in the same form the event and its dates.
In order to do this, I have define the Event entity's form like that:
easy_admin:
entities:
Event:
class: App\Entity\Event
form:
fields:
- {property: 'name'}
- {property: 'dates', type: 'collection'}
The render of the collection is right because I can add or remove a date:
But As you can see, the field that represent the EventDate is an edit text. I think it's because the field represent the EventDate class and not only the date attribute.
The aim is to have the date selector that I have if I add a new EventDate in EasyAdmin:
So the question is: How to custom EasyAdmin in order to add an Event and its dates in a single form?
Thank you for your help!
I found the way to do it.
I need to modify my yaml EasyAdmin file in order to introduce an entry_type:
easy_admin:
entities:
Event:
class: App\Entity\Event
form:
fields:
- {property: 'name'}
- {property: 'dates', type: 'collection', type_options: {entry_type: 'App\Form\EventDateForm', by_reference: false}}
Then, I have to create the EventDateForm class:
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class EventDateForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('date');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'App\Entity\EventDate'
));
}
}
I also need to update the $date attribut of my Event entity like this:
/**
* #ORM\OneToMany(targetEntity="App\Entity\EventDate", mappedBy="event", orphanRemoval=true, cascade={"persist", "remove"})
*/
private $dates;
The render is not very beautiful but it works:

Render a filtered checkbox collection in a form

I want to render a filtered collection as a checkbox list.
But i have trouble to get the collection shown. i get "Catchable Fatal Error: Object of class Doctrine\ORM\PersistentCollection could not be converted to string".
Below is my formtype:
class PropertyfilterType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('view', EntityType::class, [
'class' => Propsearch::class,
'choice_label' => 'propsearchviews',
'expanded' => true,
'multiple' => true
]);
}
This is my many-to-many entity
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
/**
*/
class Propsearch
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var Propsearchview[]|ArrayCollection
*
* #ORM\ManyToMany(targetEntity="App\Entity\Propview", cascade={"persist"})
* #ORM\JoinTable(name="propsearch_propview")
* #ORM\OrderBy({"title": "ASC"})
* #Assert\Count(max="4", maxMessage="Can only select 4 views")
*/
private $propsearchviews;
/**
* #var Propsearchfacility[]|ArrayCollection
*
* #ORM\ManyToMany(targetEntity="App\Entity\Propfacility", cascade={"persist"})
* #ORM\JoinTable(name="propsearch_propfacility")
* #ORM\OrderBy({"title": "ASC"})
* #Assert\Count(max="4", maxMessage="Can only select 4 facilities")
*/
private $propsearchfacilities;
public function getId(): ?int
{
return $this->id;
}
public function __construct()
{
$this->propsearchviews = new ArrayCollection();
$this->propsearchfacilities = new ArrayCollection();
}
/**
* #return Collection|Propsearchview[]
*/
public function getPropsearchviews(): Collection
{
return $this->propsearchviews;
}
public function addPropsearchview(Propsearchview $propsearchview): self
{
if (!$this->propsearchviews->contains($propsearchview)) {
$this->propsearchviews[] = $propsearchview;
}
return $this;
}
public function removePropsearchview(Propsearchview $propsearchview): self
{
if ($this->propsearchviews->contains($propsearchview)) {
$this->propsearchviews->removeElement($propsearchview);
}
return $this;
}
/**
* #return Collection|Propsearchfacility[]
*/
public function getPropsearchfacilities(): Collection
{
return $this->propsearchfacilities;
}
public function addPropsearchfacility(Propsearchfacility $propsearchfacility): self
{
if (!$this->propsearchfacilities->contains($propsearchfacility)) {
$this->propsearchfacilities[] = $propsearchacility;
}
return $this;
}
public function removePropsearchfacility(Propsearchfacility $propsearchfacility): self
{
if ($this->propsearchfacilities->contains($propsearchfacility)) {
$this->propsearchfacilities->removeElement($propsearchfacility);
}
return $this;
}
}
This is my original view entity.
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity()
* #ORM\Table(name="propview")
*
* Defines the properties of the Tag entity to represent the post tags.
*
* See https://symfony.com/doc/current/book/doctrine.html#creating-an-entity-class
*
* #author Yonel Ceruto <yonelceruto#gmail.com>
*/
class Propview
{
/**
* #var int
*
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string", length=191)
*/
private $title;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function __toString(): string
{
return $this->title;
}
}
So i want to show the collection of views as a checkbox list that has been added to the propsearch table in the form.
Thanks in advance!
Edit 2
Okay so i have the propsearchviews which has an colleciton from propviewtype. including the dataclass from propsearch.
I changed my propertyfiltertype to the following:
<?php
namespace App\Form;
use App\Entity\Propsearch;
class PropertyfilterType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('propsearchviews', CollectionType::class, [
'entry_type' => PropviewType::class,
'by_reference' => false,
]);
}
the propviewtype itself
namespace App\Form\Type;
use App\Entity\Propview;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class PropviewType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('propview', EntityType::class, [
'class' => Propview::class,
'choice_label' => 'title',
]);
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Propview::class,
));
}
}
and my html.twig file
<div class="col-12 col-md-4 mb-2">
{% for field in propertybuyform.propsearchviews %}
<div class="col-xs-4">
{{ form_widget(field) }}
{{ form_label(field) }}
</div>
{% endfor %}
</div>
You should use embedded form functionality to achieve this. Please refer to https://symfony.com/doc/current/form/form_collections.html to get idea of how it could be implemented.
Briefly describing your case - you should create PropsearchType which would render propsearchviews property as CollectionType, where 'entry_type' would be another custom form type that you should create - PropviewType, that should render your Propviews as checboxes.

Symfony - API Platform - File Upload

I'm trying to implement a file upload with API PLatform following the Documentation using Vich. But it's not working, precisely, the MediaObject is not hydrated by the file I send on my request.
I followed quite exactly the cookbook provided by API Platform but it doesn't seem that my form handle the request well, because it doesn't pass the constraint validation and I get this answer from the API:
{
"type": "https:\/\/tools.ietf.org\/html\/rfc2616#section-10",
"title": "An error occurred",
"detail": "file: This value should not be null.",
"violations": [
{
"propertyPath": "file",
"message": "This value should not be null."
}
]
}
My request is basic : header is multipart/form-data and I send a single and small file (78Ko):
"file" => image.jpg
When I dump my $request object, the file is in it, but the $form->getData() is not hydrated. Any idea why ?
Here is my MediaObject :
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Traits\updatedAt;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Entity\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ApiResource(iri="http://schema.org/MediaObject", collectionOperations={
* "get",
* "post"={
* "method"="POST",
* "path"="/media_objects",
* "controller"=CreateMediaObjectAction::class,
* "defaults"={"_api_receive"=false},
* },
* })
* #Vich\Uploadable
* #ORM\Entity(repositoryClass="App\Repository\MediaObjectRepository")
* #ORM\HasLifecycleCallbacks()
*/
class MediaObject
{
use updatedAt;
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var File|null
* #Assert\NotNull()
* #Vich\UploadableField(mapping="media_object", fileNameProperty="contentUrl")
*/
public $file;
/**
* #var string|null
* #ORM\Column(type="string", length=255, nullable=true)
* #ApiProperty(iri="http://schema.org/contentUrl")
*/
private $contentUrl;
public function getId(): ?int
{
return $this->id;
}
public function getFile(): ?string
{
return $this->file;
}
/**
* #param string $file
*
* #return MediaObject
* #throws \Exception
*/
public function setFile(string $file): self
{
$this->file = $file;
if (null !== $file) {
$this->updatedAt = new DateTimeImmutable();
}
return $this;
}
public function getContentUrl(): ?string
{
return $this->contentUrl;
}
public function setContentUrl(?string $contentUrl): self
{
$this->contentUrl = $contentUrl;
return $this;
}
}
Here is my Media ObjectType :
namespace App\Form;
use App\Entity\MediaObject;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;
class MediaObjectType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// Configure each fields you want to be submitted here, like a classic form.
->add('file', VichFileType::class, [
'label' => 'label.file',
'required' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => MediaObject::class,
'csrf_protection' => false,
]);
}
public function getBlockPrefix()
{
return '';
}
}
And here is my controller :
namespace App\Controller;
use ApiPlatform\Core\Bridge\Symfony\Validator\Exception\ValidationException;
use App\Entity\MediaObject;
use App\Form\MediaObjectType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Validator\ValidatorInterface;
final class CreateMediaObjectController extends AbstractController
{
private $validator;
private $doctrine;
private $factory;
public function __construct(RegistryInterface $doctrine, FormFactoryInterface $factory, ValidatorInterface $validator)
{
$this->validator = $validator;
$this->doctrine = $doctrine;
$this->factory = $factory;
}
/**
* #param Request $request
* #Route("media_objects", name="media")
*
* #return MediaObject
*/
public function mediaCreation(Request $request): MediaObject
{
$mediaObject = new MediaObject();
$form = $this->factory->create(MediaObjectType::class, $mediaObject);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->doctrine->getManager();
$em->persist($mediaObject);
$em->flush();
// Prevent the serialization of the file property
$mediaObject->file = null;
return $mediaObject;
}
// This will be handled by API Platform and returns a validation error.
throw new ValidationException($this->validator->validate($mediaObject));
}
}
I am using postman and symfony 4.x for this answer.
API URL: localhost/api/media_objects (localhost => baseURL)
Select form-data => Key should be file and select type File ( If
you hover to right side to input you will see drop down)
Choose file and post.
NOTE: you must have config/packages/vich_uploader.yaml with configurations like
vich_uploader:
db_driver: orm
mappings:
media_object:
uri_prefix: /media
upload_destination: '%kernel.project_dir%/public/media'
namer: Vich\UploaderBundle\Naming\UniqidNamer

symfony2 embedded form won't persist to database

I followed the documentation for this here: but could not get the example to persist to the database for the embedded form; the Plant class saved just fine. I am under the assumption that the persist and flush methods in the controller handle the persisting of both entities. Is this wrong to assume? Do I need to intercept it and set it manually in the controller before flush?
At any rate, here is my code:
Plant Entity:
<?php
/**
* #ORM\Entity(repositoryClass="Blogger\BlogBundle\Entity\Repository\PlantRepository")
* #ORM\Table(name="plant")
* #ORM\HasLifecycleCallbacks
*/
class Plant
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="array", nullable=true)
* #ORM\ManyToMany(targetEntity="Blogger\BlogBundle\Entity\Picture", inversedBy="plants", cascade={"persist"})
* #ORM\JoinTable(name="picture")
*/
protected $pictures;
//...
public function __construct()
{
$this->pictures = new ArrayCollection;
}
//...
/**
* Add pictures
*
* #param \Blogger\BlogBundle\Entity\Picture $pictures
* #return Plant
*/
public function addPicture(\Blogger\BlogBundle\Entity\Picture $pictures)
{
$pictures->addPlant($this);
$this->pictures[] = $pictures;
}
/**
* Remove pictures
*
* #param \Blogger\BlogBundle\Entity\Picture $pictures
*/
public function removePicture(\Blogger\BlogBundle\Entity\Picture $pictures)
{
$this->pictures->removeElement($pictures);
}
/**
* Get pictures
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPictures()
{
return $this->pictures;
}
}
Picture Entity:
<?php
namespace Blogger\BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="Picture")
*/
class Picture
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
//...
/**
* #ORM\Column(type="text")
*/
public $path;
/**
* #ORM\Column(type="array", nullable=true)
* #ORM\ManyToMany(targetEntity="Blogger\BlogBundle\Entity\Plant", mappedBy="pictures")
*/
private $plants;
/**
* Constructor
*/
public function __construct()
{
$this->plants = new \Doctrine\Common\Collections\ArrayCollection();
}
//...
/**
* Set path
*
* #param string $path
* #return Picture
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
return $this->path;
}
/**
* Add plants
*
* #param \Blogger\BlogBundle\Entity\Plant $plants
* #return Picture
*/
public function addPlant(\Blogger\BlogBundle\Entity\Plant $plants)
{
if (!$this->plants->contains($plants)) {
$this->plants->add($plants);
}
}
/**
* Remove plants
*
* #param \Blogger\BlogBundle\Entity\Plant $plants
*/
public function removePlant(\Blogger\BlogBundle\Entity\Plant $plants)
{
$this->plants->removeElement($plants);
}
/**
* Get plants
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPlants()
{
return $this->plants;
}
}
Plant Form:
<?php
namespace Blogger\BlogBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PlantForm extends AbstractType
{
public function __construct($em) {
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
//...
$builder->add('pictures', 'collection', array(
'type' => new PictureForm(),
'options' => array(
'data_class' => 'Blogger\BlogBundle\Entity\Picture'),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Blogger\BlogBundle\Entity\Plant',
));
}
public function getName()
{
return 'plant';
}
}
Picture Form:
<?php
namespace Blogger\BlogBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PictureForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
//...
$builder->add('path', 'textarea');
//...
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Blogger\BlogBundle\Entity\Picture',
));
}
public function getName()
{
return 'picture';
}
}
Plant Controller:
public function newAction(Request $request){
$plant = new Plant();
$image1 = new Picture();
$plant->getPictures()->add($image1);
$form = $this->createForm(new PlantForm($this->getDoctrine()->getManager()), $plant);
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()
->getEntityManager();
$em->persist($plant);
$em->flush();
return $this->redirect($this->generateUrl('route', array(
'id' => $plant->getId()
)));
}
}
return $this->render('Bundle:Plant:new.html.twig', array(
'form' => $form->createView()
));
}
I suspect I don't have my annotations for the database mapped correctly. When I open phpadmin, there are no relationships defined in the database.
I figured it out. It was indeed an error with my annotations. This question was actually answered in a post here. For my solution, it looked like this:
Plant entity:
class Plant
{
//..
/**
* #ORM\ManyToMany(targetEntity="Blogger\BlogBundle\Entity\Picture", inversedBy="plants", cascade={"persist"})
* #ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
protected $pictures;
/**
* Constructor
*/
public function __construct()
{
$this->pictures = new ArrayCollection();
}
public function addPicture(\Blogger\BlogBundle\Entity\Picture $pictures)
{
$pictures->addPlant($this);
$this->pictures[] = $pictures;
}
//..
}
class Picture
{
//..
/**
* #ORM\ManyToMany(targetEntity="Blogger\BlogBundle\Entity\Plant", mappedBy="pictures", cascade={"persist", "remove"})
*/
private $plants;
/**
* Constructor
*/
public function __construct()
{
$this->plants = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addPlant(\Blogger\BlogBundle\Entity\Plant $plants)
{
if (!$this->plants->contains($plants)) {
$this->plants->add($plants);
}
}
//..
}
Controller:
public function newAction(Request $request){
$plant = new Plant();
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()
->getEntityManager();
$em->persist($plant);
$em->flush();
return $this->redirect($this->generateUrl('BloggerBlogBundle_plant_library_show', array(
'id' => $plant->getId()
)));
}
}
return $this->render('BloggerBlogBundle:Library:new.html.twig', array(
'form' => $form->createView()
));
}
Maybe a useful note to others: this mapping created a new table called plant_picture which holds the association. I was surprised to learn that a "picture" column was NOT added to the Plant entity at all.

How to access one to one embedded form symfony2

I used symfony 2 only few days.
I got two entities and i want to create one form and save data from this form into database.
UserDetails and Contract have relations OneToOne.
I embed a form of Contract into UserDetails form (form apper on web) but when i set some data into form and click button "save" i get a error. as i notice a "try" to assign an array instead of Contract i don't know how to access this new contract entite in Controler.
example ERRORS:
ContextErrorException: Catchable Fatal Error: Argument 1 passed to Leave\DatabaseBundle\Entity\UserDetails::setContract() must be an instance of Leave\DatabaseBundle\Entity\Contract, array given, called in /var/www/nowyUrlop/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php on line 360 and defined in /var/www/nowyUrlop/src/Leave/DatabaseBundle/Entity/UserDetails.php line 165
at UserDetails->setContract(array('start_contr' => object(DateTime), 'end_contr' => object(DateTime), 'hours_per_week' => '6')) in /var/www/nowyUrlop/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php line 360
UserDetails entity:
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Annotations\AnnotationReader;
use Leave\DatabaseBundle\Entity\User;
/**
* #ORM\Entity
* #ORM\Table(name="userDetails")
*/
class UserDetails {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToOne(targetEntity="Contract", mappedBy="user_details", cascade={"persist"})
*/
protected $contract;
/**
* #ORM\OneToOne(targetEntity="User", inversedBy="userDetails")
* #ORM\JoinColumn(name="user_id", nullable = true, referencedColumnName="id")
* */
protected $user;
Contract Entity:
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="contract")
*/
class Contract {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="date")
*/
protected $start_contr;
/**
* #ORM\Column(type="date")
*/
protected $end_contr;
/**
* #ORM\Column(type="integer")
*/
protected $type ;
/**
* #ORM\Column(type="integer")
*/
protected $hours_per_week;
/**
* #ORM\OneToOne(targetEntity="UserDetails", inversedBy="contract")
* #ORM\JoinColumn(name="user_details_id", nullable = true, referencedColumnName="id")
* */
protected $user_details;
public function setUserDetails(\Leave\DatabaseBundle\Entity\UserDetails $userDetails = null)
{
$this->user_details = $userDetails;
return $this;
}
/**
* Get user_details
*
* #return \Leave\DatabaseBundle\Entity\UserDetails
*/
public function getUserDetails()
{
return $this->user_details;
}
UserDetals Form:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Leave\DatabaseBundle\Form\Type\ContractFormType;
class UserDetailsFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('exp', 'text')
->add('total_leave', 'text')
->add('days_left', 'text')
->add('contract', new contractFormType())
->add('save', 'submit');
}
public function getName()
{
return 'userDetails';
}
}
Contract Form:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class ContractFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('start_contr', 'date', array('widget' => 'single_text'))
->add('end_contr', 'date', array('widget' => 'single_text'))
->add('hours_per_week', 'text');
}
public function getName()
{
return 'contractForm';
}
}
Controller:
public function editUserAction(Request $request) {
$user = $this->get('security.context')->getToken()->getUser();
$userDetails = new UserDetails();
$form = $this->createForm(new UserDetailsFormType(), $userDetails);
$userDetails->setUser($user);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($userDetails);
$em->flush();
return $this->render('LeaveEmployeeBundle:Employee:editUser.html.twig', array(
'formEditUser' => $form->createView(),
'userDetails' => $userDetails,
'user' => $user
));
}
return $this->render('LeaveEmployeeBundle:Employee:editUser.html.twig', array(
'formEditUser' => $form->createView()
));
}
You have to pass data_class as second parameter.
In your UserDetals Form: do the following
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Leave\DatabaseBundle\Form\Type\ContractFormType;
class UserDetailsFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('exp', 'text')
->add('total_leave', 'text')
->add('days_left', 'text')
->add('contract', new contractFormType(), array(
'data_class' => 'Leave\DatabaseBundle\Entity\Contract')
)
->add('save', 'submit');
}
public function getName()
{
return 'userDetails';
}
}

Resources