Symfony 3: UniqueEntity(errorPath) not working - doctrine-orm

I have an entity called ClassSubject and you can see it bellow,
/**
* ClassSubject
*
* #ORM\Table(name="class_subject")
* #ORM\Entity(repositoryClass="PIE10Bundle\Repository\ClassSubjectRepository")
*
* #UniqueEntity( fields={"class", "subjects"},
* errorPath="subjects",
* message="This subject is already added"
* )
*/
class ClassSubject
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="Classes")
* #ORM\JoinColumn(name="class_id", referencedColumnName="id")
*/
private $class;
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="Subject")
* #ORM\JoinColumn(name="subject_id", referencedColumnName="id")
*/
private $subjects;
as you can see, I want to make this Unique Entity so I am following this guide.
Further I have added the followings to the top of the entity file,
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
After all when I submit data it still adds duplicating rows to the table. For an example,
if the table exists with a row like, class=clsA and subjects=subA and if I try to insert with same values, it inserts data with no validation/message "This subject is already added"
My controller is like below,
public function addTeacherSubjectAction( $id, Request $request )
{
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('PIE10Bundle:Users')->find($id);
$subjects = $em->getRepository('PIE10Bundle:Subject')->findAll();
$form = $this->createForm(ClassSubjectType::class,$subjects);
$form->handleRequest($request);
if( $form->isSubmitted() && $form->isValid() )
{
$allSubjects = $form['subject']->getData();
foreach( $allSubjects as $subject )
{
$subject_repo = $em->getRepository('PIE10Bundle:Subject')->find($subject->getId());
$teacherSubject = new TeacherSubjects();
$teacherSubject->setTeachers($user);
$teacherSubject->setSubjects($subject_repo);
$em->persist($teacherSubject);
$em->flush();
}
$this->addFlash('notice',
'Subjects added');
return $this->redirectToRoute('user_teachers');
}
return $this->render( 'PIE10Bundle:Form:layout_commom_form.html.twig',
array( 'title' => 'Add Subjects',
'form' => $form->createView() )
);
}
and the ClassSubjectType is like below,
class ClassSubjectType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('subject',
EntityType::class,
array('class' => 'PIE10Bundle:Subject',
'expanded' => true,
'multiple' => true,
'attr' => array( 'class' => 'form-control',
'style' => 'margin:5px 0;')));
$builder->add('Add Classes',
SubmitType::class,
array('attr' => array('class' => 'btn btn-primary',
'style' => 'margin:15px 0;')) );
}
}
So I need to know what is wrong with my code. Thanks in advance.

Related

upload not running with VichUploaderBundle

