Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expected argument of type "Doctrine\Common\Collections\ArrayCollection","Doctrine\ORM\PersistentCollection" given

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!

like image 610
Moustapha Cissé Avatar asked Oct 16 '22 23:10

Moustapha Cissé


1 Answers

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);
}
like image 192
DrKey Avatar answered Oct 21 '22 00:10

DrKey