{"id":13435440,"url":"https://github.com/AndyDune/rzn.library","last_synced_at":"2025-03-18T11:31:08.439Z","repository":{"id":13242316,"uuid":"15927101","full_name":"AndyDune/rzn.library","owner":"AndyDune","description":"Модуль с полезными классами для CMS 1С-Битрникс","archived":false,"fork":false,"pushed_at":"2024-03-04T07:41:02.000Z","size":451,"stargazers_count":9,"open_issues_count":0,"forks_count":2,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-07T06:23:22.731Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"rznw.ru","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AndyDune.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-01-15T06:37:17.000Z","updated_at":"2023-08-21T11:18:13.000Z","dependencies_parsed_at":"2024-10-27T18:13:37.337Z","dependency_job_id":"0318fa3b-3634-495a-860a-04eae1c5d2ff","html_url":"https://github.com/AndyDune/rzn.library","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyDune%2Frzn.library","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyDune%2Frzn.library/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyDune%2Frzn.library/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyDune%2Frzn.library/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AndyDune","download_url":"https://codeload.github.com/AndyDune/rzn.library/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244210943,"owners_count":20416555,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T03:00:35.710Z","updated_at":"2025-03-18T11:31:08.434Z","avatar_url":"https://github.com/AndyDune.png","language":"PHP","funding_links":[],"categories":["\u003ca id=\"repos-organization\"\u003eGithub, Bitbucket, etc\u003c/a\u003e"],"sub_categories":["Репозитории"],"readme":"rzn.library\n===========\nМодуль с полезными классами для CMS 1С-Битрикс\n\nОснову программирования на основе библиотеки закладывает конфигурация. Файл конфигурации может содержать любую информацию: описание ключевых параметров, сервисов, помошников компонентов, каналов медиатора и описания водопадов.\n\n\n## Дерево конфигурации\n\nВ кажом модуле, который указан в конфигурации приложения может быть файл */local/modules/\u003cмодуль\u003e/config/module.config.php* с базовым содержимым:\n\nСекции под ключами invoke и factory для сервисов, помогаторов, слушателей событий идентичны\n\n```php\nreturn array(\n    'service_manager' =\u003e [\n        'invokables' =\u003e [\n            '\u003cимя сервиса\u003e' =\u003e [\n                'name'=\u003e '\u003cполное имя класса\u003e',\n                'injector' =\u003e \u003cмассив с описанием инъекций\u003e\n                'shared'=\u003e \u003ctrue|false - по умолчанию true\u003e, // вкл/откл сохранения объекта для повторного возврата\n            ],\n        ],\n        'factories' =\u003e [\n           '\u003cимя сервиса\u003e' =\u003e [\n                'name'=\u003e '\u003cполное имя класса фабрики\u003e',\n                'injector' =\u003e \u003cмассив с описанием инъекций\u003e\n                'shared'=\u003e \u003ctrue|false - по умолчанию true\u003e, // вкл/откл сохранения объекта для повторного возврата\n            ],\n\n        ]\n    ],\n    'view_helpers' =\u003e [\n    /*\n    Возвращаемый объект-помогатор должен иметь метод __invoke для прямого запуска\n    */\n        'invokables' =\u003e [\n            '\u003cимя помогатора\u003e' =\u003e [\n                'name'=\u003e '\u003cполное имя класса\u003e',\n                'injector' =\u003e \u003cмассив с описанием инъекций\u003e\n                'shared'=\u003e \u003ctrue|false - по умолчанию true\u003e, // вкл/откл сохранения объекта для повторного возврата\n            ],\n        ],\n        'factories' =\u003e [\n           '\u003cимя помогатора\u003e' =\u003e [\n                'name'=\u003e '\u003cполное имя класса фабрики\u003e',\n                'injector' =\u003e \u003cмассив с описанием инъекций\u003e\n                'shared'=\u003e \u003ctrue|false - по умолчанию true\u003e, // вкл/откл сохранения объекта для повторного возврата\n            ],\n\n        ]\n\n    ],\n   'configurable_event_manager' =\u003e [\n        'listeners' =\u003e [\n            '\u003cимя события\u003e' =\u003e [\n                'invokables' =\u003e [\n                    '\u003cимя слушателя - произвольная строка\u003e' =\u003e [\n                        'name' =\u003e '\u003cкласс слушателя с методом __invoke\u003e',\n                        // включение инъекции по интерфейсам\n                        'injector' =\u003e [\n                            'inject' =\u003e [\n                                'handler' =\u003e 'initializer',\n                                'options' =\u003e []\n                            ]\n                        ]\n                    ]\n                ]\n            ]\n        ]\n    ],\n    'mediator' =\u003e [\u003cмассив описание медиаторов\u003e],\n    'waterfall' =\u003e [\u003cмассив описания водопадов\u003e],\n    'bitrix_events' =\u003e [\n        'main' =\u003e [ // модуль события битрикса\n            'OnPageStart' =\u003e [ // имя события битрикса\n                '\u003cимя слушателя - произвольная строка\u003e' =\u003e [\n                        'name' =\u003e '\u003cкласс слушателя с методом __invoke\u003e',\n                        'injector' =\u003e []\n                ]            \n            ]\n        ]\n    ]\n    \n\n\n```\n\nРекомендую к просмотру базовую папку для кастомного кода в битриксе [local](https://github.com/AndyDune/bitrix_local). Здесь папка congif содержит в себе основной конфиг, который определяет настройки для всего приложения. Этот файл загружается самым первым.\n#### Содержимое файла application.config.php\n```php\nreturn array(\n    'modules' =\u003e [\n       // Список подключаемых модулей при загрузке приложения\n    ],\n    /*\n     * Инструкции для запуска крона\n     */\n    'cron' =\u003e [\n        'tasks' =\u003e [\n            'код события крона' =\u003e [\n                'event' =\u003e 'имя события для запуска',\n                'minute' =\u003e 5,\n                'hour' =\u003e 2\n            ],\n        ],\n        // Использовать в локальногй конфигурации для разрешения прямого запуска событий\n        // todo на продакшене здесь закоментировать\n        'direct' =\u003e true\n    ]\n);\n// любые свои настроки\n```\n\n\nОписание для подключения в качестве субмодуля смотреть здесь: http://git-scm.com/book/ru/v1/Инструменты-Git-Подмодули\n\n## Присоединение тестовой части конфига\nЕсть возможность не изменяя файлы конфига, которые размещены в основной части (local/config/application.config.php) в модулях и шаблонах.\nБолее того, тестовые участки не попадают в репозиторий, остаются только на машине текущего программиста.\n\nДелаем так:\nСоздаем файл : `local/config/local.config.php` - располагается рядом с конфигом приложения.\nЭтот файл не добавляется в репозиторий. В процессе слияния всех доступных файлом конфигурации этот локальный файл загружается последним и перегружает любые участки конфигурационного массива, которые были загружены ранее.\n\nПример:\n\nlocal/config/application.config.php содержит\n\n    'api' =\u003e [\n         'x' =\u003e [\n             'url' =\u003e 'http://api.rzrw.ru/',\n             'login' =\u003e 'user',\n             'password' =\u003e 'qwerty'\n         ]\n    ]\n\nlocal/config/local.config.php содержит\n\n     'api' =\u003e [\n         'x' =\u003e [\n             'url' =\u003e 'http://api.test.rzrw.ru/',\n         ]\n    ]\n\nНа выходе получаем:\n\n     'api' =\u003e [\n         'x' =\u003e [\n             'url' =\u003e 'http://api.test.rzrw.ru/',\n             'login' =\u003e 'user',\n             'password' =\u003e 'qwerty'\n         ]\n    ]\n\nТочно так же можно перегрузить сервисы, хелперы, медиаторы, водопады (для перегрузки дропов нужно дать им символьные ключи).\n\n## Менеджер сервисов\n\n### Описание сервиса в конфин файле модуля\n\nКорневой ключ конфиг. массива `service_manager` Менеджер сервисов позволяет управлять инъекциями, создавать объекты с помощью фабрик, сохранять состония объектов между вызовами в разнах местах приложения.\n\nНиже собственный класс, который я планирную зарегистрировать в системе как вызываемый сервис. Класс реализует интерфейс `ServiceLocatorAwareInterface` который позволяет при включении инъекций через интерфейс внедрить в объект менеджер сервисов.\n```php\nuse Rzn\\Library\\ServiceManager\\ServiceLocatorAwareInterface;\nuse Rzn\\Library\\ServiceManager\\ServiceLocatorInterface;\n\nclass MyService implements ServiceLocatorAwareInterface\n{\n    protected $sm;\n    \n    protected $count = 0;\n    \n    /**\n    * Собственный рабочий метод сервиса\n    */\n    public function countUse()\n    {\n        $this-\u003ecount++;\n    }\n    \n    public function getCount()\n    {\n        return $this-\u003ecount;\n    }\n    \n    /**\n    * Извлечение сервиса сессии для использовании внутри объекта класса.\n    * @return \\Rzn\\Library\\Session\n    */\n    protected function getSession()\n    {\n        $this-\u003egetServiceLocator()-\u003eget('session');\n    }\n    \n    /**\n     * Внедрение сервис локатора\n     *\n     * @param ServiceLocatorInterface $serviceLocator\n     */\n    public function setServiceLocator(ServiceLocatorInterface $serviceLocator)\n    {\n        $this-\u003esm = $serviceLocator;\n    }\n\n    /**\n     * Возврат сервис локатора.\n     *\n     * @return ServiceLocatorInterface\n     */\n    public function getServiceLocator()\n    {\n        return $this-\u003esm;\n    }\n}\n```\n\nОписываем сервис в конфиге модуля.\n\n```php\nreturn array(\n    'service_manager' =\u003e [\n        'invokables' =\u003e [\n            'MySuperService' =\u003e [ // Имя может быть любой строкой\n                'name'=\u003e 'MyService',\n                'injector' =\u003e [\n                            'inject' =\u003e [\n                                'handler' =\u003e 'initializer',\n                         ],\n                'shared'=\u003e true // сохранение создаваемого объекта для последующего возврата при вызове\n            ],\n        ]\n    ]\n);\n```\n\nДля создания и вызова созданного объекта сервиса нужно получить объект менеджера сервисов, для которого в регистри есть специальной метод.\n```php \nuse Rzn\\Librabry\\Registry;\n/** @var MyService $myobject */\n$myobject = Registry:: getServiceMenager()-\u003eget('MySuperService');\n$myobject-\u003ecountUse();\necho $myobject-\u003egetCount(); // 1\n\n\n// Однажды созданный объект сохраняется внутри менеджера и возвращается при следующем запросе.\n\n/** @var MyService $myobject */\n$myobject = Registry:: getServiceMenager()-\u003eget('MySuperService');\n$myobject-\u003ecountUse();\necho $myobject-\u003egetCount(); // 2\n\n```\n\n\n## События\n\n### Перегрузка событий битрикса\n\n#### Классическое присоединение слушателей к событиям\n\nСобытия битрикса по рекомендации описываются в init.php. Как слушатели выступают функции или статичные методы:\n\n```php\n// Статичный метод\nAddEventHandler(\"iblock\", \"OnBeforeIBlockElementAdd\", Array(\"\u003cимя класса\u003e\", \"имя метода\"));\n\n// Функция\nAddEventHandler(\"iblock\", \"OnBeforeIBlockElementAdd\", \"имя функции\");\n```\nБолее продвинутый метод  - это анонимные функции:\n\n```php\nuse Bitrix\\Main\\EventManager;\n$eventManager = EventManager::getInstance();\n\n$eventManager-\u003eaddEventHandlerCompatible(\"iblock\", \"OnBeforeIBlockElementAdd\", function(\u0026$arFields) {\n    // код функции\n});\n\n```\nПрименение анонимных функций более удобный вариант - объявление кода на месте, нет конфликтов имен. Это в случае размещения кода в init.php\n\nНо все это плохо при большой кодовой базе.\n\n#### Слушатели событий битрикса на основе rzn.library\n\nБуду использовать некий модуль rzn.test - он должен быть уставновлен в битриксе и прописан в секции *modules* в конфиге приложения (см. ниже).\n\nГлавный принцип - описательность. Используются только объекты классов, которые хранятся в кастомных модулях. Наименования классов по парвилам d7.\n\nИнтерфейс классов событий заимствован из *ZendFW 2*\n\n\nПример класса, который принимает параметры и модифицирует их.\n\nРассмотрю событие Sale OnBeforeBasketUpdate - оно запускается в скрипте: *\\bitrix\\modules\\sale\\general\\basket.php* (строка 1450) \n\n```php\nforeach(GetModuleEvents(\"sale\", \"OnBeforeBasketUpdate\", true) as $arEvent)\n    if (ExecuteModuleEventEx($arEvent, array($ID, \u0026$arFields))===false)\n        return false;\n```\n2 параметра:\n\n- $ID - стандартная передача\n- $arFields - по ссылке, может быть мождифицировано\n\nВ кастомном механизмусе параметры упаковываются в объект \\ArrayAccess для возможности изменения параметров внутри слушателей.\n\n```php\nnamespace Rzn\\Test\\EventListener\\Sale\\OnBeforeBasketUpdate;\n\nclass ApplyNewPriceForUser\n{\n\n    /**\n     * @param $e \\Rzn\\Library\\EventManager\\Event\n     */\n    public function __invoke($e)\n    {\n       /*\n        Извлечение параметров, которые передал битрикс\n        Это объект, поэмому значения, которые будут в нем изменены передадутся наружу\n       */\n       /** @var \\ArrayAccess  $params */\n        $params = $e-\u003egetParams();\n        \n        $ID = $params[0];\n        // имеют числовий ключи\n        $arFields = $params[1];\n\n        if (!isset($arFields['PRODUCT_ID']) or !$arFields['PRODUCT_ID']) {\n            $id = $params[0];\n            $data = \\CSaleBasket::GetByID($id);\n            if (!$data) {\n                return;\n            }\n            $arFields['PRODUCT_ID'] = $data['PRODUCT_ID'];\n        }\n        \n        // Моежм изменить цену для корзины\n        $price = $this-\u003egetNewPrice();\n        if ($price) {\n            $arFields['PRICE'] = $price;\n        }\n        // Параметр будет изменен\n        $params[1] = $arFields;\n    }\n\n}\n```\n\nКласс создан, расположен в модуле по урлу: */local/modules/rzn.test/lib/sale/onbeforebasketupdate/applynewpriceforuser.php* может автоматически загрузиться битриксом.\n\nДля присоединения в качестве слушателя нужно прописать в конфиге модуля */local/modules/rzn.test/config/module.config.php*\n\n```php\n'bitrix_events' =\u003e [\n        'sale' =\u003e [\n           'OnBeforeBasketUpdate' =\u003e [\n                'invokables' =\u003e [\n                    'ApplyNewPriceForUser' =\u003e [\n                        'name' =\u003e 'Rzn\\Test\\EventListener\\Sale\\OnBeforeBasketUpdate\\ApplyNewPriceForUser',\n                    ]\n                ]\n            ]\n        ]\n ]\n```\nили \n\n```php\n'bitrix_events' =\u003e [\n        'sale' =\u003e [\n           'OnBeforeBasketUpdate' =\u003e [\n                'invokables' =\u003e [\n                    'ApplyNewPriceForUser' =\u003e [\n                        'Rzn\\Test\\EventListener\\Sale\\OnBeforeBasketUpdate\\ApplyNewPriceForUser',\n                    ]\n                ]\n            ]\n        ]\n ]\n```\n\n## Инъекция \n\nИнъекции в основном описываются в конфиге вместе c сущностями, к которым они применяются. Сущности: все сервисы (service_manager, helper_manager, custom_service_managers, event_manager), канала медиатора и водопады.\n\n##### Примеры инъекций\n```php\n    'custom_service_managers' =\u003e [\n        'models' =\u003e [\n            'invokables' =\u003e [\n                'import_task' =\u003e [\n                    'name' =\u003e 'ImportTask',\n                    'shared' =\u003e false,\n                    'injector' =\u003e [\n                        // Инъекция сервиса (service_manager)\n                        'injectEventManager' =\u003e [\n                            'handler' =\u003e 'setter', // обработчик\n                            'options' =\u003e [\n                                'set' =\u003e 'service', // сеттер сервисов\n                                'service' =\u003e 'event_manager',\n                                'method' =\u003e 'setEventManager'\n                            ]\n                        ],\n                        // Инъекция собственного сервиса (custom_service_managers)\n                        'injectMyService' =\u003e [\n                            'handler' =\u003e 'setter', // обработчик\n                            'options' =\u003e [\n                                'set' =\u003e 'custom_service', // сеттер собственных сервисов\n                                'manager' =\u003e 'my_service_manager',\n                                'service' =\u003e 'my_serive',\n                                'method' =\u003e 'setMyService'\n                            ]\n                        ],\n                        // Инъекция конфига, любой указанной части\n                        'injectApiConfig' =\u003e [\n                            'handler' =\u003e 'setter',\n                            'options' =\u003e [\n                                'set' =\u003e 'config',\n                                'config' =\u003e 'api.x', // последовательность ключей для конфига\n                                'method' =\u003e 'setApiConfig'\n                            ]\n                        ],\n                        // Инъекция одного параметра\n                        'injectSetAvailability' =\u003e [\n                            'handler' =\u003e 'setter',\n                            'options' =\u003e [\n                                'set' =\u003e 'params',\n                                'param' =\u003e false, // Вставляется как -\u003esetSaveAvailability(false)\n                                'method' =\u003e 'setSaveAvailability'\n                            ]\n                        ],\n                        // Инъекция многих параметров\n                        'injectOneTwo' =\u003e [\n                            'handler' =\u003e 'setter',\n                            'options' =\u003e [\n                                'set' =\u003e 'params',\n                                'paras' =\u003e [1, 2], // Вставляется как -\u003esetOneTwo(1, 2)\n                                'method' =\u003e 'setOneTwo'\n                            ]\n                        ],\n                        // Инъекция нового объекта указанного класса\n                        'injectNewInstance' =\u003e [\n                            'handler' =\u003e 'setter',\n                            'options' =\u003e [\n                                'set' =\u003e 'invokable',\n                                'class' =\u003e 'Rzn\\Library\\GoodClass', \n                                'method' =\u003e 'setGood',\n                                'injector' =\u003e []\n                            ]\n                        ],\n                        \n                    ]\n                ],\n            ]\n        ]\n    ]\n\n```\nЕсть возможность делать инъекции через интерфейс:\n```php\n'waterfall' =\u003e [\n  'streams' =\u003e [\n        'exportOrder' =\u003e [\n            'drops' =\u003e [\n                'basket_add' =\u003e [\n                    'invokable' =\u003e 'Rzn\\Library\\BasketAdd',\n                    'injector' =\u003e [\n                        'injectWithInterface' =\u003e [\n                            'handler' =\u003e 'initializer',\n                        ],\n                    ]\n                ],\n            ]\n        ],\n        ...\n    ]\n]\n```\n\n#### Водопад\n\n##### Тестирование описания водопада\nФункции для водопада могут представлять сервисы или объекты указанных класов. В описании обязательно будет инъекции. \n\nДля быстрой начальной проверки валидности водопада есть специальный сервис waterfall_check. Его метод checkStream возвращает массив с результатом.\n```php\n/** @var Rzn\\Library\\Waterfall\\Check $waterfallCheck */\n$waterfallCheck = $sm-\u003eget('waterfall_check');\npr($waterfallCheck-\u003echeckStream('loadCatalogImportFileFrom1c'));\n\n```\n\n#### Специальный обработчик множественного свойтсва картинки\nЗадача: сохранять картинки с формы с контролем максимального их числа, с указанием описания и сортировки:\n\n##### Скрипт сохранения\n```php\n\n$filesArrayNormalize = function($files) {\n    $result = array();\n\n    foreach($files as $nameOption =\u003e $array1) {\n        foreach($array1 as $id =\u003e $array2) {\n            foreach($array2 as $nameField =\u003e $value) {\n                if (!isset($result[$id])) {\n                    $result[$id] = array();\n                }\n                $result[$id][$nameField][$nameOption] = $value;\n            }\n        }\n    }\n    return $result;\n};\n\n$filesMorePicture = $filesArrayNormalize($_FILES['image']); // \u003cinput type=\"file\" name=\"image[more_picture][3025]\"\u003e\n\n$saveMorepicture = new Rzn\\Library\\BitrixTrial\\Iblock\\MultiFileProperty($config-\u003egetNested('infoblocks.ids.shops'));\n$saveMorepicture-\u003esetPropertyCode('more_picture')\n    -\u003esetElementId($ID)\n    -\u003esetMaxImages($maxImagesCount)\n    -\u003esetDescriptionArray($_POST['description']['more_picture']) // \u003cinput type=\"text\" name=\"description[more_picture][3025]\"  value=\"Картинка\"\u003e\n    -\u003esetSortArray($_POST['order']['more_picture'])\u003cinput type=\"text\" name=\"order[more_picture][3025]\" value=\"100\"\u003e\n    -\u003esetDeleteArray($_POST['delete_image']['more_picture']) // \u003cinput type=\"hidden\" value=\"0\" name=\"delete_image[more_picture][3025]\"\u003e\n    -\u003esetFilesArray($filesMorePicture, 'VALUE') // Внедрение нормализованого массива с данными из формы\n    -\u003esave()\n;\n```\n##### Выборка отсортированных даных\n```php\n$saveMorepicture = new Rzn\\Library\\BitrixTrial\\Iblock\\MultiFileProperty($config-\u003egetNested('infoblocks.ids.shops'));\n$saveMorepicture-\u003esetPropertyCode('more_picture');\n$saveMorepicture-\u003esetElementId($ID);\n$morePictures = $saveMorepicture-\u003eextractSorted(); // Массив готовый для участия в выводе картинок\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAndyDune%2Frzn.library","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAndyDune%2Frzn.library","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAndyDune%2Frzn.library/lists"}