I would like to use a VichUploaderBundle for upload files in my symfony 3.2.3 project (PHP 5.6). I try lot of thing but nothing upload run. But the persistance layer run perfeclty with my database.
config.yml
knp_gaufrette:
stream_wrapper: ~
adapters:
fileupload_adapter:
local:
directory: %kernel.root_dir%/../web/
create: true
filesystems:
fileupload_fs:
adapter: fileupload_adapter
# VichUploaderBundle Configuration
vich_uploader:
db_driver: orm
twig: true
storage: gaufrette
mappings:
tgmedia_file:
uri_prefix: web
upload_destination: fileupload_fs
namer: vich_uploader.namer_origname
Entity
namespace MediaBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use TweedeGolf\MediaBundle\Model\AbstractFile;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ORM\Entity
* #Vich\Uploadable
* #ORM\HasLifecycleCallbacks
* #ORM\Table
*/
class FileUpload
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="tgmedia_file", fileNameProperty="imageName")
*
* #var File
*/
protected $imageFile;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
private $imageName;
/**
* #ORM\Column(type="datetime")
*
* #var \DateTime
*/
protected $updatedAt;
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #return File|null
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
*
* #return Product
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($image) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTimeImmutable();
}
return $this;
}
/**
* #param string $imageName
*
* #return FileUpload
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
return $this;
}
/**
* #return string|null
*/
public function getImageName()
{
return $this->imageName;
}
/**
* Set updatedAt
*
* #param \DateTime $updatedAt
*
* #return FileUpload
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* #return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
}
The formType
<?php
namespace MediaBundle\Form;
use MediaBundle\Entity\File;
use MediaBundle\Entity\FileUpload;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Vich\UploaderBundle\Form\Type\VichFileType;
use Vich\UploaderBundle\Form\Type\VichImageType;
class FileUploadType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('imageName')
->add('imageFile', VichImageType::class, [
'required' => false,
'allow_delete' => true,
'download_link' => true,
'mapped' => false,
'data_class' => null
])
->add('submit', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => FileUpload::class,
'csrf_protection' => false,
));
}
}
Controller
<?php
namespace MediaBundle\Controller;
use MediaBundle\Entity\File;
use MediaBundle\Entity\FileUpload;
use MediaBundle\Entity\Product;
use MediaBundle\Form\FileType;
use MediaBundle\Form\FileUploadType;
use MediaBundle\Form\ProductType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class MainController extends Controller
{
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$fileUploaded = new FileUpload();
$form = $this->createForm(FileUploadType::class, $fileUploaded, array(
'action' => $this->generateUrl('media_main')
));
$form->handleRequest($request);
if ($form->isSubmitted())
{
$fileUploaded->setUpdatedAt(new \DateTime());
$fileUploaded->setImageFile($form->get('imageFile')->getData());
$em->persist($fileUploaded);
$em->flush();
}
return $this->render('MediaBundle:main:index.html.twig', array(
'form' => $form->createView()
));
}
}
Where is my error ? What wrong ?
uri_prefix: /project/web/fileupload_fs
upload_destination: '%kernel.root_dir%/../web/fileupload_fs'
uri_prefix begins from www folder.
and change controller like that
if ($form->isSubmitted() && $form->isValid())
{
$em->persist($fileUploaded);
$em->flush();
}

Symfony2: How to add FormType

I have an Entity Product and I have an Entity File.
The relationship between those 2 is normally a ManyToMany Relation. But I need also a field Value "sorting" and "description", so I must declare my own JoinTable ProductHasFile. Now I want in my Form, that I can have a Product bute include the File FormTpye an not the ProductHasFile FormType.
Product Entity:
/**
* #ORM\Entity
* #ORM\Table(name="Product")
*/
class Product
{
/**
* #ORM\OneToMany(targetEntity="ProductHasFile", mappedBy="product", cascade={"persist"}))
*/
private $productHasFiles;
....
File Entity:
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class File
{
/**
* #ORM\OneToMany(targetEntity="ProductHasFile", mappedBy="file")
*/
private $productHasFiles;
...
And my own generated Entity ProductHasFile:
/**
* #ORM\Entity
* #ORM\Table(name="ProductHasFile")
*/
class ProductHasFile
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $sorting;
/**
* #ORM\Column(type="string", nullable=true)
*/
private $description;
/**
* #ORM\ManyToOne(targetEntity="Product", inversedBy="productHasFiles")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
/**
* #ORM\ManyToOne(targetEntity="File", inversedBy="productHasFiles")
* #ORM\JoinColumn(name="file_id", referencedColumnName="id")
*/
private $file;
When I now make my Formtype for Product:
class ProductType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
->add('productHasFiles', CollectionType::class, array(
'entry_type' => ProductHasFileType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
,
)
)
and my FormType for ProductHasFileType:
class ProductHasFileType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
/**
* #var $entityManager EntityManager
*/
$fileRepository = $options['fileRepository'];
$builder
->add('sorting', TextType::class)
->add('description', TextType::class)
->add('file', EntityType::class, array(
'class' => 'AppBundle\Entity\File',
'label' => 'file',
))
;
I get only a dropdwon for the File Entity. But I want to have my full File FormType which has the Uploadfield and other things, it looks like this:
class FileType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('description', TextType::class, array(
'label' => 'description',
))
->add('type', TextType::class, array(
'label' => 'type',
))
->add('file', \Symfony\Component\Form\Extension\Core\Type\FileType::class, array(
'label' => 'file',
))
;
}
Does anybody has a solution for this?
As explained in Symfony doc about how to embed forms this is as easy as
class ProductHasFileType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
/**
* #var $entityManager EntityManager
*/
$fileRepository = $options['fileRepository'];
$builder
->add('sorting', TextType::class)
->add('description', TextType::class)
->add('file', FileType::class) //where FileType is your own FormType
;
}
}
In ProductHasFileType you're adding your file like this:
->add('file', EntityType::class, array(
'class' => 'AppBundle\Entity\File',
'label' => 'file',
))
so you are adding your file as entity type wich logically renders a dropdown of all file entities.
What you need to do is pass your own FileType.To make sure you are using your own file type and not the default symfony one don't forget to add the correct use statement.
result:
use MyBundle\Form\Type\FileType;
class ProductHasFileType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('sorting', TextType::class)
->add('description', TextType::class)
->add('file', FileType::class)
;

Zend Framework 2 Doctrine 2 many to many entity relation issue

