Полный цикл в digital

Свой модуль "очистка папки"

Готовый код можно скачать в моем репозитории на GitFlic.

Cоздадим модуль, который будет удалять из папки /upload/iblock/ файлы отсутствующие в таблице b_file. Логика работы простая, если файл физически существует но его нет в базе, значит файл забыли удалить, например при редактирование страницы.

Модуль имеет ряд настроек, доступных из админки, например можно делать резервную копию удаляемых файлов, которые будут сохранены в папке /upload/iblock_Backup/. Информация об удаленных файлах записывается в таблицу hmarketing_delete, которая автоматически будет создана при установке модуля и текстовый файл который будет создан в директории /upload/iblock_Backup/ при запуске модуля из админки сайта.

Структура модуля

  • Папка hmarketing.upload
  • Папка admin
    • Файл menu.php меню в админке Битрикс
  • Папка install
    • Файл index.php основной файл установки
    • Файл version.php файл с версией и датой выхода модуля
    • Файл instalInfo.php вывод сообщения в админке сайта об ошибке или успехе при установке модуля
    • Файл deInstalInfo.php вывод сообщения в админке сайта об ошибке или успехе при удалении модуля
  • Папка lang
    • Папка ru
      • Папка lib
        • Файл data.php языковой файл базы данных
      • Папка install
        • Файл instalInfo.php языковой файл вывода сообщения в админке сайта об ошибке при установке модуля
        • Файл deInstalInfo.php языковой файл вывода сообщения в админке сайта об ошибке при удалении модуля
        • Файл index.php языковой файл основного файла установки
  • Папка lib
    • Файл Main.php основной класс модуля
    • Файл dataFile.php сущность ORM для таблицы b_file
    • Файл dataTable.php сущность ORM для таблицы hmarketing_upload_delete
  • Файл include.php подключение файлов модуля
  • Файл options.php настройка модуля в админке

Файл /admin/menu.php

Файл вызывается ядром Битрикс автоматически при установке модуля.

Данный файл создаёт пункты меню в административном интерфейсе Битрикс:

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

local/modules/hmarketing.upload/admin/menu.php<?php
/**
 * расположение модуля в админки
 * 
 */
defined('B_PROLOG_INCLUDED') and (B_PROLOG_INCLUDED === true) or die();
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
// основной массив $aMenu
$aMenu = array(
    // оснавная ветка меню
    array(
        // пункт меню в разделе Контент
        'parent_menu' => 'global_menu_services',
        // сортировка
        'sort' => 1,
        // название пункта меню
        'text' => "Очистка папки upload",
        // идентификатор ветви
        "items_id" => "menu_webforms",
        // иконка
        "icon" => "form_menu_icon",
        // дочерния ветка меню
        'items' => array(
            array(
                // название подпункта меню
                'text' => 'Удаление файлов в /upload/iblock/',
                // ссылка для перехода
                'url' => 'settings.php?lang=ru&mid=hmarketing.upload',
            ),
        )
    ),
);
// возвращаем основной массив $aMenu
return $aMenu;

Файл /install/index.php

Файл вызывается ядром Битрикс автоматически при установке модуля.

Инсталлятор должен находиться в директории /install/index.php. Имя класса должно соответствовать названию папке модуля и являться наследником от CModule. Только вместо точки, если мы делаем партнерский модуль стоит прописать нижние подчеркивание, это нужно для определения модуля в системе. Файл выполняется при установке или удалении модуля через административный интерфейс.

local/modules/hmarketing.upload/install/index.php<?php
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// пространство имен для управления (регистрации/удалении) модуля в системе/базе
use Bitrix\Main\ModuleManager;
// пространство имен с абстрактным классом для любых приложений, любой конкретный класс приложения является наследником этого абстрактного класса
use Bitrix\Main\Application;
// пространство имен для работы c ORM
use \Bitrix\Main\Entity\Base;
// пространство имен для автозагрузки модулей
use \Bitrix\Main\Loader;
// пространство имен для работы с параметрами модулей хранимых в базе данных
use Bitrix\Main\Config\Option;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
/**
 * основной класс для установки модуля
 * 
 */
