If this is a BUG REPORT:
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?
Symfony: 4.0.7
EasyAdminBundle: 1.17.12
(Optional) If they are useful, include logs, code samples, screenshots, etc.
<?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;
}
}
<?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
<?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:
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
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
App\Entity\NewsTag
App\Form\Type\TagType
config/packages/easy_admin/entities/news.yml
Thank You