Параметры доктрины каскада для OneToMany
Мне нелегко осмыслить руководство "Doctrine manual" объяснение каскадных операций и нужно, чтобы кто-то помог мне понять варианты в условия простого отношения ManyToOne.
В моем приложении у меня есть таблица/объект с именем Article, который имеет поле внешнего ключа, ссылающееся на поле id в таблице/объекте с именем Topic.
Когда я создаю новую статью, я выбираю тему из выпадающего меню. Это вставляет целое число в поле "topic_id" внешнего ключа в таблице "Статьи".
У меня есть ассоциация $topic, созданная в объекте Article следующим образом:
/**
* @ManyToOne(targetEntity="Topic")
* @JoinColumn(name="topic_id", referencedColumnName="id", nullable=false)
*/
private $topic;
У объекта темы нет взаимной аннотации относительно объекта Article. Темы не заботятся о том, что статьи ссылаются на них, и ничего не должно произойти с темой, когда статья, ссылающаяся на тему, удаляется.
Поскольку я не указываю каскадную операцию в объекте Article, Doctrine выдает ошибку при попытке создать новую статью: "Новый объект был найден через отношения, которые не были настроены на каскадное сохранение операций. Явно сохраняю новый объект или сконфигурировать каскадные операции с сохранением отношения."
Итак, я знаю, что мне нужно выбрать каскадную операцию для включения в объект Article, но как узнать, какую операцию выбрать в этой ситуации?
Из прочитанного руководства Doctrine, "отсоединить" звучит как правильный вариант. Но изучение других подобных вопросов здесь и здесь заставляет меня думать, что я хочу использовать вместо этого "persist".
Может ли кто-нибудь помочь мне понять, что "сохраняются", "удалять", "сливать" и "отделять" означать в терминах простого отношения ManyToOne, подобного тому, которое я описал?
Ответы
Ответ 1
В документации Doctrine2 "9.6. Transitive persistence/Cascade Operations" есть несколько примеров того, как вы должны настраивать свои сущности, чтобы при сохранении статьи $$ topic также будет сохраняться. В вашем случае я бы предложил эту аннотацию для объекта Subject:
/**
* @OneToMany(targetEntity="Article", mappedBy="topic", cascade={"persist", "remove"})
*/
private $articles;
Недостатком этого решения является то, что вы должны включать $articles collection в объект Subject, но вы можете оставить его закрытым без getter/setter.
И как упоминал @kurt-krueckeberg, вы должны передать реальный объект Subject при создании новой статьи, то есть:
$topic = $em->getRepository('Entity\Topic')->find($id);
$article = new Article($topic);
$em->persist($article);
$em->flush();
// perhaps, in this case you don't even need to configure cascade operations
Удачи!
Ответ 2
Если у вас есть однонаправленная связь @OneToMany, как описано в разделе 6.10 "Ссылка на доктрину", скорее всего, вы забыли сохранить тему перед вызовом флеша. Не устанавливайте первичный ключ topic_id в статье. Вместо этого установите экземпляр темы.
Например, данные объекты Article и Topic, подобные этим:
<?php
namespace Entities;
/**
@Entity
@Table(name="articles")
*/
class Article {
/**
* @Id
* @Column(type="integer", name="article_id")
* @GeneratedValue
*/
protected $id;
/**
* @Column(type="text")
*/
protected $text;
/**
* @ManyToOne(targetEntity="Topic", inversedBy="articles")
* @JoinColumn(name="topic_id", referencedColumnName="topic_id")
*/
protected $topic;
public function __construct($text=null)
{
if (!is_null($text)) {
$this->text = $text;
}
}
public function setArticle($text)
{
$this->text = $text;
}
public function setTopic(Topic $t)
{
$this->topic = $t;
}
}
<?php
namespace Entities;
/**
@Entity
@Table(name="topics")
*/
class Topic {
/**
* @Id
* @Column(type="integer", name="topic_id")
* @GeneratedValue
*/
protected $id;
public function __construct() {}
public function getId() {return $this->id;}
}
После создания схемы:
# doctrine orm:schema-tool:create
ваш код для сохранения этих объектов будет выглядеть как-то, что
//configuration omitted..
$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);
$topic = new Entities\Topic();
$article1 = new Entities\Article("article 1");
$article2 = new Entities\Article("article 2");
$article1->setTopic($topic);
$article2->setTopic($topic);
$em->persist($article1);
$em->persist($article2);
$em->persist($topic);
try {
$em->flush();
} catch(Exception $e) {
$msg= $e->getMessage();
echo $msg . "<br />\n";
}
return;
Надеюсь, это поможет.