class Hmarketing_Upload extends CModule
{
    /**
     * ID модуля
     *
     * @var string
     */
    public  $MODULE_ID;
    /**
     * версия модуля
     *
     * @var string
     */
    public  $MODULE_VERSION;
    /**
     * дата релиза версии модуля
     *
     * @var string
     */
    public  $MODULE_VERSION_DATE;
    /**
     * название модуля
     *
     * @var string
     */
    public  $MODULE_NAME;
    /**
     * описание модуля
     *
     * @var string
     */
    public  $MODULE_DESCRIPTION;
    /**
     * имя партнера выпустившего модуль
     *
     * @var string
     */
    public  $PARTNER_NAME;
    /**
     * ссылка на рисурс партнера выпустившего модуль
     *
     * @var string
     */
    public  $PARTNER_URI;
    /**
     * в конструкторе заполняем свойства
     *
     * @return void
     */
    function __construct()
    {
        // создаем пустой массив для файла version.php
        $arModuleVersion = array();
        // подключаем файл version.php
        include_once(__DIR__ . '/version.php');
        // версия модуля
        $this->MODULE_VERSION = $arModuleVersion['VERSION'];
        // дата релиза версии модуля
        $this->MODULE_VERSION_DATE = $arModuleVersion['VERSION_DATE'];
        // id модуля
        $this->MODULE_ID = "hmarketing.upload";
        // название модуля
        $this->MODULE_NAME = "Удаление файлов в /upload/iblock/";
        // описание модуля
        $this->MODULE_DESCRIPTION = "Удаление ненужных файлов в директории /upload/iblock/";
        // имя партнера выпустившего модуль
        $this->PARTNER_NAME = "Эйч Маркетинг";
        // ссылка на рисурс партнера выпустившего модуль
        $this->PARTNER_URI = "https://hmarketing.ru";
    }
    /**
     * метод отрабатывает при установке модуля
     *
     * @return void
     */
    function DoInstall()
    {
        // глобальная переменная с обстрактным классом
        global $APPLICATION;
        // регистрируем модуль в системе
        ModuleManager::RegisterModule("hmarketing.upload");
        // создаем таблицы баз данных, необходимые для работы модуля
        $this->InstallDB();
        // подключаем скрипт с административным прологом и эпилогом
        $APPLICATION->includeAdminFile(
            Loc::getMessage('INSTALL_TITLE'),
            __DIR__ . '/instalInfo.php'
        );
        // для успешного завершения, метод должен вернуть true
        return true;
    }
    /**
     * метод отрабатывает при удалении модуля
     *
     * @return void
     */
    function DoUninstall()
    {
        // глобальная переменная с обстрактным классом
        global $APPLICATION;
        // удаляем таблицы баз данных, необходимые для работы модуля
        $this->UnInstallDB();
        // удаляем регистрацию модуля в системе
        ModuleManager::UnRegisterModule("hmarketing.upload");
        // подключаем скрипт с административным прологом и эпилогом
        $APPLICATION->includeAdminFile(
            Loc::getMessage('DEINSTALL_TITLE'),
            __DIR__ . '/deInstalInfo.php'
        );
        // для успешного завершения, метод должен вернуть true
        return true;
    }
    /**
     * метод для создания таблицы баз данных
     *
     * @return void
     */
    function InstallDB()
    {
        // подключаем модуль для того что бы был видем класс ORM
        Loader::includeModule($this->MODULE_ID);
        // через класс Application получаем соединение по переданному параметру, параметр берем из ORM-сущности (он указывается, если необходим другой тип подключения, отличный от default), если тип подключения по умолчанию, то параметр можно не передавать. Далее по подключению вызываем метод isTableExists, в который передаем название таблицы полученное с помощью метода getDBTableName() класса Base
        if (!Application::getConnection(\Hmarketing\Upload\DataTable::getConnectionName())->isTableExists(Base::getInstance("\Hmarketing\Upload\DataTable")->getDBTableName())) {
            // eсли таблицы не существует, то создаем её по ORM сущности
            Base::getInstance("\Hmarketing\Upload\DataTable")->createDbTable();
        }
    }
    /**
     * мметод для удаления таблицы баз данных
     *
     * @return void
     */
    function UnInstallDB()
    {
        // подключаем модуль для того что бы был видем класс ORM
        Loader::includeModule($this->MODULE_ID);
        // делаем запрос к бд на удаление таблицы, если она существует, по подключению к бд класса Application с параметром подключения ORM сущности
        Application::getConnection(\Hmarketing\Upload\DataTable::getConnectionName())->queryExecute('DROP TABLE IF EXISTS ' . Base::getInstance("\Hmarketing\Upload\DataTable")->getDBTableName());
        // удаляем параметры модуля из базы данных битрикс
        Option::delete($this->MODULE_ID);
    }
    /**
     * метод для создания обработчика событий
     *
     * @return bool для успешного завершения, метод должен вернуть true
     */
    function InstallEvents()
    {
        // для успешного завершения, метод должен вернуть true
        return true;
    }
    /**
     * метод для удаления обработчика событий
     *
     * @return bool для успешного завершения, метод должен вернуть true
     */
    function UnInstallEvents()
    {
        // для успешного завершения, метод должен вернуть true
        return true;
    }
    /**
     * метод для копирования файлов модуля при установке
     *
     * @return bool для успешного завершения, метод должен вернуть true
     */
    function InstallFiles()
    {
        // для успешного завершения, метод должен вернуть true
        return true;
    }
    /**
     * метод для удаления файлов модуля при удалении
     *
     * @return bool для успешного завершения, метод должен вернуть true
     */
    function UnInstallFiles()
    {
        // для успешного завершения, метод должен вернуть true
        return true;
    }
}

