【问题标题】:Sonata Admin Bundle One-to-Many relationship not saving foreign IDSonata Admin Bundle 一对多关系不保存外国 ID
【发布时间】:2013-06-04 07:54:06
【问题描述】:

我在将 SonataAdminBunle 与 symfony 2.2 结合使用时遇到问题。 我有一个 Project 实体和一个 ProjectImage 实体,并指定了这两者之间的一对多关系,如下所示:

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;
}

我已经配置了 ProjectAdmin 和 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
            ))
            ;
    }
}

问题是在数据库的 project_image 表中,project_id 没有保存,而所有其他数据和图像都保存了。在其他任何地方都找不到有效的答案。

【问题讨论】:

  • 你试过删除 by_reference 吗?
  • 是的,我确实尝试过。很遗憾没有结果。
  • 您的项目实体中是否有 setImages、addImage、removeImage 和 getImages 方法?
  • 我没有 setImages 方法,这应该是什么样子?
  • 知道了,谢谢。这里实际上给出了答案:github.com/sonata-project/SonataAdminBundle/issues/…

标签: symfony sonata-admin


【解决方案1】:

虽然不相关,但我会稍微调整您的一对多注释:

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

回到正轨,您的注释和 Sonata Admin 表单看起来不错,所以我很确定您在 Project 实体类中缺少这些方法之一:

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;
}

在你的管理类中:

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

public function preUpdate($project)
{
    $project->setImages($project->getImages());
}

【讨论】:

  • 这个奏鸣曲包有点毛茸茸的。 prePersist()|preUpdate() 的建议帮助了我.. 但我必须做一些不同的事情。查看要点 - gist.github.com/justinpfister/9634619
  • 也许你应该在setEntries 中改用foreach?在preUpdate 中这样做似乎很奇怪
【解决方案2】:

我解决的一种方法是通过自定义 Sonata 模型管理器手动设置所有反向关联。

<?php

namespace Sample\AdminBundle\Model;

class ModelManager extends \Sonata\DoctrineORMAdminBundle\Model\ModelManager
{
    /**
     * {@inheritdoc}
     */
    public function create($object)
    {
        try {
            $entityManager = $this->getEntityManager($object);
            $entityManager->persist($object);
            $entityManager->flush();
            $this->persistAssociations($object);
        } catch (\PDOException $e) {
            throw new ModelManagerException('', 0, $e);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function update($object)
    {       
        try {
            $entityManager = $this->getEntityManager($object);
            $entityManager->persist($object);
            $entityManager->flush();
            $this->persistAssociations($object);
        } catch (\PDOException $e) {
            throw new ModelManagerException('', 0, $e);
        }
    }

    /**
     * Persist owning side associations
     */
    public function persistAssociations($object)
    {       
        $associations = $this
            ->getMetadata(get_class($object))
            ->getAssociationMappings();

        if ($associations) {
            $entityManager = $this->getEntityManager($object);

            foreach ($associations as $field => $mapping) {
                if ($mapping['isOwningSide'] == false) {
                    if ($owningObjects = $object->{'get' . ucfirst($mapping['fieldName'])}()) {
                        foreach ($owningObjects as $owningObject) {
                            $owningObject->{'set' . ucfirst($mapping['mappedBy']) }($object);
                            $entityManager->persist($owningObject);
                        }
                        $entityManager->flush();
                    }
                }
            }
        }
    }
}

请务必在 services.yml 文件中将其定义为新服务:

services:
    sample.model.manager:
        class: Sample\AdminBundle\Model\ModelManager
        arguments: [@doctrine]


    sample.admin.business:
        class: Sample\AdminBundle\Admin\BusinessAdmin
        tags:
            - { name: sonata.admin, manager_type: orm, group: "Venues", label: "Venue" }
        arguments: [~, Sample\AppBundle\Entity\Business, ~]
        calls:
            - [ setContainer, [@service_container]]
            - [ setModelManager, [@sample.model.manager]]

【讨论】:

    【解决方案3】:

    您可以直接在 preUpdate 函数中执行此操作

        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);
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      由于 Symfony 表单集合发生了一些变化,现在添加 addChild()removeChild() 并将 by_reference 选项设置为 false 自动持久化 Collection 并按预期在反面设置 ID。

      这是一个完整的工作版本: 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'
              ))
          ;
      }
      

      addChild() 方法必须包含子级父级的设置器:

      public function addChild($child)
      {
          $child->setParent($this); // !important
          $this->childs[] = $child;
          return $this;
      } 
      

      【讨论】:

      • 这是最好的答案,因为它允许学说隐式设置关系,并将尊重非拥有方的级联定义。
      【解决方案5】:
      public function prePersist($user)
      {
          $this->preUpdate($user);
      }
      
      public function preUpdate($user)
      {
          $user->setProperties($user->getProperties());
      }
      

      这已经完美地解决了我的问题,谢谢!

      【讨论】:

        【解决方案6】:

        最简单的解决方案是,自然地替换你的变量名;这对我有用: 'by_reference' => 错误

        如下:

        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,
                ))
            ;
        }
        

        【讨论】:

          猜你喜欢
          • 2020-04-10
          • 2012-07-05
          • 1970-01-01
          • 2016-09-03
          • 1970-01-01
          • 1970-01-01
          • 2016-04-20
          • 2015-08-12
          • 2012-07-21
          相关资源
          最近更新 更多