Easyadminbundle: Expected value of type "App\Entity\UserMeta" for association field "App\Entity\Users#$usermeta", got "array" instead.

Created on 12 Apr 2018  路  1Comment  路  Source: EasyCorp/EasyAdminBundle

If this is a BUG REPORT:

  • Describe what you wanted to do and the wrong result you got.

I'm trying to create 2 classes / tables:

Class A) [Users] - to hold all user accounts in the system
Class B) [UserMeta] - to hold additional meta data on the Users.

Each [User] instance can have multiple [UserMeta] references.
Each [UserMeta] instance can have 1 [User] reference.

When I try to save the association through the easyadmin interface I receive the error:
Expected value of type "App\Entity\UserMeta" for association field "App\Entity\Users#$usermeta", got "array" instead.

The error makes sense to me as the $usermeta property looks to be of Doctrine's "ArrayCollection" type - but I'm not sure where or when to do the conversion from ArrayCollection to the custom UserMeta class. I've been following multiple tutorials / comments / questions, but I keep ending up at this same error - either they have not listed all of the steps, or I am missing something (hopefully simple).

This comment got me closest to what I want: https://github.com/EasyCorp/EasyAdminBundle/issues/1470#issuecomment-274056894

But once I change the relation from "many to many" to "one to many" - I am unable to keep the relation working.

Am I missing something simple?

  • Include EasyAdmin version (it's displayed in the Symfony Debug Toolbar)

Symfony: 4.0.7
EasyAdminBundle: 1.17.12

  • (Optional) If they are useful, include logs, code samples, screenshots, etc.

    • [Users] 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\UsersRepository")
 */
class Users
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $displayname;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\UserMeta", mappedBy="user", orphanRemoval=true, cascade={"persist"})
     */
    private $usermeta;

    public function __construct()
    {
        $this->usermeta = new ArrayCollection();
    }

    public function getId()
    {
        return $this->id;
    }

    public function getDisplayname(): ?string
    {
        return $this->displayname;
    }

    public function setDisplayname(string $displayname): self
    {
        $this->displayname = $displayname;

        return $this;
    }

    /**
     * @return Collection|UserMeta[]
     */
    public function getUsermeta(): Collection
    {
        return $this->usermeta;
    }

    public function addUsermetum(UserMeta $usermetum): self
    {
        if (!$this->usermeta->contains($usermetum)) {
            $this->usermeta[] = $usermetum;
            $usermetum->setUser($this);
        }

        return $this;
    }

    public function removeUsermetum(UserMeta $usermetum): self
    {
        if ($this->usermeta->contains($usermetum)) {
            $this->usermeta->removeElement($usermetum);
            // set the owning side to null (unless already changed)
            if ($usermetum->getUser() === $this) {
                $usermetum->setUser(null);
            }
        }

        return $this;
    }
}
  • [UserMeta] Class -
<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\UserMetaRepository")
 */
class UserMeta
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name;

    /**
     * @ORM\Column(type="text")
     */
    private $value;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Users", inversedBy="usermeta")
     * @ORM\JoinColumn(nullable=false)
     */
    private $user;

    public function getId()
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getValue(): ?string
    {
        return $this->value;
    }

    public function setValue(string $value): self
    {
        $this->value = $value;

        return $this;
    }

    public function getUser(): ?Users
    {
        return $this->user;
    }

    public function setUser(?Users $user): self
    {
        $this->user = $user;

        return $this;
    }
}

Config YAML: config/packages/easy_admin/entities/users.yml

easy_admin:
  entities:
    UserEntity:
      class: App\Entity\Users
      label: "Users"
      list:
        title: "Users"
        actions: 
          - name: "new"
            label: "New User"
        fields:
          - property: "id"
            label: "User ID"
          - property: "display_name"
            label: "Display Name"
      new:
        title: "Add New User"
      form:
        fields:
          - property: "displayname"
            label: "Display Name"
          - property: "usermeta"
            label: "User Details"
            type: collection
            type_options:
              entry_type: App\Form\Type\UserMetaType
  • [UserMetaType] Class -
<?php

namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\OptionsResolver\OptionsResolver;

use App\Entity\UserMeta;

class UserMetaType extends AbstractType
{
    public function buildForm (FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add("name")
            ->add("value")
        ;
    }
    public function configureOption(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            "data_class" => UserMeta::class
        ));
    }
}

If this is a FEATURE REQUEST:

  • Describe the new feature briefly.
  • Please, DON'T submit a Pull Request yet (first we need to discuss about it).

Most helpful comment

I was able to get this working (per the referenced comment) - I will close this as it does not look to be an issue with the system - just an issue with my configs.

Below is a complete working example with "News" and "NewsTag"

App\Entity\News

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;


/**
 * @ORM\Entity
 * @ORM\Table(name="news")
 */
class News
{

    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", nullable=false)
     */
    private $title;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\NewsTag", mappedBy="news", cascade={"persist"})
     */
    private $tags;


    /**
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param mixed $id
     */
    public function setId($id)
    {
        $this->id = $id;
    }

    /**
     * @return mixed
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * @param mixed $title
     */
    public function setTitle($title)
    {
        $this->title = $title;
    }

    /**
     * -------------------------- Tags ------------------------------
     */