Файл /install/version.php

Файл вызывается при установки модуля из /install/index.php

Номер версии модуля должен храниться в файле /install/version.php в виде массива. Плюс к этому нужно в классе модуля записывать эти значения. В связи с этим нужно налепить небольшую городушку, которая будет брать значение из этого файла и записывать его в модуль:

local/modules/hmarketing.upload/install/version.php<?php
/**
 * версия модуля
 * 
 */
$arModuleVersion = array(
    'VERSION'      => '1.0.0',
    'VERSION_DATE' => '2023-01-01 10:00:00'
);

Файл /install/instalInfo.php

Файл вызывается при установки модуля из /install/index.php

Файл вывод сообщения в админке сайта об ошибке или успехе при установке модуля:

local/modules/hmarketing.upload/install/instalInfo.php<?php
/**
 * статус установки
 * 
 */
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
// метод возвращает объект класса CApplicationException, содержащий последнее исключение
if ($errorException = $APPLICATION->getException()) {
    // вывод сообщения об ошибке при установке модуля
    CAdminMessage::showMessage(
        Loc::getMessage('INSTALL_FAILED') . ': ' . $errorException->GetString()
    );
} else {
    // вывод уведомления при успешной установке модуля
    CAdminMessage::showNote(
        Loc::getMessage('INSTALL_SUCCESS')
    );
}
?>
<!-- Кнопка возврата к списку модулей -->
<form action="<?= $APPLICATION->getCurPage(); ?>">
    <input type="submit" value="<?= Loc::getMessage('RETURN_MODULES'); ?>">
</form>

Файл /install/deInstalInfo.php

Файл вызывается при удалении модуля из /install/index.php

Файл вывод сообщения в админке сайта об ошибке или успехе при удалении модуля:

local/modules/hmarketing.upload/install/deInstalInfo.php<?php
/**
 * статус удаления
 * 
 */
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
// метод возвращает объект класса CApplicationException, содержащий последнее исключение
if ($errorException = $APPLICATION->getException()) {
    // вывод сообщения об ошибке при удалении модуля
    CAdminMessage::showMessage(
        Loc::getMessage('DEINSTALL_FAILED') . ': ' . $errorException->GetString()
    );
} else {
    // вывод уведомления при успешном удалении модуля
    CAdminMessage::showNote(
        Loc::getMessage('DEINSTALL_SUCCESS')
    );
}
?>
<!-- Кнопка возврата к списку модулей -->
<form action="<?= $APPLICATION->getCurPage(); ?>">
    <input type="submit" value="<?= Loc::getMessage('RETURN_MODULES'); ?>">
</form>

Файлы в дириктории /lang/ru/

Файл вызывается ядром Битрикс автоматически при установке модуля.

Файлы служат для реализации переводов, в них находится простой массив $MESS в котором хранятся пары ключ=>значение перевода, часть ru в пути к файлу является указанимем на язык перевода:

Файл /lib/Main.php

Файл подключается из файла /include.php.

В данном основном файле будет храниться логика работы модуля, основной класс со всеми методами.

local/modules/hmarketing.upload/lib/Main.php<?php
/**
 * @author Эйч Маркетинг <info@hmarketing.ru>
 * @copyright 2014-2024 The hmarketing.ru
 */
// пространство имен модуля
namespace Hmarketing\Upload;
// пространство имен для работы с датой
use Bitrix\Main\Type\DateTime;
// пространство имен для ORM
use \Hmarketing\Upload\DataTable;
// пространство имен для b_file
use \Hmarketing\Upload\FileTable;
/**
 * основной класс модуля
 * 
 */
