Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sonata Admin Bundle One-to-Many relationship not saving foreign ID

I have a problem with the SonataAdminBunle in combination with symfony 2.2. I have a Project entity and a ProjectImage entity and specified a One-to-Many relationship between these two like so:

class Project
{
    /**
     * @ORM\OneToMany(targetEntity="ProjectImage", mappedBy="project", cascade={"all"}, orphanRemoval=true)
     */
    private $images;
}

class ProjectImage
{

    /**
     * @ORM\ManyToOne(targetEntity="Project", inversedBy="images")
     * @ORM\JoinColumn(name="project_id", referencedColumnName="id")
     */
    private $project;
}

I've configured the ProjectAdmin and ProjectImageAdmin:

class ProjectAdmin extends Admin
{
    protected function configureFormFields(FormMapper $formMapper)
    {
        $formMapper
            ->add('title')
            ->add('website')
            ->add('description', 'textarea')
            ->add('year')
            ->add('tags')
            ->add('images', 'sonata_type_collection', array(
                            'by_reference' => false
            ), array(
                            'edit' => 'inline',
                            'inline' => 'table',
                            'sortable' => 'id',
            ))
            ;
    }
}

class ProjectImageAdmin extends Admin
{
    protected function configureFormFields(FormMapper $formMapper)
    {
        $formMapper
            ->add('file', 'file', array(
                          'required' => false
            ))
            ;
    }
}

The problem is that in the project_image table in the database the project_id is not saved, while all other data is and also the image is saved. Couldn't find a working answer anywhere else.

like image 430
René Avatar asked Jun 07 '13 22:06

René


4 Answers

Although unrelated, I'd slighty tweak your One-to-Many annotation:

class Project
{
    /**
     * @ORM\OneToMany(targetEntity="ProjectImage", mappedBy="project", cascade={"persist"}, orphanRemoval=true)
     * @ORM\OrderBy({"id" = "ASC"})
     */
    private $images;
}

Back on track, your annotations and Sonata Admin forms look fine, so I'm pretty sure you're missing one of those methods in your Project entity class:

public function __construct() {
    $this->images = new \Doctrine\Common\Collections\ArrayCollection();
}

public function setImages($images)
{
    if (count($images) > 0) {
        foreach ($images as $i) {
            $this->addImage($i);
        }
    }

    return $this;
}

public function addImage(\Acme\YourBundle\Entity\ProjectImage $image)
{
    $image->setProject($this);

    $this->images->add($image);
}

public function removeImage(\Acme\YourBundle\Entity\ProjectImage $image)
{
    $this->images->removeElement($image);
}

public function getImages()
{
    return $this->Images;
}

And in your Admin class:

public function prePersist($project)
{
    $this->preUpdate($project);
}

public function preUpdate($project)
{
    $project->setImages($project->getImages());
}
like image 153
Pier-Luc Gendreau Avatar answered Nov 11 '22 08:11

Pier-Luc Gendreau


Since some things have changed with Symfony form collection now adding the addChild() and removeChild() with the by_reference option set to false automatically persist the Collection and set the ID on the inverse side as expected.

Here is a full working version: https://gist.github.com/webdevilopers/1a01eb8c7a8290d0b951

protected function configureFormFields(FormMapper $formMapper)
{
    $formMapper
        ->add('childs', 'sonata_type_collection', array(
            'by_reference' => false
        ), array(
            'edit' => 'inline',
            'inline' => 'table'
        ))
    ;
}

The addChild() method must contain the setter for the parent on the child:

public function addChild($child)
{
    $child->setParent($this); // !important
    $this->childs[] = $child;
    return $this;
} 
like image 45
webDEVILopers Avatar answered Nov 11 '22 07:11

webDEVILopers


You ca do it directly in the preUpdate function

    public function prePersist($societate)
{
    $this->preUpdate($societate);
}

public function preUpdate($societate)
{
    $conturi = $societate->getConturi();
    if (count($conturi) > 0) {
        foreach ($conturi as $cont) {
            $cont->setSocietate($societate);
        }
    }
}
like image 22
Razvan.432 Avatar answered Nov 11 '22 09:11

Razvan.432


the simplest solution is, and naturally replace your variable names; this works for me: 'by_reference' => false

as follows:

protected function configureFormFields(FormMapper $formMapper)
{
    $formMapper
        ->add('name')
        ->add('articles', EntityType::class, array(
            'class' => 'EboxoneAdminBundle:Article',
            'required' => false,
            'by_reference' => false,
            'multiple' => true,
            'expanded' => false,
        ))
        ->add('formInputs', CollectionType::class, array(
            'entry_type' => FormInputType::class,
            'allow_add' => true,
            'allow_delete' => true,
            'by_reference' => false,
        ))
    ;
}
like image 1
Shadi Akil Avatar answered Nov 11 '22 08:11

Shadi Akil