Вставить / обновить объект Doctrine из Excel - PullRequest
2 голосов
/ 31 августа 2010

В проекте, над которым я сейчас работаю, мне нужно прочитать файл Excel (содержащий более 1000 строк), извлечь все из них и вставить / обновить в таблицу базы данных.

  1. с точки зрения производительности, лучше добавить все записи в Doctrine_Collection и вставить / обновить их после использования метода fromArray(), верно? Еще один возможный подход заключается в создании нового объекта для каждой строки (строка Excel будет объектом), и они сохраняют его, но я думаю, что это хуже всего с точки зрения производительности.

  2. Каждый раз, когда Excel загружается, необходимо сравнивать его строки с существующими объектами в базе данных. Если строка не существует как объект, должна быть вставлена, в противном случае обновлена. Мой первый подход состоял в том, чтобы превратить и объект, и строки в массивы (или Doctrine_Collections); затем сравните оба массива перед выполнением необходимых операций.

Может кто-нибудь предложить мне другой возможный подход?

Ответы [ 2 ]

2 голосов
/ 02 сентября 2010

Недавно мы сделали это в проекте с данными CSV. это было довольно безболезненно. Есть плагин Symfony tmCsvPlugin, но мы его немного расширили, так как версия в репозитории плагинов довольно устарела. Нужно добавить это в список @TODO:)

Вопрос 1:

Я не знаю точно о производительности, но я бы предположил, что добавление записей в Doctrine_Collection и затем вызов Doctrine_Collection :: save () было бы самым подходящим подходом. Я уверен, что это было бы удобно, если бы где-то было сгенерировано исключение, и вам пришлось откатиться при последнем сохранении ..

Вопрос 2:

Если бы вы могли использовать поле строки в качестве уникального идентификатора (предположим, имя пользователя), то вы могли бы искать существующую запись. Если вы найдете запись и предполагаете, что ваша импортированная строка является массивом, используйте Doctrine_Record :: synchronizeWithArray (), чтобы обновить эту запись; затем добавьте его в коллекцию Doctrine_Collection. Когда закончите, просто вызовите Doctrine_Collection :: save ()

Довольно грубая и готовая реализация:

// set up a new collection
$collection = new Doctrine_Collection('User');

// assuming $row is an associative 
// array representing one imported row.

foreach ($importedRows as $row) {

    // try to find an existing record 
    // based on a unique identifier.
    $user = Doctrine_Core::getTable('User')
        ->findOneByUsername($row['username']);

    // create a new user record if 
    // no existing record is found.
    if (!$user instanceof User) {
        $user = new User();
    }

    // sync record with current data.
    $user->synchronizeWithArray($row);

    // add to collection.
    $collection->add($user);

}

// done. save collection.
$collection->save();

Довольно грубо, но что-то вроде этого хорошо сработало для меня. Это предполагает, что вы можете каким-то образом использовать импортированные данные строк в качестве уникального идентификатора.

ПРИМЕЧАНИЕ: будьте осторожны с synchronizeWithArray (), если вы используете sf1.2 / doctrine 1.0 - если я правильно помню, он не был реализован правильно. в доктрине 1.2 это работает нормально.

1 голос
/ 02 сентября 2010

Я никогда не работал над Doctrine_Collections, но я могу ответить с точки зрения запросов к базе данных и логики кода в более широком смысле. Я бы применил следующую логику: -

  1. Извлечь все строки таблицы Excel из базы данных в одном запросе и сохранить их в массиве $uploadedSheet.

  2. Создайте единый массив из всех строк загруженного листа Excel, назовите его $storedSheet. Я предполагаю, что структуры Doctrine_Collections $uploadedSheet и $storedSheet будут похожи (обе двумерные - строки, ячейки можно идентифицировать и сравнить).

3.Запустите циклы foreach на $uploadedSheet следующим образом и идентифицируют только те строки, которые необходимо вставить и которые нужно обновить (выполните фактические запросы позже) -

$rowsToBeUpdated =array();
$rowsToBeInserted=array();
foreach($uploadedSheet as $row=>$eachRow)  
{  
    if(is_array($storedSheet[$row]))
    {
         foreach($eachRow as $column=>$value)
         {
              if($value != $storedSheet[$row][$column]) 
              {//This is a representation of comparison
                  $rowsToBeUpdated[$row]=true;
                  break; //No need to check this row anymore - one difference detected.
              }
         }
    }
    else
    {
         $rowsToBeInserted[$row] = true;
    }
} 

4. Таким образом, у вас есть два массива. Теперь выполните 2 запроса к базе данных -

  • массовая вставка всех строк $uploadedSheet, номера которых хранятся в массиве $rowsToBeInserted.

  • массовое обновление всех строк $uploadedSheet, номера которых хранятся в массиве $rowsToBeUpdated.

Эти массовые запросы являются ключом к повышению производительности.

Дайте мне знать, помогло ли это, или вы хотели узнать что-то еще.

...