class Main
{
    /**
     * удалять найденые файлы
     *
     * @var string
     */
    public $deleteFiles;
    /**
     * создавать бэкап файлов
     *
     * @var string
     */
    public $saveBackup;
    /**
     * папка для бэкапа
     *
     * @var string
     */
    public $patchBackup;
    /**
     * целевая папка для поиска файлов
     *
     * @var string
     */
    public $rootDirPath;
    /**
     * файл для записи данных    
     *
     * @var mixed
     */
    public $file;
    /**
     * массив для записи файлов из таблицы b_file
     *
     * @var array
     */
    public $arFilesCache = array();
    /**
     * конструктор
     *
     * @return void
     */
    function __construct()
    {
        // папка для бэкапа
        $this->patchBackup = $_SERVER['DOCUMENT_ROOT'] . "/upload/iblock_Backup/";
        // целевая папка для поиска файлов
        $this->rootDirPath = $_SERVER['DOCUMENT_ROOT'] . "/upload/iblock";
        // создаем пустой файл
        file_put_contents($this->file = $this->patchBackup . date('H.i.s_d.m.Y') . '.txt', '');
        // вызываем метод получения данных модуля из базы
        $this->Option();
        // вызываем метод создания папки для бекапа
        $this->DirPatchBackup();
        // вызываем метод создания массива с файлами из базы
        $this->ArFiles();
        // вызываем метод создания пути
        $this->Path();
    }
    /**
     * получение из базы опций настройки модуля  
     *
     * @return void
     */
    function Option()
    {
        // получаем значения из настроек модуля всех полей
        $dataArr = \Bitrix\Main\Config\Option::getForModule("hmarketing.upload");
        // перебираем данные полученные из базы
        foreach ($dataArr as $key => $value) {
            switch ($key) {
                case 'deletefiles':
                    $this->deleteFiles = $value;
                    break;
                case 'savebackup':
                    $this->saveBackup = $value;
                    break;
            }
        }
    }
    /**
     * cоздание папки для бэкапа 
     *
     * @return void
     */
    function DirPatchBackup()
    {
        if (!file_exists($this->patchBackup)) {
            CheckDirPath($this->patchBackup);
        }
    }
    /**
     * запись файлов в массив из базы    
     *
     * @return void
     */
    function ArFiles()
    {
        // получаем записи из таблицы b_file
        $result = FileTable::getList(array(
            'select' => array('FILE_NAME', 'SUBDIR'),
            'filter' => array('=MODULE_ID' => 'iblock'),
        ));
        // перебираем записи из таблицы b_file
        while ($row = $result->Fetch()) {
            $this->arFilesCache[$row['FILE_NAME']] = $row['SUBDIR'];
        }
    }
    /**
     * получение пути  
     *
     * @return void
     */
    function Path()
    {
        // открываем целевую папку с файлами /upload/iblock
        $rootDir = opendir($this->rootDirPath);
        // запускаем цикл и получает элемент подкатигории по его дескриптору из папки /upload/iblock
        while (false !== ($subDirName = readdir($rootDir))) {
            // проверяем на точку и прирываем итерацию, в каталоге самая первая запись всегда точка, вторая две точки, после этого идут подпапки и файлы
            if ($subDirName == '.' || $subDirName == '..') {
                continue;
            }
            // путь до подкатегории /upload/iblock/..
            $subDirPath = "$this->rootDirPath/$subDirName";
            // открываем папку подкатигории /upload/iblock/..
            $subDir = opendir($subDirPath);
            // запускаем цикл и получает элемент подкатигории по его дескриптору из папки /upload/iblock/..
            while (false !== ($subFileName = readdir($subDir))) {
                // проверяем на точку и прирываем итерацию, в каталоге самая первая запись всегда точка, вторая две точки, после этого идут подпапки и файлы
                if ($subFileName == '.' || $subFileName == '..') {
                    continue;
                }
                // путь до подкатегории или файла /upload/iblock/../..
                $subFilePath = "$this->rootDirPath/$subDirName/$subFileName";
                // проверяем на директорию/файл и вызываем соответствующий метод
                if (is_dir($subFilePath)) {
                    $this->TwoDir($subFilePath, $subDirName, $subFileName, $subDirPath);
                } else {
                    $this->OneDir($subDirPath, $subFileName, $subDirName);
                }
            }
        }
        // закрываем целевую папку с файлами /upload/iblock
        closedir($rootDir);
    }
    /**
     * обработка одной папки 
     *
     * @param  string $subDirPath
     * @param  string $fileName
     * @param  string $subDirName
     * @return void
     */
    function OneDir($subDirPath, $fileName, $subDirName)
    {
        // пометка для файла
        $filesCount = 0;
        // если файл с диска есть в списке файлов базы, значит пропуск
        if (array_key_exists($fileName, $this->arFilesCache)) {
            // увеличиваем счетчик нужных файлов
            $filesCount++;
        }
        // полный путь до файла
        $fullPath = "$subDirPath/$fileName";
        // переменная сигнализирующая о наличии поддириктории
        $backTrue = false;
        // если задано удаление найденных файлов и найденный файл не нужный
        if ($this->deleteFiles === 'Y' && $filesCount == 0) {
            // если задано делать бекап, делаем
            if ($this->saveBackup === 'Y') {
                // проверяем наличие поддиректории в папке для бекапа /upload/iblock_Backup/
                if (!file_exists($this->patchBackup . $subDirName)) {
                    // если в папке для бекапов /upload/iblock_Backup/ нет поддириктории, создаем ее 
                    if (CheckDirPath($this->patchBackup . $subDirName)) {
                        // меняем переменную сигнализирующая о наличии поддириктории
                        $backTrue = true;
                    }
                } else {
                    // меняем переменную сигнализирующая о наличии поддириктории
                    $backTrue = true;
                }
                // если поддириктория есть
                if ($backTrue) {
                    // создаем копию в бэкап
                    CopyDirFiles($fullPath, $this->patchBackup . $subDirName . '/' . $fileName);
                }
            }
            // метод записи в информационный файл
            $this->Save('Файл удален: ', $fullPath);
            // удаление файла
            unlink($fullPath);
            // удаление поддириктории
            rmdir($subDirPath);
        }
        // если задано не удаление найденных файлов и найденный файл не нужный
        if ($this->deleteFiles != 'Y' && $filesCount == 0) {
            // проверяем наличие поддиректории в папке для бекапа /upload/iblock_Backup/
            if (!file_exists($this->patchBackup . $subDirName)) {
                // если в папке для бекапов /upload/iblock_Backup/ нет поддириктории, создаем ее 
                if (CheckDirPath($this->patchBackup . $subDirName)) {
                    // меняем переменную сигнализирующая о наличии поддириктории
                    $backTrue = true;
                }
            } else {
                // меняем переменную сигнализирующая о наличии поддириктории
                $backTrue = true;
            }
            // если поддириктория есть
            if ($backTrue) {
                // создаем копию в бэкап
                CopyDirFiles($fullPath, $this->patchBackup . $subDirName . '/' . $fileName);
            }
            // метод записи в информационный файл
            $this->Save('Файл не удален и скопирован: ', $fullPath);
        }
    }
    /**
     * обработка двух папок
     *
     * @param  string $subSubDirPath
     * @param  string $subDirName
     * @param  string $subFileName
     * @param  string $subDirPath
     * @return void
     */
    function TwoDir($subSubDirPath, $subDirName, $subFileName, $subDirPath)
    {
        // пометка для файла
        $filesCount = 0;
        // открываем папку с файлом
        $hSubDir = opendir($subSubDirPath);
        // запускаем цикл и получает уже элемент по его дескриптору из подкатигории /upload/iblock/../..
        while (false !== ($fileName = readdir($hSubDir))) {
            // проверяем на точку и прирываем итерацию, в каталоге самая первая запись всегда точка, вторая две точки, после этого идут подпапки и файлы
            if ($fileName == '.' || $fileName == '..') {
                continue;
            }
            // если файл с диска есть в списке файлов базы, значит пропуск
            if (array_key_exists($fileName, $this->arFilesCache)) {
                // увеличиваем счетчик нужных файлов
                $filesCount++;
                continue;
            }
            // полный путь до файла
            $fullPath = "$subSubDirPath/$fileName";
            // переменная сигнализирующая о наличии поддириктории
            $backTrue = false;
            // если задано удаление найденных файлов
            if ($this->deleteFiles === 'Y') {
                // проверяем наличие поддиректории в папке для бекапа /upload/iblock_Backup/
                if (!file_exists($this->patchBackup . $subDirName . '/' . $subFileName . '/')) {
                    // если в папке для бекапов /upload/iblock_Backup/ нет поддириктории, создаем ее 
                    if (CheckDirPath($this->patchBackup . $subDirName . '/' . $subFileName . '/')) {
                        // меняем переменную сигнализирующая о наличии поддириктории
                        $backTrue = true;
                    }
                } else {
                    // меняем переменную сигнализирующая о наличии поддириктории
                    $backTrue = true;
                }
                // если поддириктория есть и нужно создавать бекап файла
                if ($backTrue && $this->saveBackup === 'Y') {
                    // создаем копию в бэкап
                    CopyDirFiles($fullPath, $this->patchBackup . $subDirName . '/' . $subFileName . '/' . $fileName);
                }
                // метод записи в информационный файл
                $this->Save('Файл удален: ', $fullPath);
                // удаление файла
                unlink($fullPath);
                // удаление поддиректории, если каталог пуст
                if ($this->deleteFiles === 'Y' && $filesCount == 0) {
                    rmdir($subSubDirPath);
                    rmdir($subDirPath);
                }
            }
            // если задано не удаление найденных файлов
            if ($this->deleteFiles != 'Y') {
                // проверяем наличие поддиректории в папке для бекапа /upload/iblock_Backup/
                if (!file_exists($this->patchBackup . $subDirName . '/' . $subFileName . '/')) {
                    // если в папке для бекапов /upload/iblock_Backup/ нет поддириктории, создаем ее 
                    if (CheckDirPath($this->patchBackup . $subDirName . '/' . $subFileName . '/')) {
                        // меняем переменную сигнализирующая о наличии поддириктории
                        $backTrue = true;
                    }
                } else {
                    // меняем переменную сигнализирующая о наличии поддириктории
                    $backTrue = true;
                }
                // если поддириктория есть
                if ($backTrue) {
                    // создаем копию в бэкап
                    CopyDirFiles($fullPath, $this->patchBackup . $subDirName . '/' . $subFileName . '/' . $fileName);
                }
                // метод записи в информационный файл
                $this->Save('Файл не удален и скопирован: ', $fullPath);
            }
            // удаляем переменные
            unset($fileName, $backTrue);
        }
        // закрываем папку подкатигории
        closedir($hSubDir);
    }
    /**
     * сохранения информации о затронутых файлах
     *
     * @param  mixed $name
     * @param  mixed $path
     * @return void
     */
    function Save($name, $path)
    {
        // записываем в базу
        DataTable::add(array(
            'DATE' => new DateTime(date('Y-m-d H:i:s'), "Y-m-d H:i:s"),
            'TIP' => $name,
            'PATH' => $path,
        ));
        // записываем в файл
        $fd = fopen($this->file, 'a');
        // записываем в информационный файл
        fputs($fd, $name . $path . "\n");
        // закрываем информационный файл
        fclose($fd);
    }
}