    /**
     * @param NewsTag $tag
     */
    public function addTag(NewsTag $tag)
    {
        $this->tags->add($tag);
        $tag->setNews($this);
    }

    public function removeTag(NewsTag $tag)
    {
        // ...
    }


    /**
     * -------------------------- Tags end-------------------------------
     */

    public function getTags()
    {
        return $this->tags;
    }



    public function __construct()
    {

        $this->tags = new ArrayCollection();

    }

    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return (string) $this->getTitle();

    }
}

App\Entity\NewsTag

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 *
 * @ORM\Table(name="news_tags")
 * @ORM\Entity
 */
class NewsTag
{
    /**
     *
     * @var int
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id = null;

    /**
     *
     * @var string
     * @ORM\Column(type="string")
     */
    protected $name;

    /**
     *
     * @var string
     * @ORM\Column(type="string")
     */
    protected $extra;


    /**
     * @ORM\ManyToOne(targetEntity="News", inversedBy="tags")
     * @ORM\JoinColumn(name="news_id", referencedColumnName="id")
     */
    protected $news;


    public function getNews ()
    {
        return $this->news;
    }

    public function setNews ($news)
    {
        $this->news = $news;
    }

    /**
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * 
     * @return string
     */
    public function getExtra ()
    {
        return $this->extra;
    }

    /**
     *  @param string $extra
     */
    public function setExtra ($extra)
    {
        $this->extra = $extra;
    }



    public function __construct()
    {

    }

    public function __toString()
    {
        return $this->getName();
    }
}

App\Form\Type\TagType

<?php

namespace App\Form\Type;

use App\Entity\NewsTag;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class TagType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('extra')
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => NewsTag::class,
        ));
    }
}

config/packages/easy_admin/entities/news.yml

easy_admin:
    entities:
        News:
            class: App\Entity\News
            label: 'News'
            form:
                title: "Test Title"
                fields:
                    #your fields ...
                    - 
                        property: "title"
                        type: text

                    -
                        property: "tags"
                        type: collection 
                        type_options: 
                            entry_type: App\Form\Type\TagType
                            allow_add: true
                            by_reference: false

Thank You

>All comments

I was able to get this working (per the referenced comment) - I will close this as it does not look to be an issue with the system - just an issue with my configs.

Below is a complete working example with "News" and "NewsTag"

App\Entity\News

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;


/**
 * @ORM\Entity
 * @ORM\Table(name="news")
 */
class News
{

    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", nullable=false)
     */
    private $title;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\NewsTag", mappedBy="news", cascade={"persist"})
     */
    private $tags;


    /**
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param mixed $id
     */
    public function setId($id)
    {
        $this->id = $id;
    }

    /**
     * @return mixed
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * @param mixed $title
     */
    public function setTitle($title)
    {
        $this->title = $title;
    }

    /**
     * -------------------------- Tags ------------------------------
     */

    /**
     * @param NewsTag $tag
     */
    public function addTag(NewsTag $tag)
    {
        $this->tags->add($tag);
        $tag->setNews($this);
    }

    public function removeTag(NewsTag $tag)
    {
        // ...
    }


    /**
     * -------------------------- Tags end-------------------------------
     */

    public function getTags()
    {
        return $this->tags;
    }



    public function __construct()
    {

        $this->tags = new ArrayCollection();

    }

    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return (string) $this->getTitle();

    }
}

App\Entity\NewsTag

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 *
 * @ORM\Table(name="news_tags")
 * @ORM\Entity
 */
class NewsTag
{
    /**
     *
     * @var int
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id = null;

    /**
     *
     * @var string
     * @ORM\Column(type="string")
     */
    protected $name;

    /**
     *
     * @var string
     * @ORM\Column(type="string")
     */
    protected $extra;


    /**
     * @ORM\ManyToOne(targetEntity="News", inversedBy="tags")
     * @ORM\JoinColumn(name="news_id", referencedColumnName="id")
     */
    protected $news;


    public function getNews ()
    {
        return $this->news;
    }

    public function setNews ($news)
    {
        $this->news = $news;
    }

    /**
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * 
     * @return string
     */
    public function getExtra ()
    {
        return $this->extra;
    }

    /**
     *  @param string $extra
     */
    public function setExtra ($extra)
    {
        $this->extra = $extra;
    }



    public function __construct()
    {

    }

    public function __toString()
    {
        return $this->getName();
    }
}

App\Form\Type\TagType

<?php

namespace App\Form\Type;

use App\Entity\NewsTag;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class TagType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('extra')
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => NewsTag::class,
        ));
    }
}

config/packages/easy_admin/entities/news.yml

easy_admin:
    entities:
        News:
            class: App\Entity\News
            label: 'News'
            form:
                title: "Test Title"
                fields:
                    #your fields ...
                    - 
                        property: "title"
                        type: text

                    -
                        property: "tags"
                        type: collection 
                        type_options: 
                            entry_type: App\Form\Type\TagType
                            allow_add: true
                            by_reference: false

Thank You

Was this page helpful?
0 / 5 - 0 ratings

Related issues

liarco picture liarco  路  3Comments

devkbsc picture devkbsc  路  3Comments

Ealenn picture Ealenn  路  3Comments

haithem-rihane picture haithem-rihane  路  4Comments

bocharsky-bw picture bocharsky-bw  路  3Comments