I'm using Sonata admin in my Symfony project. I have 2 entities like Parent and Child. Parent entity is connected to child by one-to-many relationship.
I have created 2 admin classes for child entity with different baseRoutName. I need to use Child entity fields in Parent entity sonata form for 2 times.
//ParentAdmin.php
$formMapper
->with('Child 1', ['class' => 'col-md-4'])
->add('child', CollectionType::class, [], [
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position',
'admin_code' => 'admin.child1'
])
->end()
->with('Child 2', ['class' => 'col-md-4'])
->add('child', CollectionType::class, [], [
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position',
'admin_code' => 'admin.child2'
])
->end();
The problem here is that I need to use child field for multiple times. But the child field within Child 2 is overriding the child field in Child 1. As you can see I have used different admin_code for these 2 fields.
My expected output is,
But the actual output I'm getting is,
I know the problem here is duplicate entity fields. Is it possible to display same field for multiple times?
Does anyone have solution/suggestion? Thanks in advance!!
maybe it's late but i had the same problem, found this post with no answer, and finally found a solution, so here it is for future purpose.
i have a Request class with generated document, get an eye on get and add functions:
class Request
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\OneToMany(targetEntity="GeneratedDocument", mappedBy="request", cascade={"all"}, orphanRemoval=true)
*/
protected $generatedDocuments;
/**
* @var ArrayCollection
* each of this attributes will get one type of $generatedDocuments
*/
protected $generatedAttestationDocuments;
protected $generatedCertificationDocuments;
public function __construct()
{
$this->generatedDocuments = new ArrayCollection();
}
/**
* Set the value of id.
*
* @param integer $id
* @return \App\Entity\Request
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Get the value of id.
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* @return Collection|GeneratedDocument[]
*/
public function getGeneratedDocuments(): Collection
{
return $this->generatedDocuments;
}
public function addGeneratedDocument(GeneratedDocument $generatedDocument): self
{
if (!$this->generatedDocuments->contains($generatedDocument)) {
$this->generatedDocuments[] = $generatedDocument;
$generatedDocument->setRequest($this);
}
return $this;
}
public function removeGeneratedDocument(GeneratedDocument $generatedDocument): self
{
if ($this->generatedDocuments->contains($generatedDocument)) {
$this->generatedDocuments->removeElement($generatedDocument);
// set the owning side to null (unless already changed)
if ($generatedDocument->getRequest() === $this) {
$generatedDocument->setRequest(null);
}
}
return $this;
}
/**
* @return Collection|GeneratedDocument[]
*
* @param int $type
*/
protected function getTypedGeneratedDocuments(int $type): Collection
{
return $this->getGeneratedDocuments()->filter(function (GeneratedDocument $gd) use ($type) {
if ($gd->getGeneratedDocumentModel()) {
return $type === $gd->getGeneratedDocumentModel()->getType();
}
return false;
});
}
/**
* @return Collection|GeneratedDocument[]
*/
public function getGeneratedAttestationDocuments(): Collection
{
if (empty($this->generatedAttestationDocuments)) {
$this->generatedAttestationDocuments =
$this->getTypedGeneratedDocuments(GeneratedDocumentModel::TYPE_ATTESTATION);
}
return $this->generatedAttestationDocuments;
}
public function addGeneratedAttestationDocument(GeneratedDocument $generatedDocument): self
{
$this->generatedAttestationDocuments[] = $generatedDocument;
return $this->addGeneratedDocument($generatedDocument);
}
public function removeGeneratedAttestationDocument(GeneratedDocument $generatedDocument): self
{
return $this->removeGeneratedDocument($generatedDocument);
}
/**
* @return Collection|GeneratedDocument[]
*/
public function getGeneratedCertificationDocuments(): Collection
{
if (empty($this->generatedCertificationDocuments)) {
$this->generatedCertificationDocuments =
$this->getTypedGeneratedDocuments(GeneratedDocumentModel::TYPE_CERTIFICATE);
}
return $this->generatedCertificationDocuments;
}
public function addGeneratedCertificationDocument(GeneratedDocument $generatedDocument): self
{
$this->generatedCertificationDocuments[] = $generatedDocument;
return $this->addGeneratedDocument($generatedDocument);
}
public function removeGeneratedCertificationDocument(GeneratedDocument $generatedDocument): self
{
return $this->removeGeneratedDocument($generatedDocument);
}
}
Then in admin you have to give a different name in each add, here I use my typed generated documents so there's no duplication problem, but they are not mapped and symfony complain about it.
Trying to use 'mapped' => false
resolved nothing, the simplest way I found was a 'virtual mapping', based on the original mapped attribute 'generatedDocuments, just the time to fool symfony when building the form.
class RequestAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper): void
{
/** @var Request $createdRequest */
$createdRequest = $this->getSubject();
$metaData = $this->getModelManager()->getMetadata($this->getClass());
//We need many CollectionType based on 'generatedDocuments', and we need an ArrayCollection for each of them
//so here is a virtual mapping to make symfony accept the persistence of our CollectionType
//then setter should fill 'generatedDocuments'
$mapping = $metaData->getAssociationMappings()['generatedDocuments'];
$mapping['fieldName'] = 'generatedAttestationDocuments';
$metaData->mapOneToMany($mapping);
$mapping['fieldName'] = 'generatedCertificationDocuments';
$metaData->mapOneToMany($mapping);
$formMapper
->with(('Attestation Deposit'))
->add('generatedAttestationDocuments', CollectionType::class, [
'label' => false,
'by_reference' => false,
'btn_add' => 'Add Attestation',
'data' => $createdRequest->getGeneratedAttestationDocuments(),
], [
'edit' => 'inline',
'inline' => 'table',
'admin_code' => 'admin.generated_document_attestation',
])
->end()
->with(('Certificate'))
->add('generatedCertificationDocuments', CollectionType::class, [
'label' => false,
'by_reference' => false,
'btn_add' => 'Add Certification',
'data' => $createdRequest->getGeneratedCertificationDocuments(),
], [
'edit' => 'inline',
'inline' => 'table',
'admin_code' => 'admin.generated_document_certification',
])
->end()
//delete virtual mapping to avoid it to get handle like a real mapping
unset($metaData->associationMappings['generatedAttestationDocuments']);
unset($metaData->associationMappings['generatedCertificationDocuments']);
}
}
I would like to know a simplest way, but It really work like individual CollectionType for me.
Hope it will help!