Файл /lib/dataFile.php

Файл подключается из файла /include.php.

Данный файл служит для организации доступа к таблице с помощью ORM, в нём создаётся класс, который наследуется от класса Entity\DataManager, который в свою очередь реализует парадигму ORM.

local/modules/hmarketing.upload/lib/dataFile.php<?php
// пространство имен модуля
namespace Hmarketing\Upload;
// пространство имен для ORM
use Bitrix\Main\Localization\Loc,
    Bitrix\Main\ORM\Data\DataManager,
    Bitrix\Main\ORM\Fields\DatetimeField,
    Bitrix\Main\ORM\Fields\IntegerField,
    Bitrix\Main\ORM\Fields\StringField,
    Bitrix\Main\ORM\Fields\Validators\LengthValidator,
    Bitrix\Main\Type\DateTime;
Loc::loadMessages(__FILE__);
/**
 * сущность ORM унаследованная от DataManager
 * 
 */
class FileTable extends DataManager
{
    /**
     * Returns DB table name for entity.
     *
     * @return string
     */
    public static function getTableName()
    {
        return 'b_file';
    }
    /**
     * Returns entity map definition.
     *
     * @return array
     */
    public static function getMap()
    {
        return [
            new IntegerField(
                'ID',
                [
                    'primary' => true,
                    'autocomplete' => true,
                    'title' => Loc::getMessage('FILE_ENTITY_ID_FIELD')
                ]
            ),
            new DatetimeField(
                'TIMESTAMP_X',
                [
                    'default' => function () {
                        return new DateTime();
                    },
                    'title' => Loc::getMessage('FILE_ENTITY_TIMESTAMP_X_FIELD')
                ]
            ),
            new StringField(
                'MODULE_ID',
                [
                    'validation' => [__CLASS__, 'validateModuleId'],
                    'title' => Loc::getMessage('FILE_ENTITY_MODULE_ID_FIELD')
                ]
            ),
            new IntegerField(
                'HEIGHT',
                [
                    'title' => Loc::getMessage('FILE_ENTITY_HEIGHT_FIELD')
                ]
            ),
            new IntegerField(
                'WIDTH',
                [
                    'title' => Loc::getMessage('FILE_ENTITY_WIDTH_FIELD')
                ]
            ),
            new IntegerField(
                'FILE_SIZE',
                [
                    'title' => Loc::getMessage('FILE_ENTITY_FILE_SIZE_FIELD')
                ]
            ),
            new StringField(
                'CONTENT_TYPE',
                [
                    'default' => 'IMAGE',
                    'validation' => [__CLASS__, 'validateContentType'],
                    'title' => Loc::getMessage('FILE_ENTITY_CONTENT_TYPE_FIELD')
                ]
            ),
            new StringField(
                'SUBDIR',
                [
                    'validation' => [__CLASS__, 'validateSubdir'],
                    'title' => Loc::getMessage('FILE_ENTITY_SUBDIR_FIELD')
                ]
            ),
            new StringField(
                'FILE_NAME',
                [
                    'required' => true,
                    'validation' => [__CLASS__, 'validateFileName'],
                    'title' => Loc::getMessage('FILE_ENTITY_FILE_NAME_FIELD')
                ]
            ),
            new StringField(
                'ORIGINAL_NAME',
                [
                    'validation' => [__CLASS__, 'validateOriginalName'],
                    'title' => Loc::getMessage('FILE_ENTITY_ORIGINAL_NAME_FIELD')
                ]
            ),
            new StringField(
                'DESCRIPTION',
                [
                    'validation' => [__CLASS__, 'validateDescription'],
                    'title' => Loc::getMessage('FILE_ENTITY_DESCRIPTION_FIELD')
                ]
            ),
            new StringField(
                'HANDLER_ID',
                [
                    'validation' => [__CLASS__, 'validateHandlerId'],
                    'title' => Loc::getMessage('FILE_ENTITY_HANDLER_ID_FIELD')
                ]
            ),
            new StringField(
                'EXTERNAL_ID',
                [
                    'validation' => [__CLASS__, 'validateExternalId'],
                    'title' => Loc::getMessage('FILE_ENTITY_EXTERNAL_ID_FIELD')
                ]
            ),
        ];
    }
    /**
     * Returns validators for MODULE_ID field.
     *
     * @return array
     */
    public static function validateModuleId()
    {
        return [
            new LengthValidator(null, 50),
        ];
    }
    /**
     * Returns validators for CONTENT_TYPE field.
     *
     * @return array
     */
    public static function validateContentType()
    {
        return [
            new LengthValidator(null, 255),
        ];
    }
    /**
     * Returns validators for SUBDIR field.
     *
     * @return array
     */
    public static function validateSubdir()
    {
        return [
            new LengthValidator(null, 255),
        ];
    }
    /**
     * Returns validators for FILE_NAME field.
     *
     * @return array
     */
    public static function validateFileName()
    {
        return [
            new LengthValidator(null, 255),
        ];
    }
    /**
     * Returns validators for ORIGINAL_NAME field.
     *
     * @return array
     */
    public static function validateOriginalName()
    {
        return [
            new LengthValidator(null, 255),
        ];
    }
    /**
     * Returns validators for DESCRIPTION field.
     *
     * @return array
     */
    public static function validateDescription()
    {
        return [
            new LengthValidator(null, 255),
        ];
    }
    /**
     * Returns validators for HANDLER_ID field.
     *
     * @return array
     */
    public static function validateHandlerId()
    {
        return [
            new LengthValidator(null, 50),
        ];
    }
    /**
     * Returns validators for EXTERNAL_ID field.
     *
     * @return array
     */
    public static function validateExternalId()
    {
        return [
            new LengthValidator(null, 50),
        ];
    }
}

