Symfony2表单和Doctrine2 - 更新指定实体的外键失败[已解决] [英] Symfony2 form and Doctrine2 - update foreign key in assigned entities fails [solved]
问题描述
完整性约束违规:1048列'profile_id'不能为空
我明白这个错误,但我不知道如何摆脱它。我知道我可以在控制器中绑定后更新研究收藏,但我希望有一种方法可以在注释中正确配置。当我只更新实体时,一切都可以正常工作。
代码是:
class Profile {
/ **
* @var integer $ profileId
*
* @ ORM\Column(name =profile_id,type =integer nullable = false)
* @ ORM\Id
* @ ORM\GeneratedValue(strategy =IDENTITY)
* /
private $ profileId;
...
/ **
*
* @var学习
* @ 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个人资料
*
* @ ORM\ManyToOne(targetEntity =个人资料)
* @ 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`)参考`profile`(`profile_id`);
表单buidlers是:
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 $选项)
{
$ builder
- > add('city')//上面结构中不包含的示例字段
}
...
}
Javascript部分
function profileNewStudy(){
var nr = $('[class ^ = profile_study _] [类* = 2型])最后()的父()。;
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 =submitvalue =Zapisz/>
< / form>
为了测试目的,我从study.profile_id上的数据库约束NOT 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;
当我在表单中添加新的Study实体并将其发布到服务器时,我发现错误:通过关系Alden\xxxBundle\Entity\Profile#study找到一个新实体配置为级联实体的持久化操作:Alden\xxxBundle\Entity\Study @ 0000000073eb1a8b00000000198469ff。明确地保留新实体或对关系配置级联持久性操作,如果y ou找不到哪个实体导致问题实现'Alden\xxxxBundle\Entity\Study #__ toString()'得到一个线索。
所以我添加了级联到个人资料:
/ **
* @var学习
* @ ORM\OneToMany(targetEntity =Study,mappedBy =profile,cascade = {persist})
* /
private $研究;
而不是像在开始时遇到的错误:SQLSTATE [23000]:违反完整性约束:1048列profile_id不能为空。
编辑
我的控制器代码:
$ 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'));
}
}
返回数组(
'profile'=> $ profile,
'form'=> $ form-> createView()
);
解决方案
在Profile类中,重要的部分是在setStudies()中的注释和foreach循环中的cascade,orphanRemoval(感谢建议@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($研究为$ v)
{
if(is_null $ v-> getProfile()))
{
$ v-> setProfile($ this);
}
}
$ this-> studies = $ studies;
}
在学习课
/ **
* @var个人资料
* @ ORM\ManyToOne(targetEntity =个人资料,inversedBy =研究)
* @ ORM\JoinColumn(name =profile_id,nullable = false,onDelete =CASCADE,referencedColumnName =profile_id)
* /
private $ profile;
和通常的getter和setter。
如果我正确理解您的关系是双向的,则必须指定拥有方和反方。作为Doctrine 2文档:
- 反面必须使用OneToOne的mappedBy属性,
OneToMany或ManyToMany映射声明 - 拥有方必须使用OneToOne,
ManyToOne或ManyToMany映射声明的inversedBy属性, li>
- ManyToOne始终是拥有的一方,OneToMany始终是
反面
所以我弄错了你的协会在我的第一个答案。在你的情况下,简介
必须是相反的一面,而学习应该是拥有的一面。但是您正在使用配置文件
,因此您需要在配置文件上使用级联
注释来持久存储新实体:
class Profile
{
/ **
*双向(INVERSE SIDE)
*
* @ ORM\OneToMany(targetEntity =Study,mappedBy =profile,
* cascade = {persist,remove})
* /
private $研究;
}
类学习
{
/ **
*双向(OWNING SIDE - FK)
*
* ORM\ManyToOne(targetEntity =Profile,inversedBy =研究)
* @ ORM\JoinColumn(nullable = false,onDelete =CASCADE)
* /
private $简介;
}
请注意,您的示例与那个在Doctrine2文档。
I have profiles and studies. One person can finish many studies. The form renders correctly. There is a button "Add new study" and with jQuery I add another subform based on data-prototype and this works well. When I submit such a form with new subforms I get an database error
Integrity constraint violation: 1048 Column 'profile_id' cannot be null
I understand this error but I don't know how to get rid of it. I know I can update collection of studies after binding in controller but I hope there is a way to configure it properly in annotations. When I only update entities everything works fine.
The code is:
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();
}
...
}
and
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;
...
}
with s(g)etters. Underlaying database structure is
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`);
Form buidlers are:
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
)
)
}
...
}
and
class StudyType extends AbstractType {
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('city') //example field not included in above structures
}
...
}
The Javascript part
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);
}
and Twig template
<form action="#" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<input type="submit" value="Zapisz"/>
</form>
For testing purposes I removed from database constraint NOT NULL on study.profile_id and then everything was saved except that study.profile_id=null.
edited after @Gremo answer
I did some tests. Unfortunately it didn't help :( I corrected his mistakes with code
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;
and when I added new Study entity in form and posted it to server I got an error: A new entity was found through the relationship 'Alden\xxxBundle\Entity\Profile#study' that was not configured to cascade persist operations for entity: Alden\xxxBundle\Entity\Study@0000000073eb1a8b00000000198469ff. Explicitly persist the new entity or configure cascading persist operations on the relationship. If you cannot find out which entity causes the problem implement 'Alden\xxxxBundle\Entity\Study#__toString()' to get a clue.
So I added cascade to profile:
/**
* @var Study
* @ORM\OneToMany(targetEntity="Study", mappedBy="profile", cascade={"persist"})
*/
private $study;
and than I got an error like in the begining: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'profile_id' cannot be null.
edited My controller code:
$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()
);
SOLUTION
In Profile class, important parts are cascade, orphanRemoval in annotations and foreach loop in setStudies() (thanks to suggestion @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;
}
In Study class
/**
* @var Profile
* @ORM\ManyToOne(targetEntity="Profile", inversedBy="studies")
* @ORM\JoinColumn(name="profile_id", nullable=false, onDelete="CASCADE", referencedColumnName="profile_id")
*/
private $profile;
and usual getters and setters.
If i understand you correctly your relation is bidirectional, so you have to specify an owning side and an inverse side. As Doctrine 2 documentation:
- The inverse side has to use the mappedBy attribute of the OneToOne, OneToMany, or ManyToMany mapping declaration
- The owning side has to use the inversedBy attribute of the OneToOne, ManyToOne, or ManyToMany mapping declaration
- ManyToOne is always the owning side and OneToMany is always the inverse side
So i messed up with your association in my first answer. In your case Profile
has to be the inverse side while study should be the owning side. But you are working on Profile
, so you need the cascade
annotation on profile to persist new entities:
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;
}
Note that your example is exactly the same as that on Doctrine2 documentation.
这篇关于Symfony2表单和Doctrine2 - 更新指定实体的外键失败[已解决]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!