Игнорировать дубликаты при использовании INSERT в базе данных с Symfony и Doctrine
у меня есть таблица
CREATE TABLE `sob_tags_articles` (
`tag_id` int(11) NOT NULL,
`article_id` int(11) NOT NULL,
`id` int(11) NOT NULL auto_increment,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=112
и попытка спасти объект с помощью доктрины:
$sbTagsArticles = new SobTagsArticles();
$sbTagsArticles->article_id = $pubId;
$sbTagsArticles->tag_id = $tagId;
$sbTagsArticles->save();
но если запись существует с тем же $pubId и $tagId новая запись будет insertet с новым PK.
как вставить IGNORE в таблицу с symfony?
$sbTagsArticles->isNew();
возвращает 1.
Thnx.
3 ответов
try
{
$record->save();
}
catch(Doctrine_Exception $e)
{
if($e->getErrorCode() !== $duplicateKeyCode)
{
/**
* if its not the error code for a duplicate key
* value then rethrow the exception
*/
throw $e;
}
/**
* you might want to fetch the real record here instead
* so yure working with the persisted copy
*/
}
вы должны убедиться, что одна и та же запись не существует на стороне приложения, а не на стороне SQL. Если вы никогда не хотите, чтобы одна и та же статья/тег существовала, добавьте уникальный индекс в (article_id, tag_id)
. Это должно генерировать ошибку mysql, которая, в свою очередь, генерирует исключение доктрины, которое вы можете поймать. Для сохранения нет флага ignore... Возможно, вы сможете использовать один, работающий на более низком уровне DBAL (Doctrine_Query, Doctrine_Connection и т. д..) но не напрямую из ORM слой.
Doctrine_Record::isNew()
всегда будет возвращать true, если у вас есть экземпляр record asopposed, чтобы вытащить его из БД, иначе у него нет способа узнать, что запись/не новая.
также почему вы используете механизм хранения MyISAM? Я уверен, что это на самом деле приведет к большим накладным расходам при использовании доктрины, так как тогда ей нужно эмулировать ограничения на стороне php. Обычно ваша схема будет выглядеть примерно так:
CREATE TABLE `sob_tags_articles` (
`tag_id` int(11) NOT NULL,
`article_id` int(11) NOT NULL,
`id` int(11) NOT NULL auto_increment,
PRIMARY KEY (`id`),
CONSTRAINT `some_unique_constraint_name_1`
FOREIGN KEY `article_id`
REFERENCES `article` (`id`)
ON DELETE CASCADE,
CONSTRAINT `some_unique_constraint_name_2`
FOREIGN KEY `tag_id`
REFERENCES `tag` (`id`)
ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=112
это фактический код, который будет использовать
try
{
$record->save();
}
catch(Doctrine_Connection_Exception $e)
{
if($e->getPortableCode() != Doctrine::ERR_ALREADY_EXISTS)
{
/**
* if its not the error code for a duplicate key
* value then rethrow the exception
*/
throw $e;
}
/**
* you might want to fetch the real record here instead
* so yure working with the persisted copy
*/
}
вы можете расширить объект SobTagsArticles новым методом сохранения и проверить, существует ли запись:
public function exists() {
$q = Doctrine_Query::create()
->from('sobtagsarticles ta')
->where('ta.tag_id = ? and ta.article_id = ?', array($this->getTagId(), $this->getArticleId()));
if (!$result = $q->execute())
{
parent::save();
}
}
таким образом, объект будет сохранен, только если он не существует.
вы также можете установить уникальный индекс в таблице вот так:
UNIQUE INDEX `sb_tags_articles_unique` (`tag_id` ASC, `article_id` ASC)
ваша схема будет выглядеть так:
CREATE TABLE `sob_tags_articles` (
`tag_id` int(11) NOT NULL,
`article_id` int(11) NOT NULL,
`id` int(11) NOT NULL auto_increment,
PRIMARY KEY (`id`),
UNIQUE INDEX `sb_tags_articles_unique` (`tag_id` ASC, `article_id` ASC),
CONSTRAINT `some_unique_constraint_name_1`
FOREIGN KEY `article_id`
REFERENCES `article` (`id`)
ON DELETE CASCADE,
CONSTRAINT `some_unique_constraint_name_2`
FOREIGN KEY `tag_id`
REFERENCES `tag` (`id`)
ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=112