4

私にはプロフィールと研究があります。一人で多くの研究を終えることができます。フォームは正しく表示されます。「新しいスタディを追加」ボタンがあり、jQuery を使用して、データ プロトタイプに基づいて別のサブフォームを追加すると、うまく機能します。このようなフォームを新しいサブフォームとともに送信すると、データベース エラーが発生します

Integrity constraint violation: 1048 Column 'profile_id' cannot be null 

このエラーは理解できますが、それを取り除く方法がわかりません。コントローラーでバインドした後にスタディのコレクションを更新できることはわかっていますが、注釈で適切に構成する方法があることを願っています。エンティティのみを更新すると、すべて正常に動作します。

コードは次のとおりです。

class Profile {
    /**
     * @var integer $profileId
     *
     * @ORM\Column(name="profile_id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $profileId;
...
    /**
     *
     * @var Study
     * @ORM\OneToMany(targetEntity="Study", mappedBy="profile", cascade={"persist", "remove"})
     */
    private $study;

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

    class Study {
    /**
     * @var integer $studyId
     *
     * @ORM\Column(name="study_id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $studyId;
...
    /**
     * @var Profile
     *
     * @ORM\ManyToOne(targetEntity="Profile")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="profile_id", referencedColumnName="profile_id")
     * })
     */
    private $profile;
...
}

s(g)ettersで。基盤となるデータベース構造は

CREATE TABLE IF NOT EXISTS `profile` (
  `profile_id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`profile_id`),
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `study` (
  `study_id` int(11) NOT NULL AUTO_INCREMENT,
  `profile_id` int(11) NOT NULL,
  PRIMARY KEY (`study_id`),
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

ALTER TABLE `study`
  ADD CONSTRAINT `study_fk2` FOREIGN KEY (`profile_id`) REFERENCES `profile` (`profile_id`);

フォーム ビルダーは次のとおりです。

class ProfileType extends AbstractType {

    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('study', 'collection', array(
                    'type' => new StudyType(),
                    'allow_add' => true,
                    'allow_delete' => true
                        )
                )
    }
...
}

class StudyType extends AbstractType {

    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
                ->add('city') //example field not included in above structures
    }
...
}

Javascript部分

function profileNewStudy() {
    var nr = $('[class^=profile_study_][class*=type2]').last().parent();
    nr = nr.attr('id').split('_');
    nr = nr.pop()*1 + 1;
    var proto = $('#profile_study').data('prototype');
    proto = proto.replace(/\$\$name\$\$/g, nr);
    $('#profile_study').append(proto).find('.profile_study_' + nr + ' input').val('qpa' + nr);
}

および Twig テンプレート

<form action="#" method="post" {{ form_enctype(form) }}>
    {{ form_widget(form) }}
    <input type="submit" value="Zapisz"/>
</form>

テストの目的で、データベースから study.profile_id の NOT NULL 制約を削除すると、study.profile_id=null 以外のすべてが保存されました。

@Gremoの回答後に編集

私はいくつかのテストを行いました。残念ながら、それは役に立ちませんでした:(コードで彼の間違いを修正しました

class Profile
    /**
     * @var Study
     * @ORM\OneToMany(targetEntity="Study", mappedBy="profile")
     */
    private $study;
class Study
    /**
     * @var Profile
     * @ORM\ManyToOne(targetEntity="Profile", inversedBy="study")
     * @ORM\JoinColumn(nullable=false, onDelete="CASCADE", referencedColumnName="profile_id")
     */
    private $profile;

新しい研究エンティティをフォームに追加してサーバーに投稿すると、エラーが発生しました: エンティティの永続化操作をカスケードするように構成されていない関係 'Alden\xxxBundle\Entity\Profile#study' を通じて、新しいエンティティが見つかりました: Alden \xxxBundle\Entity\Study@0000000073eb1a8b00000000198469ff. 新しいエンティティを明示的に永続化するか、関係に対してカスケード永続化操作を構成します。問題の原因となっているエンティティが見つからない場合は、'Alden\xxxxBundle\Entity\Study#__toString()' を実装して手がかりを得てください。

だから私はプロファイルにカスケードを追加しました:

/**
 * @var Study
 * @ORM\OneToMany(targetEntity="Study", mappedBy="profile", cascade={"persist"})
 */
private $study;

そして、最初のようなエラーが発生しました: SQLSTATE[23000]: 整合性制約違反: 1048 列 'profile_id' を null にすることはできません。

編集された 私のコントローラーコード:

    $request = $this->getRequest();
    $r = $this->getProfileRepository();
    $profile = $id ? $r->find($id) : new \Alden\BonBundle\Entity\Profile();
    /* @var $profile \Alden\BonBundle\Entity\Profile */
    $form = $this->createForm(new ProfileType(), $profile);
    if ($request->getMethod() == 'POST')
    {
        $form->bindRequest($request);
        if ($form->isValid())
        {
            $vacation = $profile->getVacation();
            foreach($vacation as $v) {
                $r=5;
            }
            $em = $this->getEntityManager();
            $em->persist($profile);
            $em->flush();
            //return $this->redirect($this->generateUrl('profile_list'));
        }
    }
    return array(
        'profile' => $profile,
        'form' => $form->createView()
    );

解決

Profile クラスでは、重要な部分は、注釈のカスケード、orphanRemoval、および setStudies() の foreach ループです (提案 @Parhs に感謝)

/**
 * @var \Doctrine\Common\Collections\ArrayCollection
 * @ORM\OneToMany(targetEntity="Study", mappedBy="profile", cascade={"ALL"}, orphanRemoval=true)
 */
private $studies;

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

/**
 * @return Doctrine\Common\Collections\Collection
 */
public function getStudies()
{
    return $this->studies;
}

public function setStudies($studies)
{
    foreach ($studies as $v)
    {
        if (is_null($v->getProfile()))
        {
            $v->setProfile($this);
        }
    }
    $this->studies = $studies;
}

スタディクラスで

/**
 * @var Profile
 * @ORM\ManyToOne(targetEntity="Profile", inversedBy="studies")
 * @ORM\JoinColumn(name="profile_id", nullable=false, onDelete="CASCADE", referencedColumnName="profile_id")
 */
private $profile;

そして通常のゲッターとセッター。

4

3 に答える 3

3

私があなたを正しく理解しているなら、あなたの関係は双方向であるため、所有側逆側を指定する必要があります。Doctrine 2のドキュメントとして:

  • 逆側は、OneToOne、OneToMany、またはManyToManyマッピング宣言のmappedBy属性を使用する必要があります
  • 所有側は、OneToOne、ManyToOne、またはManyToManyマッピング宣言のinversedBy属性を使用する必要があります
  • ManyToOneは常に所有側であり、OneToManyは常に 逆側です

だから私は私の最初の答えであなたの協会を台無しにしました。あなたの場合Profileは逆の側でなければなりませんが、研究は所有側でなければなりません。ただし、作業中なので、新しいエンティティを永続化するには、プロファイルに注釈を付けるProfile必要があります。cascade

class Profile
{
    /**
     * Bidirectional (INVERSE SIDE)
     *
     * @ORM\OneToMany(targetEntity="Study", mappedBy="profile",
     *     cascade={"persist", "remove"})
     */
    private $studies;
}

class Study
{
    /**
     * Bidirectional (OWNING SIDE - FK)
     * 
     * @ORM\ManyToOne(targetEntity="Profile", inversedBy="studies")
     * @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
     */
    private $profile;
}

あなたの例はDoctrine2のドキュメントの例とまったく同じであることに注意してください

于 2012-05-15T00:46:53.020 に答える
1

インスタンスにプロファイルを設定する必要があります。コントローラーで。コントローラー コードを貼り付けていません。

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html#use-case-1-dynamic-attributes

そこで起こっていることはフォームには当てはまりません..彼は実際にこの参照を持っています..うーん、これを追加することは、反復を避けるための一種の自動修正です.それが機能するかどうかはわかりません.

于 2012-05-15T20:37:36.533 に答える
0

あなたの解決策は私にはうまくいきませんでした。実際、私はすでにリレーションシップを設定し、加算器、リムーバー、ゲッターを定義していました。ソリューションを見た後、セッターを追加しました。

ただし、フォームが送信されたときにセッターが呼び出されませんでした。

私にとっての解決策は別のものでした。

adder メソッドで、所有者を設定する呼び出しを追加しました。

public function addStudie($studie)
{
     $studie->setProfile($this);
     $this->studies->add($studie);
}

フォームが送信されたときにこの adder が呼び出されるようにするには、フォーム コレクション フィールドに にby_reference設定されたプロパティを追加する必要がありますfalse

   $builder->add('study', 'collection', array(
                'type' => new StudyType(),
                'allow_add' => true,
                `by_reference` => false,
                'allow_delete' => true
                    )
            )

ドキュメントは次のとおりです。

同様に、基になるコレクション データがオブジェクトである CollectionType フィールドを使用している場合 (Doctrine の ArrayCollection のように)、加算器と除去器 (例: addAuthor() と removeAuthor()) が必要な場合は、by_reference を false に設定する必要があります。呼ばれます。

http://symfony.com/doc/current/reference/forms/types/collection.html#by-reference

于 2016-06-10T06:16:37.997 に答える