I have a many to many relationship between Tag and Article entities, the insertion works well but the creation of the edit form (the editAction function) does not work. All the code is there:
Article.php
<?php
namespace Diapalema\DiapalemaBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
class Article
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="titre", type="string", length=255)
*/
private $titre;
/**
* @var \DateTime
*
* @ORM\Column(name="created", type="datetimetz")
*/
private $created;
/**
* @var \DateTime
*
* @ORM\Column(name="updated", type="datetimetz")
*/
private $updated;
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="article", cascade=
{"persist"})
*/
private $auteur;
/**
* BlogArticle constructor.
* @param \DateTime $created
* @param \DateTime $updated
*/
public function __construct()
{
$this->created = $created;
$this->updated = $updated;
$this->tags = new ArrayCollection();
}
public function addTag(Tag $tag)
{
$this->tags->add($tag);
return $this;
}
public function addTags($tags)
{
foreach($tags as $tag){
$this->addTag($tag);
}
return $this;
}
public function removeTag(Tag $tag)
{
$this->tags->removeElement($tag);
return $this;
}
public function removeTags($tags)
{
foreach($tags as $tag){
$this->removeTag($tag);
}
return $this;
}
public function setTags(Tag $tag)
{
$this->tags[] = $tag;
return $this;
}
/**
* Get tags
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getTags()
{
return $this->tags;
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
}
Tag.php
class Tag
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="tagname", type="string", length=255)
*/
private $tagname;
/**
*
@ORM\ManyToMany(targetEntity="Diapalema\DiapalemaBundle\Entity\Article",
mappedBy="tags", cascade={"persist","remove"})
*/
private $articles;
/**
* Tag constructor.
*/
public function __construct()
{
$this->articles = new ArrayCollection();
}
/**
* @param Article $articles
*/
public function setArticles($articles)
{
$this->articles = $articles;
}
public function getArticles()
{
return $this->articles;
}
public function addArticles(Article $article)
{
$this->articles[] = $article;
$article->addTag($this);
return $this;
}
public function removeArticles(Article $article)
{
$this->articles->removeElement($article);
$article->removeTag($this);
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
}
StringToTagsTransformer.php
<?php
namespace Diapalema\DiapalemaBundle\Form;
use Symfony\Component\Form\DataTransformerInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Collections\ArrayCollection;
use Diapalema\DiapalemaBundle\Entity\Tag;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Ldap\Adapter\ExtLdap\Collection;
class StringToTagsTransformer implements DataTransformerInterface
{
private $om;
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
private function stringToArray($string)
{
$tags = explode(',', $string);
foreach ($tags as &$text) {
$text = trim($text);
}
return array_unique($tags);
}
public function transform($value)
{
if (null === $value) {
return null;
}
if (!($value instanceof ArrayCollection)) {
throw new UnexpectedTypeException($value,
'Doctrine\Common\Collections\ArrayCollection');
}
$tags = array();
foreach ($value as $tag) {
array_push($tags, $tag->getTagname());
}
return implode(',', $tags);
}
public function reverseTransform($value)
{
$tagCollection = new ArrayCollection();
if ('' === $value || null === $value) {
return $tagCollection;
}
if (!is_string($value)) {
throw new UnexpectedTypeException($value, 'string');
}
foreach ($this->stringToArray($value) as $name) {
$tag = $this->om->getRepository('DiapalemaBundle:Tag')
->findOneBy(array('tagname' => $name));
if (null === $tag) {
$tag = new Tag();
$tag->setTagname($name);
$this->om->persist($tag);
}
$tagCollection->add($tag);
}
return $tagCollection;
}
}
TagType.php
<?php
namespace Diapalema\DiapalemaBundle\Form;
use Diapalema\DiapalemaBundle\Entity\Tag;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TagType extends AbstractType
{
private $om;
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new StringToTagsTransformer($this->om);
$builder->addModelTransformer($transformer);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
array(
'data_class' => Tag::class
)
);
}
public function getParent()
{
return TextType::class;
}
public function getName()
{
return 'tag';
}
}
ArticleType.php
<?php
namespace Diapalema\DiapalemaBundle\Form;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ArticleType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('tags',TagType::class, array(
'required' => false,
'label' => 'form.tag',
'translation_domain' => 'FOSUserBundle',
'attr' => array(
'class' => 'form-control',
'multiple' => true,
))
);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Diapalema\DiapalemaBundle\Entity\Article'
));
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'diapalema_diapalemabundle_article';
}
}
ArticleController.php
/**
* Displays a form to edit an existing Article entity.
*
*/
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('DiapalemaBundle:Article')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Impossible de trouver
l\'entité concernée.');
}
$editForm = $this->createEditForm($entity);
return $this->
render('DiapalemaBundle:Admin/Layout:edit_article.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView()
));
}
/**
* Creates a form to edit a Article entity.
* @param Article $entity The entity
* @return \Symfony\Component\Form\Form The form
*/
private function createEditForm(Article $entity)
{
$form = $this->createForm(ArticleType::class, $entity);
$form->add('submit', SubmitType::class, array(
'label' => 'form.editbutton',
'translation_domain' => 'FOSUserBundle',
'attr' =>
array(
'class' => 'btn btn-success'
)
));
return $form;
}
services.yml
services:
app.type.tag:
class: Diapalema\DiapalemaBundle\Form\TagType
arguments: ['@doctrine.orm.entity_manager']
tags:
- { name: form.type, alias: tag }
I have the following error: Expected argument of type "Doctrine\Common\Collections\ArrayCollection","Doctrine\ORM\PersistentCollection" given. Help me!
The problem comes when you expect to get ArrayCollection
in StringToTagsTransformer
if (!($value instanceof ArrayCollection)) {
throw new UnexpectedTypeException($value,
'Doctrine\Common\Collections\ArrayCollection');
}
Actually this happens because ArrayCollection
class is used from Doctrine only for eagerly fetched relations and by default relations are always fetched lazily. So you have to fetch it eagerly or just check if $value
is instance of Doctrine\Common\Collections\Collection
which is the interface implemented by both classes
use Doctrine\Common\Collections\Collection;
if (!($value instanceof Collection)) {
throw new UnexpectedTypeException($value, Collection::class);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With