#php #symfony #doctrine
#php #symfony #доктрина
Вопрос:
я немного поиграл с Symfony2 и Doctrine2.
У меня есть объект, который имеет уникальный заголовок, например:
class listItem
{
/**
* @orm:Id
* @orm:Column(type="integer")
* @orm:GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @orm:Column(type="string", length="255", unique="true")
* @assert:NotBlank()
*/
protected $title;
теперь я извлекаю json и обновляю свою базу данных этими элементами:
$em = $this->get('doctrine.orm.entity_manager');
foreach($json->value->items as $item) {
$listItem = new ListItem();
$listItem->setTitle($item->title);
$em->persist($listItem);
}
$em->flush();
с первого раза работает нормально. но во второй раз я получаю ошибку sql (конечно): Integrity constraint violation: 1062 Duplicate entry
иногда мой файл json обновляется, и некоторые элементы являются новыми, некоторые — нет. Есть ли способ сообщить менеджеру объектов пропустить дубликаты файлов и просто вставить новые?
Каков наилучший способ сделать это?
Спасибо за любую помощь. Пожалуйста, оставьте комментарий, если что-то неясно
Редактировать:
что работает для меня, так это делать что-то вроде этого:
$uniqueness = $em->getRepository('ListItem')->checkUniqueness($item->title);
if(false == $uniqueness) {
continue;
}
$listItem = new ListItem();
$listItem->setTitle($item->title);
$em->persist($listItem);
$em->flush();
}
checkUniqueness
это метод в моем репозитории ListItem, который проверяет, есть ли заголовок уже в моей базе данных.
это ужасно. это 2 запроса к базе данных для каждого элемента. в итоге для этого действия выполняется около 85 запросов к базе данных.
Ответ №1:
Как насчет того, чтобы сначала извлечь все текущие заголовки в массив и сравнить вставляемый заголовок с текущими заголовками в этом массиве
$existingTitles = $em->getRepository('ListItem')->getCurrentTitles();
foreach($json->value->items as $item) {
if (!in_array($item->title, $existingTitles)) {
$listItem = new ListItem();
$listItem->setTitle($item->title);
$em->persist($listItem);
}
}
$em->flush();
getCurrentTitles() необходимо было бы добавить в репозиторий ListItem, чтобы просто вернуть массив заголовков.
Для этого требуется только один дополнительный запрос к БД, но для хранения текущих заголовков в массиве требуется больше памяти. С этим методом могут возникнуть проблемы, если ваш набор данных для ListItem очень большой.
Если количество элементов, которые вы хотите вставлять каждый раз, не слишком велико, вы могли бы изменить функцию getCurrentTitles() для запроса всех этих элементов с заголовками, которые вы пытаетесь вставить. Таким образом, максимальное количество $ existingTiles, которое вы вернете, будет размером вашего списка данных insert. Затем вы могли бы выполнить свои проверки, как указано выше.
// getCurrentTitles() - $newTitles is array of all new titles you want to insert
return $qb->select('title')
->from('Table', 't')
->in('t.title = ', $newTitles)
->getArrayResult();
Комментарии:
1. да, это действительно очень большой. нет ли способа сделать это на стороне базы данных?
2. Сколько элементов вы добавляете через AJAX за раз, это тоже много?
3. В доктрине 1 доступен метод replace() , но, похоже, я не могу найти какой-либо эквивалент в Доктрине 2.
4. Вам следует использовать memcached или redis queue И не забудьте заблокировать скрипт вставки между проверкой и вставкой!
5. По-прежнему происходит сбой, если два экземпляра скрипта выполняются одновременно, используя один и тот же ввод.
Ответ №2:
Если вы используете объект, который, возможно, уже существует в диспетчере, вам нужно объединить его.
Вот что я бы сделал (еще не тестировал это) :
$repository = $this->get('doctrine.orm.entity_manager');
foreach($json->value->items as $item) {
$listItem = new ListItem();
$listItem->setTitle($item->title);
$em->merge($listItem); // return a managed entity
// no need to persist as long as the entity is now managed
}
$em->flush();
Комментарии:
1.
merge()
необходимо, чтобы у нового объекта был установлен его идентификатор. В противном случае он считает это новой сущностью, и это то же самое, что использоватьpersist()
. Это не работает, если идентификатор генерируется базой данных.