Symfony2 / Doctrine2.1:使用简单的派生身份保持OneToOne关系

时间:2012-06-07 23:38:33

标签: symfony doctrine-orm

我正在尝试使用单个表单提交来保留具有配置文件实体的用户实体。按照Doctrine2 documentation上的说明添加其他属性后,这似乎足以实现目标。

实体

按照惯例设置entites是非常直接的,并导致了这个(我遗漏了生成的getter / setter):

// ...

/**
 * @ORM\Entity
 */
class User
{
  /**
   * @ORM\Id
   * @ORM\Column(type="integer")
   * @ORM\GeneratedValue
   */
  private $id;

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

  /**
   * @ORM\OneToOne(targetEntity="Profile", mappedBy="user", cascade={"persist", "remove"})
   */
  private $Profile;

  // ...
}

// ...

/**
 * @ORM\Entity
 */
class Profile
{
  /**
   * @ORM\Id
   * @ORM\OneToOne(targetEntity="User")
   */
  private $user;

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

  // ...
}

表单

现在修改表格并不太困难:

// ...

class ProfileType extends AbstractType
{
  public function buildForm(FormBuilder $builder, array $options)
  {
      $builder
          ->add('data')
      ;
  }

  public function getName()
  {
      return 'profile';
  }

  public function getDefaultOptions(array $options)
  {
      return array('data_class' => 'Acme\TestBundle\Entity\Profile');
  }
}

// ...

class TestUserType extends AbstractType
{
  public function buildForm(FormBuilder $builder, array $options)
  {
      $builder
          ->add('data')
          ->add('Profile', new ProfileType())
      ;
  }

  public function getName()
  {
      return 'user';
  }
}

控制器

class UserController extends Controller
{
  // ...

  public function newAction()
  {
      $entity = new User();
      $form   = $this->createForm(new UserType(), $entity);

      return array(
          'entity' => $entity,
          'form'   => $form->createView()
      );
  }

  public function createAction()
  {
      $entity  = new User();
      $request = $this->getRequest();
      $form    = $this->createForm(new UserType(), $entity);
      $form->bindRequest($request);

      if ($form->isValid()) {
          $em = $this->getDoctrine()->getEntityManager();
          $em->persist($entity);
          $em->flush();

          return $this->redirect($this->generateUrl('user_show',
              array('id' => $entity->getId())));

      }

      return array(
          'entity' => $entity,
          'form'   => $form->createView()
      );
    }

    // ...
}    

但现在是测试发生的部分。我开始创建一个新的用户对象,嵌入的表单按预期显示,但点击提交返回:

异常

  

Acme \ TestBundle \ Entity \ Profile类型的实体缺少   分配的ID。此实体的标识符生成策略   需要在EntityManager#persist()之前填充ID字段   叫做。如果您想要自动生成标识符   您需要相应地调整元数据映射。

我已经知道的一个可能的解决方案是在Profile实体上为独立主键添加一个额外的列。

但是我想知道是否有办法让映射保持大致相同但是处理持久化嵌入式表单呢?

2 个答案:

答案 0 :(得分:2)

经过IRC与几个人辩论了一段时间之后我修改了映射并想出了这个:

实体

// ...

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

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

    /**
     * @ORM\OneToOne(targetEntity="Profile", cascade={"persist", "remove"})
     */
    private $Profile;

    // ...
}

// ...

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

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

    // ...

}    

那么这会改变什么?首先,我删除了关系的mappedBy和inversedBy选项。此外,不需要Profile-entity上的OneToOne注释。

用户和配置文件之间的关系可以是双向的,但是与用户是拥有方的单向关系足以控制数据。由于级联选项,您可以确定没有剩余用户和用户可以维护配置文件但没有剩余的配置文件。

如果您想使用双向关系,我建议您查看Github: Doctrine2 - Tests - DDC117并特别注意文章和文章细节' OneToOne关系。但是,您需要注意,从测试文件(注释中提供的链接)可以看出,保存这种双向关系有点棘手:您需要先保留文章并在ArticleDetails::__construct中设置构造函数因此,要涵盖这种关系的双向性。

答案 1 :(得分:0)

我可以看到的问题是你只是在创建/保存用户对象。

由于用户/配置文件是一对一关系(用户是拥有方),因此可以安全地假设用户始终具有配置文件关系,因此可以在用户构造中初始化

class User 
{
    public function __construct()
    {
        $this->profile = new Profile();
    }
}

毕竟,您已将User设置为相关Profile对象的级联持久性。然后,您的实体经理将创建两个实体并建立关系。