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.
Related
the error is:
Could not determine access type for property "offerOrders" in class "App\Form\OrderType": Neither the property "offerOrders" nor one of the methods "addOfferOrder()"/"removeOfferOrder()", "setOfferOrders()", "offerOrders()", "__set()" or "__call()" exist and have public access in class "App\Form\OrderType"..
PhotoSession Class
<?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\PhotoSessionRepository")
*/
class PhotoSession
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="App\Entity\Order", inversedBy="photoSession", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=false)
*/
private $purchaseOrder;
Order Class
<?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\OrderRepository")
* #ORM\Table(name="`order`")
*/
class Order
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="App\Entity\PhotoSession", mappedBy="purchaseOrder", cascade={"persist", "remove"})
*/
private $photoSession;
/**
* #ORM\OneToMany(targetEntity="App\Entity\OfferOrder", mappedBy="purchaseOrder", cascade={"persist"})
*/
private $offerOrders;
public function __construct()
{
$this->offerOrders = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getPhotoSession(): ?PhotoSession
{
return $this->photoSession;
}
public function setPhotoSession(PhotoSession $photoSession): self
{
$this->photoSession = $photoSession;
// set the owning side of the relation if necessary
if ($photoSession->getPurchaseOrder() !== $this) {
$photoSession->setPurchaseOrder($this);
}
return $this;
}
/**
* #return Collection|OfferOrder[]
*/
public function getOfferOrders(): Collection
{
return $this->offerOrders;
}
public function addOfferOrder(OfferOrder $offerOrder): self
{
if (!$this->offerOrders->contains($offerOrder)) {
$this->offerOrders[] = $offerOrder;
$offerOrder->setPurchaseOrder($this);
}
return $this;
}
public function removeOfferOrder(OfferOrder $offerOrder): self
{
if ($this->offerOrders->contains($offerOrder)) {
$this->offerOrders->removeElement($offerOrder);
// set the owning side to null (unless already changed)
if ($offerOrder->getPurchaseOrder() === $this) {
$offerOrder->setPurchaseOrder(null);
}
}
return $this;
}
}
offerOrder Class
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\OfferOrderRepository")
*/
class OfferOrder
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Offer", inversedBy="offerOrders")
* #ORM\JoinColumn(nullable=false)
*/
private $offer;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Order", inversedBy="offerOrders")
* #ORM\JoinColumn(nullable=false)
*/
private $purchaseOrder;
/**
* #ORM\Column(type="float", nullable=true)
*/
private $quantity;
/**
* #ORM\Column(type="float", nullable=true)
*/
private $totalPriceHt;
public function getId(): ?int
{
return $this->id;
}
public function getOffer(): ?Offer
{
return $this->offer;
}
public function setOffer(?Offer $offer): self
{
$this->offer = $offer;
return $this;
}
public function getPurchaseOrder(): ?Order
{
return $this->purchaseOrder;
}
public function setPurchaseOrder(?Order $purchaseOrder): self
{
$this->purchaseOrder = $purchaseOrder;
return $this;
}
public function getQuantity(): ?float
{
return $this->quantity;
}
public function setQuantity(?float $quantity): self
{
$this->quantity = $quantity;
return $this;
}
public function getTotalPriceHt(): ?float
{
return $this->totalPriceHt;
}
public function setTotalPriceHt(?float $totalPriceHt): self
{
$this->totalPriceHt = $totalPriceHt;
return $this;
}
}
offer class
<?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\OfferRepository")
*/
class Offer
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="float")
*/
private $price;
/**
* #var Tag
* #ORM\ManyToOne(targetEntity="App\Entity\Tag")
* #ORM\JoinColumn(nullable=false)
*/
private $tag;
/**
* #var integer|null
* #ORM\Column(type="integer")
*/
private $version = 0;
/**
* #ORM\OneToMany(targetEntity="App\Entity\OfferOrder", mappedBy="offer")
*/
private $offerOrders;
public function __construct()
{
$this->offerOrders = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getPrice(): ?float
{
return $this->price;
}
public function setPrice(float $price): self
{
$this->price = $price;
return $this;
}
/**
* #return Tag|null
*/
public function getTag(): ?Tag
{
return $this->tag;
}
/**
* #param Tag $tag
* #return Offer
*/
public function setTag(Tag $tag): self
{
$this->tag = $tag;
return $this;
}
/**
* #return int|null
*/
public function getVersion(): ?int
{
return $this->version;
}
/**
* #param int|null $version
* #return Offer
*/
public function setVersion(?int $version): self
{
$this->version = $version;
return $this;
}
/**
* #return Collection|OfferOrder[]
*/
public function getOfferOrders(): Collection
{
return $this->offerOrders;
}
public function addOfferOrder(OfferOrder $offerOrder): self
{
if (!$this->offerOrders->contains($offerOrder)) {
$this->offerOrders[] = $offerOrder;
$offerOrder->setOffer($this);
}
return $this;
}
public function removeOfferOrder(OfferOrder $offerOrder): self
{
if ($this->offerOrders->contains($offerOrder)) {
$this->offerOrders->removeElement($offerOrder);
// set the owning side to null (unless already changed)
if ($offerOrder->getOffer() === $this) {
$offerOrder->setOffer(null);
}
}
return $this;
}
}
SessionPhotoType
<?php
namespace App\Form;
use App\Entity\Family;
use App\Entity\PhotoSession;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PhotoSessionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('description')
->add('family', EntityType::class, [
'class' => Family::class,
'choice_label' => 'name',
])
->add('purchaseOrder', OrderType::class, [
'data_class' => OrderType::class
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => PhotoSession::class,
]);
}
}
OrderType
<?php
namespace App\Form;
use App\Entity\Order;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class OrderType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('offerOrders', CollectionType::class, [
'entry_type' => OfferOrderType::class,
'by_reference' => false,
'allow_add' => true,
'allow_delete' => true,
])
->add('totalPriceHt')
->add('tva')
->add('finalPrice')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Order::class,
]);
}
}
OfferOrderType
class OfferOrderType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('offer', EntityType::class, [
'class' => Offer::class,
'choice_label' => function(Offer $offer) {
return sprintf('%s %f €', $offer->getTag()->getName(), $offer->getPrice());
},
'placeholder' => 'Choissiez une offre'
])
->add('quantity', NumberType::class)
->add('totalPriceHt', NumberType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'widget' => 'single_text',
'data_class' => OfferOrder::class,
]);
}
}
I'm sorry for the long code blocks, but for the people who will try to solve the bug I think that there is the necessary one.
The error is returned when I submit the form.
The question I ask myself is why symfony is looking for accessors in the App\Form\OrderType class?
I put dumps everywhere in PropertyAccessor.php PropertyPathMapper.php and vendor/symfony/form/Form.php, and when it goes here:
if (FormUtil::isEmpty($viewData)) {
$emptyData = $this->config->getEmptyData();
if ($emptyData instanceof \Closure) {
$emptyData = $emptyData($this, $viewData);
}
$viewData = $emptyData;
dump($viewData);
}
in Form.php it sets $viewData to App\Form\OrderType but I don't know why
The answer was:
For the form to pass, it was necessary to change purchaseOrder in photoSessionType like this:
-> add ('purchaseOrder', OrderType :: class)
Thanks again to #Ihor Kostrov
Try to change
->add('purchaseOrder', OrderType::class, [
'data_class' => OrderType::class
])
To
->add('purchaseOrder', OrderType::class, [
'data_class' => Order::class
])
Or just remove data_class option
I am trying to use BeelabTagBundle library to implements tags on my website. But I am in a lack of information to do so. Here is where I am at now:
Installed via composer
Created an entity that implements "TaggableInterface" with some personals fields, and the requested ones in the documentation.
/**
* Constructor
*/
public function __construct()
{
$this->keywords = new ArrayCollection();
}
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id = null;
/**
* #ORM\Column(type="string", length=255)
*/
private $type;
/**
* #ORM\Column(type="string", length=255)
*/
private $fileName;
/**
* #ORM\Column(type="boolean")
*/
private $active;
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Tag")
*/
private $keywords;
private $tagsText;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $updated;
/**
* {#inheritdoc}
*/
public function addTag(TagInterface $tag)
{
$this->keywords[] = $tag;
}
/**
* {#inheritdoc}
*/
public function removeTag(TagInterface $tag)
{
$this->keywords->removeElement($tag);
}
/**
* {#inheritdoc}
*/
public function hasTag(TagInterface $tag)
{
return $this->keywords->contains($tag);
}
/**
* {#inheritdoc}
*/
public function getTags()
{
return $this->keywords;
}
/**
* {#inheritdoc}
*/
public function getTagNames(): array
{
return empty($this->tagsText) ? [] : array_map('trim', explode(',', $this->tagsText));
}
/**
* #param string
*/
public function setTagsText($tagsText)
{
$this->tagsText = $tagsText;
$this->updated = new \DateTime();
}
/**
* #return string
*/
public function getTagsText()
{
$this->tagsText = implode(', ', $this->keywords->toArray());
return $this->tagsText;
}
Have my route to get the json of my tags (working when calling it but empty)
/**
* #Route("/miseenpage/keywords.json", name="keywords", defaults={"_format": "json"})
*/
public function tagsAction() {
$tags = $this->getDoctrine()->getRepository(Tag::class)->findBy([], ['name' => 'ASC']);
return $this->render('miseenpage/keywords.html.twig', ['tags' => $tags]);
}
I also followed the Javascript enhancement, So I now have created a "TagsTextType"
class TagsTextType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('tagsText', TextType::class, ['required' => false, 'label' => 'Tags'])
;
}
/**
* #var RouterInterface $route
*/
private $router;
/**
* #param RouterInterface $router
*/
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'required' => false,
'label' => 'Tags',
'attr' => [
'placeholder' => 'separate tags with comma',
'data-ajax' => $this->router->generate('keywords'),
],
]);
}
/**
* {#inheritdoc}
*/
public function getParent()
{
return TextType::class;
}
}
How can I show my tag field now (alongside with all my other data from my entity) ? This is not detailed in the documentation. Thanks
I have a little problem with my image upload, can u help me please:
Could not determine access type for property "file".
Controller
/**
* Creates a new Produits entity.
*
*/
public function createAction(Request $request)
{
$entity = new Produits();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('adminProduits_show', array('id' => $entity->getId())));
}
return $this->render('EcommerceBundle:Administration:Produits/layout/new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
/**
* Creates a form to create a Produits entity.
*
* #param Produits $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(Produits $entity)
{
$form = $this->createForm(ProduitsType::class, $entity);
$form->add('submit', SubmitType::class, array('label' => 'Ajouter'));
return $form;
}
/**
* Displays a form to create a new Produits entity.
*
*/
public function newAction()
{
$entity = new Produits();
$form = $this->createCreateForm($entity);
return $this->render('EcommerceBundle:Administration:Produits/layout/new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
Form
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class, array('data_class' => null))
->add('name', TextType::class)
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ecommerce\EcommerceBundle\Entity\Media'
));
}
/**
* #return string
*/
public function getName()
{
return 'ecommerce_ecommercebundle_media';
}
Entity
/**
* #ORM\Column(name="name",type="string",length=255)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\Column(type="string",length=255, nullable=true)
*/
private $path;
/**
* #Assert\File(
* maxSize = "1024k",
* mimeTypes = {"image/png", "image/jpg", "image/bmp"},
* mimeTypesMessage = "Please upload a valid PDF"
* )
*/
public $file;
public function getUploadRootDir()
{
return __dir__.'/../../../../web/uploads';
}
public function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
}
public function getAssetPath()
{
return 'uploads/'.$this->path;
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
$this->tempFile = $this->getAbsolutePath();
$this->oldFile = $this->getPath();
$this->updateAt = new \DateTime();
if (null !== $this->file)
$this->path = sha1(uniqid(mt_rand(),true)).'.'.$this->file->guessExtension();
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null !== $this->file) {
$this->file->move($this->getUploadRootDir(),$this->path);
unset($this->file);
if ($this->oldFile != null) unlink($this->tempFile);
}
}
/**
* #ORM\PreRemove()
*/
public function preRemoveUpload()
{
$this->tempFile = $this->getAbsolutePath();
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if (file_exists($this->tempFile)) unlink($this->tempFile);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function getPath()
{
return $this->path;
}
public function getName()
{
return $this->name;
}
public function getFile()
{
return $this->file;
}
/**
* Set path
*
* #param string $path
* #return String
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Set alt
*
* #param string $alt
* #return String
*/
public function setAlt($alt)
{
$this->alt = $alt;
return $this;
}
/**
* Set name
*
* #param string $name
* #return String
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Set updateAt
*
* #param \DateTime $updateAt
*
* #return Media
*/
public function setUpdateAt($updateAt)
{
$this->updateAt = $updateAt;
return $this;
}
/**
* Get updateAt
*
* #return \DateTime
*/
public function getUpdateAt()
{
return $this->updateAt;
}
Thanks for your help guys :)
Please add "'mapped' => false," to form builder.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class,
array(
'data_class' => null,
'mapped' => false,
))
->add('name', TextType::class)
;
}
Those who say that it is wrong to dissolve, see the test there. I'm not the one who made it wrong.
Test code:
https://github.com/symfony/property-access/blob/master/Tests/PropertyAccessorCollectionTest.php#L151
A second solution is to add function setXxx to the property that gives an error in class Entity.
public $xxx;
public function setXxx(Array $xxx)
{
$this->xxx = $xxx;
}
Or
public function __construct()
{
$this->xxx = new ArrayCollection();
}
Video Link: https://knpuniversity.com/screencast/doctrine-relations/create-genus-note
My English is bad, I can tell you.
Setting mapped => false is not the real solution, because the field IS mapped to the entity.
In my case, I have a OneToMany relation, so I need the field mapped in order to use the 'cascase' => {"all"} option.
If the field is not mapped, then you must to persist the related entity manually. That's no good.
Stumbled on this while searching solution for my problem, that was shooting the same error and I was using also ArrayCollection class, so this may be helpful to someone:
My field in ArrayCollection is called $files, so I had to add constructor like this:
use Doctrine\Common\Collections\ArrayCollection;
public function __construct()
{
$this->files = new ArrayCollection();
}
Then I added add and remove methods, like this:
public function addFile(MyFile $file) : self
{
$file->setParentMyItem($this); // Setting parent item
$this->files->add($file);
return $this;
}
public function removeFile(MyFile $file) : self
{
$this->files->removeElement($file);
return $this;
}
But catch is that even my field name was $files I had to name add and remove methods addFile() and removeFile(), without 's' at end, which is totally not logical to me, but that solved the problem.
Same error for me but on a OneToMany relation. Although setting "mapped => false" also solved the error there is another option. I only had a getter, adder and remover method on the entity. I needed to add a "setter" too
In my case it was:
/**
* #param ArrayCollection|SubStatusOptions[]
*/
public function setSubStatusOptions(ArrayCollection $subStatusOptions)
{
$this->subStatusOptions = $subStatusOptions;
}
This happened because I had an entity property but the getter/setters were missing. I used:
php bin/console make:entity --regenerate MyBundle\\Entity\\MyEntity
to regenerate them
I have this Entities:
<?php
namespace ProductBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use ProductBundle\DBAL\Types\StatusType;
use ProductBundle\DBAL\Types\FieldType;
use Fresh\Bundle\DoctrineEnumBundle\Validator\Constraints as DoctrineAssert;
/**
* #ORM\Entity
* #ORM\Table(name="product_detail")
*/
class ProductDetail {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="ProductDetail")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
protected $parent;
/**
* #ORM\Column(type="string", length=255)
*/
protected $description;
/**
* #ORM\Column(type="string", length=255)
*/
protected $label;
/**
* #var string $field_type
* #DoctrineAssert\Enum(entity="ProductBundle\DBAL\Types\FieldType")
* #ORM\Column(name="field_type", type="FieldType", nullable=false)
*/
protected $field_type;
/**
* #ORM\Column(name="values_text", type="string")
*/
protected $values_text;
/**
* #ORM\Column(type="string", length=255)
*/
protected $measure_unit;
/**
* #var string $status
* #DoctrineAssert\Enum(entity="ProductBundle\DBAL\Types\StatusType")
* #ORM\Column(name="status", type="StatusType", nullable=false)
*/
protected $status;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created", type="datetime")
*/
protected $created;
/**
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="modified", type="datetime")
*/
protected $modified;
/**
* #ORM\ManyToMany(targetEntity="CategoryBundle\Entity\Category", inversedBy="pd_detail")
* #ORM\JoinTable(name="product_detail_has_category",
* joinColumns={#ORM\JoinColumn(name="category", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")}
* )
*/
protected $category;
/**
* #ORM\ManyToMany(targetEntity="ProductBundle\Entity\DetailGroup", inversedBy="productDetail")
* #ORM\JoinTable(name="detail_group_has_product_detail",
* joinColumns={#ORM\JoinColumn(name="group", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")}
* )
*/
protected $detail_group;
public function __construct() {
$this->detail_group = new \Doctrine\Common\Collections\ArrayCollection();
$this->category = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId() {
return $this->id;
}
public function setParent(ProductDetail $parent = null) {
$this->parent = $parent;
}
public function getParent() {
return $this->parent;
}
public function setDescription($description) {
$this->description = $description;
}
public function getDescription() {
return $this->description;
}
public function setLabel($label) {
$this->label = $label;
}
public function getLabel() {
return $this->label;
}
public function setFieldType($field_type) {
$this->field_type = $field_type;
}
public function getFieldType() {
return $this->field_type;
}
public function setValuesText($values_text) {
$this->values_text = $values_text;
}
public function getValuesText() {
return $this->values_text;
}
public function setMeasureUnit($measure_unit) {
$this->measure_unit = $measure_unit;
}
public function getMeasureUnit() {
return $this->measure_unit;
}
public function setStatus($status) {
$this->status = $status;
}
public function getStatus() {
return $this->status;
}
public function setCreated($param) {
$this->created = $param;
return true;
}
public function getCreated() {
return $this->created;
}
public function setModified($param) {
$this->modified = $param;
return true;
}
public function getModified() {
return $this->modified;
}
public function setCategory(Category $category) {
$this->category[] = $category;
}
public function getCategory() {
return $this->category;
}
public function setDetailGroup(DetailGroup $detailGroup) {
$this->detail_group[] = $detailGroup;
}
public function getDetailGroup() {
return $this->detail_group;
}
}
namespace ProductBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Fresh\Bundle\DoctrineEnumBundle\Validator\Constraints as DoctrineAssert;
/**
* #ORM\Entity
* #ORM\Table(name="detail_group")
*/
class DetailGroup {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="DetailGroup")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
protected $parent;
/**
* #ORM\Column(type="string", length=255)
*/
protected $name;
/**
* #ORM\Column(type="string", length=255)
*/
protected $description;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created", type="datetime")
*/
protected $created;
/**
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="modified", type="datetime")
*/
protected $modified;
/**
* #ORM\ManyToMany(targetEntity="ProductBundle\Entity\ProductDetail", mappedBy="detail_group", cascade={"all"})
*/
protected $productDetail;
public function __construct() {
$this->productDetail = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId() {
return $this->id;
}
public function setParent(DetailGroup $parent = null) {
$this->parent = $parent;
}
public function getParent() {
return $this->parent;
}
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function setDescription($description) {
$this->description = $description;
}
public function getDescription() {
return $this->description;
}
public function setCreated($param) {
$this->created = $param;
return true;
}
public function getCreated() {
return $this->created;
}
public function setModified($param) {
$this->modified = $param;
return true;
}
public function getModified() {
return $this->modified;
}
public function setProductDetail(ProductDetail $productDetail) {
$this->productDetail[] = $productDetail;
}
public function getProductDetail() {
return $this->productDetail;
}
}
This is my ProductDetailType.php form:
<?php
namespace ProductBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ProductDetailType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('category', 'entity', array('class' => 'CategoryBundle:Category', 'property' => 'name', 'required' => false, 'multiple' => true, 'expanded' => false))
->add('detail_group', 'entity', array('class' => 'ProductBundle:DetailGroup', 'property' => 'name', 'required' => false, 'multiple' => true, 'expanded' => false))
->add('parent', 'entity', array('class' => 'ProductBundle:ProductDetail', 'property' => 'label', 'required' => false))
->add('description', 'text', array('required' => false))
->add('label')
->add('field_type', 'choice', ['choices' => \ProductBundle\DBAL\Types\FieldType::getChoices()])
->add('values_text', 'collection', array('type' => 'text', 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false))
->add('measure_unit', 'text', array('required' => false))
->add('status', 'choice', ['choices' => \ProductBundle\DBAL\Types\StatusType::getChoices()]);
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'ProductBundle\Entity\ProductDetail'
));
}
public function getName() {
return 'prod_detail_create';
}
}
And this is my createAction():
/**
* Handle product details creation
*
* #Route("/product_detail/create", name="product_detail_create")
* #Method("POST")
* #Template("ProductBundle:ProductDetail:new.html.twig")
*/
public function createAction(Request $request) {
$entity = new ProductDetail();
$form = $this->createForm(new ProductDetailType(), $entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$entity->setValuesText(serialize($form->get('values_text')->getData()));
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('product_detail_list'));
}
return $this->render('ProductBundle:ProductDetail:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
When I try to insert new records I get this errors:
An exception occurred while executing 'INSERT INTO
product_detail_has_category (category, detail) VALUES (?, ?)' with
params [7, 2]:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or
update a child row: a foreign key constraint fails
(product_detail_has_category, CONSTRAINT
fk_product_detail_has_category_product_detail1 FOREIGN KEY
(detail) REFERENCES product_detail (id) ON UPDATE CASCADE)
I check the logs and see this:
DEBUG - "START TRANSACTION"
DEBUG - INSERT INTO product_detail (description, label, field_type, values_text, measure_unit, status, created, modified, parent) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
DEBUG - INSERT INTO product_detail_has_category (category, detail) VALUES (?, ?)
DEBUG - "ROLLBACK"
I don't know why it doesn't insert product_detail and the tries to create the relation, what is wrong?
You can either:
set on $category field set cascade={"PERSIST"} in #ManyToMany.
This requires no chnages to controller itself but it depends on whether you like defining cascades.
persist Category object manually before persisting ProductDetail.
$em = ...; // your entity manager
// first, manually persist any category that is found
foreach ( $productDetail->getCategory() as $c ){
$em->persist($c);
}
// then, persist top-level object
$em->persist($productDetail);
// finally, flush everything
$em->flush();
Hope this helps a bit...
I've found where the errors was, see the changes below in annotations:
/**
* #ORM\ManyToMany(targetEntity="CategoryBundle\Entity\Category", inversedBy="pd_detail", cascade={"persist"})
* #ORM\JoinTable(name="product_detail_has_category",
* joinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category", referencedColumnName="id")}
* )
*/
protected $category;
/**
* #ORM\ManyToMany(targetEntity="ProductBundle\Entity\DetailGroup", inversedBy="productDetail", cascade={"persist"})
* #ORM\JoinTable(name="detail_group_has_product_detail",
* joinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="kgroup", referencedColumnName="id")}
* )
*/
protected $detail_group;
I've inverted the relations in joinColumns and inversedJoinColumns and all works fine, also has to rename group to kgroup due to MySQL query errors!!
I am new to Symfony2 and can't to figure out why I'm getting this error. Perhaps there is something wrong with my Entities? So I need some help with it.
An error:
EntityManager#persist() expects parameter 1 to be an entity object, array given.
500 Internal Server Error - ORMInvalidArgumentException
I have the following code:
User.php:
<?php
namespace Acme\MainBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* User
*/
class User
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $username;
/**
* #var string
*/
private $password;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* #param string $username
* #return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Get username
*
* #return string
*/
public function getUsername()
{
return $this->username;
}
/**
* Set password
*
* #param string $password
* #return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
}
UserRegType.php
<?php
namespace Acme\MainBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\Length;
class UserRegType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('username', 'text', array(
'constraints'=>new Length(array('min' => 3))
));
$builder->add('password', 'repeated', array(
'first_name' => 'password',
'second_name' => 'confirm',
'type' => 'password',
'constraints' => new Length(array('min' => 5, 'max' => 8))
));
}
public function getDefaultOptions(array $options)
{
return array('data_class' => 'Acme\MainBundle\Entity\User');
}
public function getName() {
return 'user';
}
}
?>
UserController.php:
<?php
namespace Acme\MainBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Acme\MainBundle\Form\Type\UserRegType;
class UserController extends Controller
{
public function registerAction()
{
$form = $this->createForm(new UserRegType());
return $this->render(
'AcmeMainBundle:User:register.html.twig',
array('form' => $form->createView())
);
}
public function createUserAction()
{
$em = $this->getDoctrine()->getEntityManager();
$form = $this->createForm(new UserRegType());
$form->bind($this->getRequest());
$username = $form["username"]->getData();
if ($form->isValid()) {
$reg = $form->getData();
$em->persist($reg);
$em->flush();
$session = $this->get('session');
$session->set('username', $username);
return $this->redirect($this->generateUrl('home'));
}
}
public function loginAction()
{
}
public function logoutAction()
{
}
}