Документация

Начало работы

Модели (models)

Шаблоны (views) и маршрутизация (routes)

Формы

SQL запросы

Сессии и безопасность

Плагины

Загрузка из csv файлов

Для быстрого занесения и обновления записей в таблицах моделей можно использовать загрузку данных из файлов в формате ".csv", который может быть получен в результате выгрузки из MS Excel файла или другой системы. В csv файле данные представлены в виде строк, в которых значения разделены специальным символом обычно ";"). Таким образом все первые значения до разделителя относятся к первому столбцу, вторые - ко второму и т.д.

Ниже представлен пошаговый пример создания загрузчика данных из csv файла для модели товаров, класс "Products".

1. Создание кнопки

Необходимо на странице модели создать кнопку для перехода на страницу загрузчика, как описано в разделе Настройка модели. Создаем файл "customs/models/products-index-bottom.php" со следующим содержимым.

if($system -> user -> checkModelRights($system -> model -> getModelClass(), "update"))
    $update_path = "location.href='".$system -> registry -> getSetting("AdminPanelPath")."custom/?view=upload'";
else
    $update_path = "dialogs.showAlertMessage('{no_rights}')";
?>

<div class="extra-admin-buttons">
   <input type="button" class="button-light" value="Обновить товарную базу" onclick="<? echo $update_path; ?>" />
</div>

2. Создание страницы загрузчика

Создаем специальную страницу для будущего загрузчика, подробнее в разделе Дополнения к административной панели. Создаем файл "customs/adminpanel/upload.php".

<?
$products = new Products();
$done = false;
$back_url = $registry -> getSetting('AdminPanelPath')."model/?model=products";

//Ищем переданный файл csv
if(isset($_GET['action'], $_FILES['price']) && $_GET['action'] == "update")    
{
    //Если формат файла не верный - показываем ошибку
    if(Service :: getExtension($_FILES['price']['name']) != "csv")
        $system -> reload("custom/?view=upload&error=wrong-file"); 

    $data = file($_FILES['price']['tmp_name']); //Считываем данные из файла и помещаем их в массив

    //Обработка данных
    //Создание и обновление записей модели (моделей)
    ...
}

include $registry -> getSetting("IncludeAdminPath")."includes/header.php";
?>

<div id="columns-wrapper">
    <div id="model-form">
        <div class="column-inner">
            <h3 class="column-header">Обновление товарной базы</h3>
            <? 
                if($done)
                {
                    //Загрузка прошла успешно
                    //Отображение результатов
                    ...
                }
                //Обработка ошибок
                else if(isset($_FILES['price']))
                {
                    $error = "Дынные не были обновлены.";

                    if(isset($_GET['error']) && $_GET['error'] == "wrong-file")
                        $error .= " Неверный формат файла.";

                    echo '<div class="form-errors"><p>'.$error.'</p></div>';
                }
            ?>
            <div class="clear"></div>
            <p>Дата последнего обновления:
            <? echo $registry -> getDatabaseSetting("products_update"); ?></p>
            <p>Выберите файл товарной базы
            в формате .csv и нажмите "Отправить".</p>            
            <form method="post" enctype="multipart/form-data" action="?view=upload&action=update">
               <div>
                  <input type="file" name="price" />


                  <input type="submit" class="button-light" value="Отправить" />
                  <input type="button" class="button-dark" value="Назад" onclick="location.href='<? echo $back_url; ?>'" />
               </div>
            </form>
        </div>
    </div>
</div>

<?
include $registry -> getSetting("IncludeAdminPath")."includes/footer.php";
?>

3. Обработка данных

После считывание данных в массив $data необходимо пройтись по каждому элементу массива, который представляет собой строку с разделенными через символ ";" данными.

//Номера колонок: 0 - артикул (ключ), 1 - название и т.д.
$columns = array("articul" => 0, "name" => 1, "price" => 2, "quantity" => 3 "description" => 4);

//Берем все id товаров, потом в массиве останутся только не найденные товары
$missed_products = $products -> selectColumn(array("fields->" => "id"));

foreach($data as $key => $string) //Обрабатываем каждую строку
{
    $fields = explode(";", $string); //Разбиваем строку в массив полей

    //Если в строке не найдены все поля - пропускаем ее
    //Можно добавлять дополнительные проверки данных в строке
    if(!isset($fields[$columns["articul"]],
       $fields[$columns["name"]], $fields[$columns["price"]], 
       $fields[$columns["quantity"]],
       $fields[$columns["description"]])) 
        continue;

    foreach($fields as $key => $value) //Убираем лишние символы из полей
        $fields[$key] = trim($value);

    //Убираем лишние символы из числовых полей
    $fields[$columns["price"]] = preg_replace("/[^d]/ui", "", $fields[$columns["price"]]);
    $fields[$columns["number"]] = preg_replace("/[^d]/ui", "", $fields[$columns["quantiy"]]);

    //Обычно проводятся дополнительные преобразования и/или проверка
    //данных для помещения в запись

    $new_product = true; //По умолчанию товар новый (не в базе данных)

    //Пробуем найти товар в базе по артикулу
    $product = $products -> findRecord(array("articul" => $fields[$columns["articul"]]));

    if($product) //Товар найден, он уже есть в таблице
    {
        $new_product = false;
        $id_index = array_search($product -> id, $missed_products);
        unset($missed_products[$id_index]); //Убираем его id из не найденных товаров
    }
    else //Товар новый - создаем новую пустую запись
    {
        $product = $products -> getEmptyRecord();
        $product -> articul = $fields[$columns["articul"]];
    }

    //Активируем товар и присваиваем ему значения других полей
    $product -> active = 1;     
    $product -> name = $fields[$columns["name"]];
    $product -> price = $fields[$columns["price"]];
    $product -> number = $fields[$columns["quantity"]];
    $product -> parameters = $fields[$columns["description"]];

    if($new_product) //Создаем товар
    {
        $product -> create();
        $created_products ++;
    }
    else //Обновляем товар
    {
        $product -> update();
        $updated_products ++;
    }

    //Если есть не найденные в csv файле товары, отключаем их
    if(count($missed_products)) 
    {
        $products -> updateManyRecords(array("active" => 0), array("id->in" => implode(",", $missed_products)));
        $missed_products = count($missed_products);
    }

    $done = true; //Флаг успешной загрузки

    //Обновляем время последней загрузки
    $registry -> setDatabaseSetting("products_update", I18n :: getCurrentDateTime());
}

4. Отображение результатов

В данном случае отображаются только количества товаров, можно например вместо счетчиков создавать массивы артикулов и детально отображать данные о товарах.

if($done)
{
    echo '<div class="form-no-errors"><p>Данные успешно обновлены.</p></div>';
    echo "<p>Обновленные товары: ".$updated_products."
";
    echo "Добавленные товары: ".$created_products."
";
    echo "Не найденные в .csv файле товары: ".$missed_products."</p>";
}

5. Распространенные ошибки

  1. Присутствие символа ";" в csv файле не в качестве разделитель столбцов.
  2. Размер загружаемого файла слишком большой. Определяется настройками PHP и опцией "MaxFileSize" в файле "config/settings.php".
  3. Ограничение на максимальное время выполнение PHP скрипта.
  4. Слишком "замусоренные" данные в csv файле, которые необходимо "чистить" строковыми функциями или регулярными выражениями.

Предыдущий раздел

Фильтрация

Следующий раздел

Работа с сессиями