I have the following tables: films, categories, films_categories and the entities:
Film.php
<?php
namespace Admin\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="films")
*/
class Film{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
* #ORM\Column(length=11)
*/
private $id;
/**
* #ORM\Column(type="string")
*/
private $name;
/* .... */
/**
* #ORM\ManyToMany(targetEntity="Category")
* #ORM\JoinTable(name="films_categories",
* joinColumns={#ORM\JoinColumn(name="film_id", referencedColumnName = "id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id")})
*/
private $categories;
public function __construct(){
$this->categories = new ArrayCollection();
}
public function getCategoriesNames(){
$names = array();
foreach($this->categories as $category){
$names[] = $category->getName();
}
return $names;
}
public function getId(){
return $this->id;
}
public function setId($id){
$this->id = $id;
}
/* ... */
/**
* #return Collection
*/
public function getCategories(){
return $this->categories;
}
public function addCategories(Collection $categories){
foreach($categories as $category){
$this->categories->add($category);
}
}
public function removeCategories(Collection $categories){
foreach($categories as $category){
$this->categories->removeElement($category);
}
}
}
Category.php
<?php
namespace Admin\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="categories")
*/
class Category {
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
/* ... */
public function getId(){
return $this->id;
}
public function setId($id){
$this->id = $id;
}
/* ... */
}
What I want to do is create a form and the action to add a new film and assign a category to it. Here is the form I used:
FilmFieldset.php
<?php
namespace Admin\Form;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
use DoctrineORMModule\Stdlib\Hydrator\DoctrineEntity;
use Admin\Entity\Film;
class FilmFieldset extends Fieldset implements InputFilterProviderInterface{
protected $entityManager;
public function __construct($em){
parent::__construct('film');
$this->entityManager= $em;
$this->setHydrator(new DoctrineEntity($em,'Admin\Entity\Film'))
->setObject(new Film());
#$this->setAttribute('method','post');
#$this->setAttribute('class','standardForm');
$this->add(array(
'name' => 'id',
'type' => 'hidden'
));
/* ... */
$this->add(
array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'categories',
'attributes' => array(
'multiple' => 'multiple',
),
'options' => array(
'object_manager' => $em,
'target_class' => 'Admin\Entity\Category',
'property' => 'name',
'label' => 'Categories: ',
'disable_inarray_validator' => true
),
)
);
}
public function getInputFilterSpecification(){
return array(
/* .... */
'categories' => array(
'required' => true,
),
);
}
}
The FilmForm.php
<?php
namespace Admin\Form;
use Zend\Form\Form;
use Zend\Stdlib\Hydrator\ClassMethods;
use Admin\Entity\Film;
use Zend\InputFilter\InputFilter;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
class FilmForm extends Form{
public function __construct($em){
parent::__construct('filmForm');
$this->setAttribute('method','post')
->setAttribute('class','standardForm')
->setHydrator(new DoctrineHydrator($em,'\Admin\Entity\Film'))
->setInputFilter(new InputFilter());
/* I register the fieldset through a service and not directly here */
// $this->add(array(
// 'type' => new FilmFieldset($em),
// 'options' => array(
// 'user_as_base_fieldset' => true
// )
// ));
$this->add(array(
'name' => 'security',
'type' => 'Zend\Form\Element\Csrf'
));
$this->add(array(
'name' => 'submit',
'type' => 'submit',
));
$this->setValidationGroup(array(
'security',
'film' => array(
'categories',
)
));
}
}
The addAction:
public function addAction() {
$em = $this->getEntityManager();
$form = $this->getForm();
$film = new Film();
$form->bind($film);
if($request->isPost()){
$post = array_merge_recursive(
$request->getPost()->toArray(),
$request->getFiles()->toArray()
);
$form->setData($post);
if($form->isValid()){
$categories = array();
foreach($post['film']['categories'] as $categoryId){
$categories[] = $em->getRepository('Admin\Entity\Category')->find($categoryId);
}
$film->addCategories($categories);
$em->persist($film);
$em->flush();
}else{
// the form is not valid
}
}
The result is various errors and and ORMExcption with the message "Found entity of type on association Admin\Entity\Film#categories, but expecting Admin\Entity\Category"
Please help me out, I'm literally freaking out over this! Thank you :)
From what i found out from this is some thing like this that your entity is receiving Admin\Entity\Film#categories and in this part categories is some what a value. Where as your entity is expecting an object of type Admin\Entity\Film#categories.
To get over this you have to create a
public function SetCategory(Admin\Entity\Category Category)
{
$this->categories(or category or w/e your variable name is)= Category;
}
public function getCategory()
{
return $this->categories(or category or w/e your variable name is);
}
And then in your Action you have to pass object of Category to Film Entity in some thing like this
$Film->SetCategory($categoryObj);
Of course you have to set your business logic according to your part, but this error Should be removed by this apporach.
You need to define the FQCN for the targetEntity.
Change:
* #ORM\ManyToMany(targetEntity="Category")
to:
* #ORM\ManyToMany(targetEntity="Admin\Entity\Category")
This has to do with hydration and lazy loading. I'm no expert on this, but the #categories is a proxy object, and when you are going to save it, it's complaining because it needs to be an actual collection, not a proxy object to a collection.
If you did noobie-php's approach, you're re-attaching new category objects on the main object, so they're not proxies any more.
This is frustrating, as Doctrine was supposed to save a lot of the hassle of this, but it doesn't actually do what you'd naturally expect in many situations. I'd found a bug ticket on this some time ago but can't find it now - will add it to this if I can find it.

Using Zend Framework 2 + Doctrine2.3 with ObjectSelect and ManyToOne relations

I"m having a bit of trouble with using Doctrine's ObjectSelect on ManyToOne relations.
The relations I have below using ManyToMany work 100% adding and editing. My edit form is populated with the current selection without any problems.
The issue arises with the ManyToOne relations, it appears the form is not being populated with the current selection.
I have tried dumping the Task entity before I bind it to the form and it looks 100% right, all my relations are populated in the entity.
However after binding it, the form is being displayed without the current value selected.
Task entity:
/**
* #ORM\Entity
* #ORM\Table(name="tasks")
*/
class Task
{
/**
* #ORM\Id
* #ORM\Column(type="integer");
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
...
/**
* #ORM\ManyToOne(targetEntity="Category")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
...
/**
* #ORM\ManyToMany(targetEntity="ZDUser\Entity\User")
* #ORM\JoinTable(name="tasks_assigned_user_linker",
* joinColumns={#ORM\JoinColumn(name="task_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")}
* )
*/
protected $assignedUsers;
/**
* Initialize
*/
public function __construct()
{
$this->assignedUsers = new ArrayCollection();
}
/**
* We need a few getters and setters
*/
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
public function getCategory() {
return $this->category;
}
public function setCategory(Category $category) {
$this->category = $category;
}
public function getAssignedUsers() {
return $this->assignedUsers;
}
public function addAssignedUsers(Collection $users) {
foreach ($users as $user) {
$this->assignedUsers->add($user);
}
}
public function removeAssignedUsers(Collection $users) {
foreach ($users as $user) {
$this->assignedUsers->removeElement($user);
}
}
}
I'm using ManyToOne in most of my entities, I find this way a little easier and extensible going forward. I can just add addtional entities and link them to other ones without having to do relations on both sides.
Category Entity
/**
* #ORM\Entity
* #ORM\Table(name="task_categories")
* #property int $id
* #property string $name
*/
class Category
{
/**
* #ORM\Id
* #ORM\Column(type="integer");
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", nullable=false)
*/
protected $name;
...
/**
* Setters and getters we need
*/
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = (int) $id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}
User Entity:
/**
* #ORM\Entity
* #ORM\Table(name="users")
*/
class User implements UserInterface, ProviderInterface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
...
/**
* #ORM\ManyToMany(targetEntity="ZDUser\Entity\Group")
* #ORM\JoinTable(name="users_groups_linker",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="group_id", referencedColumnName="id")}
* )
*/
protected $groups;
/**
* Initialies the object
*/
public function __construct()
{
$this->groups = new ArrayCollection();
}
/* Getters and setters, we must define these for the implementation to work */
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = (int) $id;
}
/* Get and add groups */
public function getGroups()
{
return $this->groups;
}
public function addGroup(Group $group)
{
$this->groups->add($group);
}
Form code:
class TaskForm extends Form implements ObjectManagerAwareInterface
{
protected $objectmanager;
public function __construct(EntityManager $em)
{
// we want to ignore the name passed
parent::__construct('task');
$this->setHydrator(new DoctrineHydrator($em,'TaskList\Entity\Task'));
$this->setAttribute('method', 'post');
$this->add(array(
'name' => 'id',
'attributes' => array(
'type' => 'hidden',
),
));
$this->add(array(
'name' => 'subject',
'type' => 'Text',
'options' => array(
'label' => 'Subject',
),
));
$this->add(array(
'name' => 'category',
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'options' => array(
'label' => "Category",
'object_manager' => $em,
'target_class' => 'TaskList\Entity\Category',
'property' => 'name',
),
));
$this->add(array(
'name' => 'assignedUsers',
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'attributes' => array(
'multiple' => 'multiple',
),
'options' => array(
'label' => "Assigned To (User)",
'object_manager' => $em,
'target_class' => 'ZDUser\Entity\User',
'property' => 'email',
),
));
Controller for Edit & Add:
public function addAction()
{
$this->addedit();
// Grab form
$form = new TaskForm($this->getEntityManager());
// Grab any request we may have
$request = $this->getRequest();
// If it a post ...
if ($request->isPost()) {
$task = new Task();
$form->bind($task);
// Populate data
$form->setData($request->getPost());
// Check if the form is valid
if ($form->isValid()) {
// Setup some things we need
$task->setCreated(new \DateTime("now"));
// Save
$this->getEntityManager()->persist($task);
$this->getEntityManager()->flush();
// Redirect to list of tasks
return $this->redirect()->toRoute('tasklist');
}
}
return array(
'form' => $form
);
}
public function editAction()
{
$this->addedit();
// Get ID or redirect
$id = (int)$this->getEvent()->getRouteMatch()->getParam('id');
if (!$id) {
return $this->redirect()->toRoute('tasklist');
}
// Create a form
$form = new TaskForm($this->getEntityManager());
// Grab entity from doctrine
$task = $this->getEntityManager()->find('TaskList\Entity\Task', $id);
// Bind the form to the task
$form->bind($task);
// Check if we have a request and if its POST
$request = $this->getRequest();
if ($request->isPost()) {
// If it is, set the form data from the request
$form->setData($request->getPost());
// If the form is valid, bind the values
if ($form->isValid()) {
// Setup some things we need
$task->setLastUpdated(new \DateTime("now"));
// Flush the update
$this->getEntityManager()->flush();
// Redirect to list of tasks
return $this->redirect()->toRoute('tasklist');
}
}
return array(
'id' => $id,
'form' => $form,
);
}
I'm so sure I'm missing something really simple.
I had a similar problem, see this issue on the DoctrineORMModule GitHub for more info.
It's something to do with Doctrine not returning the correct ID field from metadata when Proxies Entities are loaded. The solutions are as follows:
Wait for official fix in Doctrine 2.4 (you can install 2.4-beta2 now!).
Subclass the ObjectSelect to force the use of the correct ID field (someone in the aforelinked issue did this).
Patch DoctrineModule with this:
--- doctrine/doctrine-module/src/DoctrineModule/Form/Element/Proxy.php 2013-03-11 17:49:55.406011600 -0300
+++ doctrine/doctrine-module/src/DoctrineModule/Form/Element/Proxy.php 2013-03-11 17:51:33.592710900 -0300
## -240,7 +240,10 ##
if (count($identifier) > 1) {
//$value = $key;
} else {
- $value = current($metadata->getIdentifierValues($value));
+ // Doctrine has a bug that makes the following not work,
+ // this is a horrible workaround until Doctrine 2.4 is released with a fix.
+ //$value = current($metadata->getIdentifierValues($value));
+ $value = $value->getId();
}
}
}
The $value->getId() relies on that method being available in the corresponding entity. This isn't recommended but a quick fix.
Probably not the right way? I posted my idea to their ML :)
Probably not the right way? I posted my idea to their ML :)
diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
index cba525a..2f62375 100644
--- a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
## -667,7 +667,17 ## class ClassMetadataInfo implements ClassMetadata
return $id;
}
- $value = $this->reflFields[$this->identifier[0]]->getValue($entity);
+
+ /**
+ * NK: First try use the getter, in the case of a proxied object, the reflection is not going to work
+ * as the proxied object does not have any properties
+ */
+ $getter = 'get' . ucfirst($this->identifier[0]);
+ if (method_exists($entity, $getter)) {
+ $value = $entity->$getter();
+ } else {
+ $value = $this->reflFields[$this->identifier[0]]->getValue($entity);
+ }