Файл /lib/dataTable.php

Файл подключается из файла /include.php.

Данный файл служит для организации доступа к таблице с помощью ORM, в нём создаётся класс, который наследуется от класса Entity\DataManager, который в свою очередь реализует парадигму ORM.

local/modules/hmarketing.upload/lib/dataTable.php<?php
// пространство имен модуля
namespace Hmarketing\Upload;
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// пространство имен для ORM
use Bitrix\Main\Entity;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
/**
 * сущность ORM унаследованная от DataManager
 * 
 */
class DataTable extends Entity\DataManager
{
    /**
     * название таблицы в базе данных, если не указывать данную функцию, то таблица в бд сформируется автоматически из неймспейса
     *
     * @return void
     */
    public static function getTableName()
    {
        return "hmarketing_upload_delete";
    }
    /**
     * подключение к БД, если не указывать, то будет использовано значение по умолчанию подключения из файла .settings.php. Если указать, то можно выбрать подключение, которое может быть описано в .setting.php
     *
     * @return void
     */
    public static function getConnectionName()
    {
        return "default";
    }
    /**
     * метод возвращающий структуру ORM-сущности
     *
     * @return void
     */
    public static function getMap()
    {
        /*
         * Типы полей: 
         * DatetimeField - дата и время
         * DateField - дата
         * BooleanField - логическое поле да/нет
         * IntegerField - числовой формат
         * FloatField - числовой дробный формат
         * EnumField - список, можно передавать только заданные значения
         * TextField - text
         * StringField - varchar
         */
        return array(
            // ID
            new Entity\IntegerField(
                // имя сущности
                "ID",
                array(
                    // первичный ключ
                    "primary" => true,
                    // AUTO INCREMENT
                    "autocomplete" => true,
                )
            ),
            // дата и время заполнения
            new Entity\DatetimeField(
                // имя сущности
                "DATE",
                array(
                    'required' => true,
                )
            ),
            // описание картинки
            new Entity\StringField(
                // имя сущности
                "TIP",
                array(
                    // обязательное поле
                    "required" => true,
                )
            ),
            // описание картинки
            new Entity\StringField(
                // имя сущности
                "PATH",
                array(
                    // обязательное поле
                    "required" => true,
                )
            ),
        );
    }
}

файл /include.php

Файл вызывается ядром Битрикс автоматически при установке модуля.

Файл необходим для подключения классов или дополнительных файлов модуля. Если соблюдать соответствие namespace и имени модуля, то поддерживается автозагрузка класса. Автозагрузка работает медленнее, чем поключение файла в include.php.

local/modules/hmarketing.upload/include.php "lib/Main.php",
        "Hmarketing\\Upload\\DataTable" => "lib/dataTable.php",
        "Hmarketing\\Upload\\FileTable" => "lib/dataFile.php",
    )
);

файл /options.php

Файл вызывается ядром Битрикс автоматически при установке модуля.

Иногда полезно выносить какие-то настройки решения на отдельную страницу, отвлечённую от компонентов, к тому же компонентов может не быть вовсе, а всё равно надо дать возможность администратору сайта как-то влиять на работу модуля. В Битрикс довольно легко создавать страницы с параметрами для своих модулей.

В панели Битрикс сама страница с настройками находится по адресу Настройки -> Настройки модулей -> Имя_модуля:

local/modules/hmarketing.upload/options.php<?php
/**
 * административный раздел модуля
 * 
 */
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// пространство имен для получения ID модуля
use Bitrix\Main\HttpApplication;
// пространство имен для загрузки необходимых файлов, классов, модулей
use Bitrix\Main\Loader;
// пространство имен для работы с параметрами модулей хранимых в базе данных
use Bitrix\Main\Config\Option;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
// получаем id модуля
$request = HttpApplication::getInstance()->getContext()->getRequest();
$module_id = htmlspecialcharsbx($request["mid"] != "" ? $request["mid"] : $request["id"]);
// подключение модуля
Loader::includeModule($module_id);
// настройки модуля для админки в том числе значения по умолчанию
$aTabs = array(
    array(
        // значение будет вставленно во все элементы вкладки для идентификации
        "DIV" => "edit",
        // название вкладки в табах 
        "TAB" => "Удаление ненужных файлов из папки /upload/iblock",
        // массив с опциями секции
        "OPTIONS" => array(
            "Удалять найденые файлы?",
            array(
                // имя элемента формы
                "deletefiles",
                // поясняющий текст
                "Если флажок не активен, бекап автоматически создастся но файлы из /upload/iblock/ не будут удалены. Если флажок активен, можно выбрать ниже пораметр 'Сохранять в бекап найденые файлы'.",
                // значение checkbox по умолчанию "Нет"
                "N",
                // тип элемента формы "checkbox"
                array("checkbox"),
            ),
            "Сохранять в бекап найденые файлы?",
            array(
                // имя элемента формы
                "savebackup",
                // поясняющий текст
                "Если флажок активен, бекап создастся, файлы будут сохранены в /upload/iblock_Backup",
                // значение checkbox по умолчанию "Нет"
                "N",
                // тип элемента формы "checkbox"
                array("checkbox"),
            ),
        )
    )
);
// проверяем текущий POST запрос и сохраняем выбранные пользователем настройки
if ($request->isPost() && check_bitrix_sessid()) {
    // проверяем POST запрос, если инициатором выступила кнопка с name="Update" сохраняем введенные настройки в базу данных
    if ($request["Update"]) {
        // цикл по заполненым пользователем вкладкам
        foreach ($aTabs as $aTab) {
            foreach ($aTab["OPTIONS"] as $arOption) {
                // если это название секции, переходим к следующий итерации цикла
                if (!is_array($arOption)) {
                    continue;
                }
                // получаем в переменную $optionValue введенные пользователем данные
                $optionValue = $request->getPost($arOption[0]);
                // устанавливаем выбранные значения параметров и сохраняем в базу данных, перед сохранением проверяем если массив то соединяем данные, если не массив сохраняем как есть
                Option::set($module_id, $arOption[0], $optionValue);
            }
        }
    }
    // проверяем POST запрос, если инициатором выступила кнопка с name="dalete", выполняем действия с файлами 
    if ($request["dalete"]) {
        new Hmarketing\Upload\Main();
    }
}
// отрисовываем форму, для этого создаем новый экземпляр класса CAdminTabControl, куда и передаём массив с настройками
$tabControl = new CAdminTabControl(
    "tabControl",
    $aTabs
);
// отображаем заголовки закладок
$tabControl->Begin();
?>
<form action="<?= ($APPLICATION->GetCurPage()) . '?mid=' . ($module_id) . '&lang=' . (LANG) ?>" method="post">
    <? foreach ($aTabs as $aTab) {
        if ($aTab["OPTIONS"]) {
            // завершает предыдущую закладку, если она есть, начинает следующую
            $tabControl->BeginNextTab();
            // отрисовываем форму из массива
            __AdmSettingsDrawList($module_id, $aTab["OPTIONS"]);
        }
    }
    // выводит стандартные кнопки отправки формы
    $tabControl->Buttons();
    // выводим скрытый input с идентификатором сессии
    echo (bitrix_sessid_post()); ?>
    <input class="adm-btn-save" type="submit" name="Update" value="Сохранить настройки" />
    <input type="submit" name="dalete" value="Очистить папку" />
</form>
<?
// обозначаем конец отрисовки формы
$tabControl->End();
Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!
Эйч Маркетинг