symfony2 : unable to save parent id in embedded object

I'm trying tu use my first embedded form (SF2.1 + Doctrine2)
the entities :
<?php
namespace Chris\BabelBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
*/
class Needs
{
/**
* #ORM\GeneratedValue
* #ORM\Id
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Projects")
* #Assert\NotBlank()
*/
private $relatedproject;
/**
* #ORM\ManyToOne(targetEntity="Activities")
* #Assert\NotBlank()
*/
private $relatedactivity;
/**
* #ORM\ManyToOne(targetEntity="Jobs")
* #Assert\NotBlank()
*/
private $relatedjob;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank()
*/
private $firstname;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank()
*/
private $lastname;
/**
* #ORM\ManyToOne(targetEntity="Experiences")
* #Assert\NotBlank()
*/
private $relatedexperience;
/**
* #ORM\ManyToOne(targetEntity="Plants")
* #Assert\NotBlank()
*/
private $maintrainingplant;
/**
* #ORM\Column(type="date")
* #Assert\NotBlank()
*/
private $trainingneedstart;
/**
* #ORM\Column(type="date")
* #Assert\NotBlank()
*/
private $readytowork;
/**
* #var ArrayCollection $trainings
*
* #ORM\OneToMany(targetEntity="Trainings", mappedBy="training", cascade={"persist", "remove", "merge"})
*/
private $trainings;
/**
* Constructor
*/
public function __construct()
{
$this->trainings = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set firstname
*
* #param string $firstname
* #return Needs
*/
public function setFirstname($firstname)
{
$this->firstname = $firstname;
return $this;
}
/**
* Get firstname
*
* #return string
*/
public function getFirstname()
{
return $this->firstname;
}
/**
* Set lastname
*
* #param string $lastname
* #return Needs
*/
public function setLastname($lastname)
{
$this->lastname = $lastname;
return $this;
}
/**
* Get lastname
*
* #return string
*/
public function getLastname()
{
return $this->lastname;
}
/**
* Set trainingneedstart
*
* #param \DateTime $trainingneedstart
* #return Needs
*/
public function setTrainingneedstart($trainingneedstart)
{
$this->trainingneedstart = $trainingneedstart;
return $this;
}
/**
* Get trainingneedstart
*
* #return \DateTime
*/
public function getTrainingneedstart()
{
return $this->trainingneedstart;
}
/**
* Set readytowork
*
* #param \DateTime $readytowork
* #return Needs
*/
public function setReadytowork($readytowork)
{
$this->readytowork = $readytowork;
return $this;
}
/**
* Get readytowork
*
* #return \DateTime
*/
public function getReadytowork()
{
return $this->readytowork;
}
/**
* Set relatedproject
*
* #param \Chris\BabelBundle\Entity\Projects $relatedproject
* #return Needs
*/
public function setRelatedproject(\Chris\BabelBundle\Entity\Projects $relatedproject = null)
{
$this->relatedproject = $relatedproject;
return $this;
}
/**
* Get relatedproject
*
* #return \Chris\BabelBundle\Entity\Projects
*/
public function getRelatedproject()
{
return $this->relatedproject;
}
/**
* Set relatedactivity
*
* #param \Chris\BabelBundle\Entity\Activities $relatedactivity
* #return Needs
*/
public function setRelatedactivity(\Chris\BabelBundle\Entity\Activities $relatedactivity = null)
{
$this->relatedactivity = $relatedactivity;
return $this;
}
/**
* Get relatedactivity
*
* #return \Chris\BabelBundle\Entity\Activities
*/
public function getRelatedactivity()
{
return $this->relatedactivity;
}
/**
* Set relatedjob
*
* #param \Chris\BabelBundle\Entity\Jobs $relatedjob
* #return Needs
*/
public function setRelatedjob(\Chris\BabelBundle\Entity\Jobs $relatedjob = null)
{
$this->relatedjob = $relatedjob;
return $this;
}
/**
* Get relatedjob
*
* #return \Chris\BabelBundle\Entity\Jobs
*/
public function getRelatedjob()
{
return $this->relatedjob;
}
/**
* Set relatedexperience
*
* #param \Chris\BabelBundle\Entity\Experiences $relatedexperience
* #return Needs
*/
public function setRelatedexperience(\Chris\BabelBundle\Entity\Experiences $relatedexperience = null)
{
$this->relatedexperience = $relatedexperience;
return $this;
}
/**
* Get relatedexperience
*
* #return \Chris\BabelBundle\Entity\Experiences
*/
public function getRelatedexperience()
{
return $this->relatedexperience;
}
/**
* Set maintrainingplant
*
* #param \Chris\BabelBundle\Entity\Plants $maintrainingplant
* #return Needs
*/
public function setMaintrainingplant(\Chris\BabelBundle\Entity\Plants $maintrainingplant = null)
{
$this->maintrainingplant = $maintrainingplant;
return $this;
}
/**
* Get maintrainingplant
*
* #return \Chris\BabelBundle\Entity\Plants
*/
public function getMaintrainingplant()
{
return $this->maintrainingplant;
}
/**
* Add trainings
*
* #param \Chris\BabelBundle\Entity\Trainings $trainings
* #return Needs
*/
public function addTraining(\Chris\BabelBundle\Entity\Trainings $trainings)
{
$this->trainings[] = $trainings;
return $this;
}
/**
* Remove trainings
*
* #param \Chris\BabelBundle\Entity\Trainings $trainings
*/
public function removeTraining(\Chris\BabelBundle\Entity\Trainings $trainings)
{
$this->trainings->removeElement($trainings);
}
/**
* Get trainings
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getTrainings()
{
return $this->trainings;
}
public function __toString() {return $this->getFirstname();}
}
<?php
namespace Chris\BabelBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
*/
class Trainings
{
/**
* #ORM\GeneratedValue
* #ORM\Id
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Plants")
* #Assert\NotBlank()
*/
private $trainingplant;
/**
* #ORM\Column(type="date")
* #Assert\NotBlank()
*/
private $trainingstart;
/**
* #ORM\Column(type="date")
* #Assert\NotBlank()
*/
private $trainingend;
/**
* #var Needs $trainings
*
* #ORM\ManyToOne(targetEntity="Needs", inversedBy="trainings", cascade={"persist", "remove", "merge"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="Needs_id", referencedColumnName="id")
* })
*/
private $training;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set trainingstart
*
* #param \DateTime $trainingstart
* #return Trainings
*/
public function setTrainingstart($trainingstart)
{
$this->trainingstart = $trainingstart;
return $this;
}
/**
* Get trainingstart
*
* #return \DateTime
*/
public function getTrainingstart()
{
return $this->trainingstart;
}
/**
* Set trainingend
*
* #param \DateTime $trainingend
* #return Trainings
*/
public function setTrainingend($trainingend)
{
$this->trainingend = $trainingend;
return $this;
}
/**
* Get trainingend
*
* #return \DateTime
*/
public function getTrainingend()
{
return $this->trainingend;
}
/**
* Set trainingplant
*
* #param \Chris\BabelBundle\Entity\Plants $trainingplant
* #return Trainings
*/
public function setTrainingplant(\Chris\BabelBundle\Entity\Plants $trainingplant = null)
{
$this->trainingplant = $trainingplant;
return $this;
}
/**
* Get trainingplant
*
* #return \Chris\BabelBundle\Entity\Plants
*/
public function getTrainingplant()
{
return $this->trainingplant;
}
/**
* Set training
*
* #param \Chris\BabelBundle\Entity\Needs $training
* #return Trainings
*/
public function setTraining(\Chris\BabelBundle\Entity\Needs $training = null)
{
$this->training = $training;
return $this;
}
/**
* Get training
*
* #return \Chris\BabelBundle\Entity\Needs
*/
public function getTraining()
{
return $this->training;
}
}
The 2 Controllers :
<?php
namespace Chris\BabelBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Chris\BabelBundle\Entity\Needs;
use Chris\BabelBundle\Form\NeedsType;
/**
* Needs controller.
*
*/
class NeedsController extends Controller
{
/**
* Lists all Needs entities.
*
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('ChrisBabelBundle:Needs')->findAll();
return $this->render('ChrisBabelBundle:Needs:index.html.twig', array(
'entities' => $entities,
));
}
/**
* Finds and displays a Needs entity.
*
*/
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ChrisBabelBundle:Needs')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Needs entity.');
}
return $this->render('ChrisBabelBundle:Needs:show.html.twig', array(
'entity' => $entity, ));
}
/**
* Displays a form to create a new Needs entity.
*
*/
public function newAction()
{
$entity = new Needs();
$form = $this->createForm(new NeedsType(), $entity);
return $this->render('ChrisBabelBundle:Needs:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
/**
* Creates a new Needs entity.
*
*/
public function createAction(Request $request)
{
$entity = new Needs();
$form = $this->createForm(new NeedsType(), $entity);
$form->bind($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('needs_show', array('id' => $entity->getId())));
}
return $this->render('ChrisBabelBundle:Needs:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
/**
* Displays a form to edit an existing Needs entity.
*
*/
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ChrisBabelBundle:Needs')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Needs entity.');
}
$editForm = $this->createForm(new NeedsType(), $entity);
return $this->render('ChrisBabelBundle:Needs:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
));
}
/**
* Edits an existing Needs entity.
*
*/
public function updateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ChrisBabelBundle:Needs')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Needs entity.');
}
$editForm = $this->createForm(new NeedsType(), $entity);
$editForm->bind($request);
if ($editForm->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('needs_edit', array('id' => $id)));
}
return $this->render('ChrisBabelBundle:Needs:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
));
}
/**
* Deletes a Needs entity.
*
*/
public function deleteAction($id)
{
$em = $this->container->get('doctrine')->getEntityManager();
$metier = $em->find('ChrisBabelBundle:Needs', $id);
if (!$metier)
{
throw new NotFoundHttpException("Need not found");
}
$em->remove($metier); $em->flush();
return $this->redirect($this->generateUrl('needs'));
}
}
<?php
namespace Chris\BabelBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Chris\BabelBundle\Entity\Trainings;
use Chris\BabelBundle\Form\TrainingsType;
/**
* Trainings controller.
*
*/
class TrainingsController extends Controller
{
/**
* Lists all Trainings entities.
*
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('ChrisBabelBundle:Trainings')->findAll();
return $this->render('ChrisBabelBundle:Trainings:index.html.twig', array(
'entities' => $entities,
));
}
/**
* Finds and displays a Trainings entity.
*
*/
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ChrisBabelBundle:Trainings')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Trainings entity.');
}
$deleteForm = $this->createDeleteForm($id);
return $this->render('ChrisBabelBundle:Trainings:show.html.twig', array(
'entity' => $entity,
'delete_form' => $deleteForm->createView(), ));
}
/**
* Displays a form to create a new Trainings entity.
*
*/
public function newAction()
{
$entity = new Trainings();
$form = $this->createForm(new TrainingsType(), $entity);
return $this->render('ChrisBabelBundle:Trainings:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
/**
* Creates a new Trainings entity.
*
*/
public function createAction(Request $request)
{
$entity = new Trainings();
$form = $this->createForm(new TrainingsType(), $entity);
$form->bind($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('trainings_show', array('id' => $entity->getId())));
}
return $this->render('ChrisBabelBundle:Trainings:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
/**
* Displays a form to edit an existing Trainings entity.
*
*/
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ChrisBabelBundle:Trainings')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Trainings entity.');
}
$editForm = $this->createForm(new TrainingsType(), $entity);
$deleteForm = $this->createDeleteForm($id);
return $this->render('ChrisBabelBundle:Trainings:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Edits an existing Trainings entity.
*
*/
public function updateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ChrisBabelBundle:Trainings')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Trainings entity.');
}
$deleteForm = $this->createDeleteForm($id);
$editForm = $this->createForm(new TrainingsType(), $entity);
$editForm->bind($request);
if ($editForm->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('trainings_edit', array('id' => $id)));
}
return $this->render('ChrisBabelBundle:Trainings:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Deletes a Trainings entity.
*
*/
public function deleteAction(Request $request, $id)
{
$form = $this->createDeleteForm($id);
$form->bind($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ChrisBabelBundle:Trainings')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Trainings entity.');
}
$em->remove($entity);
$em->flush();
}
return $this->redirect($this->generateUrl('trainings'));
}
private function createDeleteForm($id)
{
return $this->createFormBuilder(array('id' => $id))
->add('id', 'hidden')
->getForm()
;
}
}
and finally the forms :
<?php
namespace Chris\BabelBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class NeedsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstname')
->add('lastname')
->add('trainingneedstart')
->add('readytowork')
->add('relatedproject')
->add('relatedactivity')
->add('relatedjob')
->add('relatedexperience')
->add('maintrainingplant')
// Ajout du formulaire imbriqué des trainings en collection
->add('trainings', 'collection', array('type' => new TrainingsType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Chris\BabelBundle\Entity\Needs'
));
}
public function getName()
{
return 'chris_babelbundle_needstype';
}
}
<?php
namespace Chris\BabelBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TrainingsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('trainingstart')
->add('trainingend')
->add('trainingplant')
->add('training')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Chris\BabelBundle\Entity\Trainings'
));
}
public function getName()
{
return 'chris_babelbundle_trainingstype';
}
}
most of this is working except that the trainings saved do not contain the needs ID in the DB so, when editing a need I cant retrieve the several trainings.
Finally I found the solution with the help of French IRC :)
I only had to modify the Needs entity like this :
/**
* Add trainings
*
* #param \Chris\BabelBundle\Entity\Trainings $trainings
* #return Needs
*/
public function addTraining(\Chris\BabelBundle\Entity\Trainings $trainings)
{
$this->trainings[] = $trainings;
$trainings->setTraining($this);
return $this;
}