Download the PHP package arrilot/bitrix-models without Composer
On this page you can find all versions of the php package arrilot/bitrix-models. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package bitrix-models
Данный пакет больше активно не поддерживается
Причина - мы больше не используем Битрикс в своих проектах. Если вам интересен этот проект и вы хотите заняться его поддержкой - форкните его и создайте Issue в данном репозитории чтобы мы поместили здесь ссылку на форк.
Форки:
Bitrix models
Вступление
Данный пакет привносит Model Layer в Битрикс. Этот слой логически состоит из двух частей:
- Модели для сущностей битрикса (в дальнейшем будем называть их "Битрикс-модели") работающие внутри через API Битрикса (
CIBlockElementи т д) и представляющие собой надстройку над ним. С внешней же стороны эта надстройка напоминаетEloquent. - Модели для произвольных таблицы работающие через
illuminate/databaseв целом иEloquentв частности.
Установка
- Регистрируем пакет в
init.php-Arrilot\BitrixModels\ServiceProvider::register();
Использование моделей Bitrix
Для наследования доступны следующие модели:
Для пример далее везде будем рассматривать модель для элемента инфоблока (ElementModel). Для других сущностей API практически идентичен.
ElementModel полноценно поддерживает только инфоблоки второй версии (та что хранит свойства в отдельных таблицах). С первой версией некоторые моменты могут не работать должным образом из-за особенностей работы CIBlockElement::GetList(). Самая большая проблема: Если в инфоблоке есть множественные свойства, то запросы с limit(), take(), и first() будут отрабатывать неверно и получать меньше элементов чем нужно и не с полным набором множественных свойств. Если вы всё же собираетесь использовать ElementModel с инфоблоками первой версии, то обязательно выставите const IBLOCK_VERSION = 1; внутри класса модели.
Создадим, модель для инфоблока Товары.
Для работы модели необходимо лишь задать ID инфоблока в константе. Для юзеров не требуется и этого.
Если вы не хотите привязываться к ID инфоблоков, можно переопределить в моделе метод
public static iblockId()и получать в нём ID инфоблока например по коду. Это дает большую гибкость, но скорее всего вам потребуются дополнительные запросы в БД.
В дальнейшем мы будем использовать наш класс Product как в статическом, так и в динамическом контексте.
Добавление продукта
Заметка: В случае если поля $product в дальнейшем как-то используются, рекомендуется сразу же обновить объект новым запросом в БД . Нужно это из-за того, что форматы полей в CIblockElement::Add() и CIblockElement::GetList() не совпадают на 100% Делается это так:
Обновление
Инстанцирование модели без запросов к базе.
Для ряда операций нет необходимости в получении информации из БД, достаточно лишь ID объекта. В этом случае достаточно инстанцировать объект модели, передав в конструктор идентификатор.
Получение полей объекта из базы
Описанные методы сохраняют данные из БД внутри экземпляра класса-модели. Объекты-модели реализуют ArrayAccess, поэтому с ними можно во многом работать как с массивами.
Преобразование моделей в массив/json.
По умолчанию, все поля модели становятся доступными в массиве, что не всегда желательно.
У моделей есть специальные свойства protected $visible = []; и protected $hidden = [];,
при помощи которых можно составить белый/черный список полей для преобразования модели в массив/json.
Получение информации из базы
Наиболее распостраненный сценарий работы с моделями - получение элементов/списков из БД. Для построение запроса используется "Fluent API", который использует в недрах себя стандартный Битриксовый API.
Для начала построения запроса используется статический метод ::query().
Данный метод возвращает объект-построитель запроса (ElementQuery, SectionQuery или UserQuery), через который уже строится цепочка запроса.
Простейший пример:
На самом деле данная форма приведена больше для понимания, есть более удобный вид, который использует __callStatic для передачи управления в объект-запрос.
Любая цепочка запросов должна заканчиваться одним из следующих методов:
->getList()- получение коллекции (см. http://laravel.com/docs/master/collections) объектов. По умолчанию ключом каждого элемента является его ID.->getById($id)- получение объекта по его ID.->first()- получение одного (самого первого) объекта удовлетворяющего параметрам запроса.->count()- получение количества объектов.->paginate() или ->simplePaginate()- получение спагинированного списка с мета-данными (см. http://laravel.com/docs/master/pagination)- Методы для отдельных сущностей:
->getByLogin($login)и->getByEmail($email)- получение первого попавшегося юзера с данным логином/email.->getByCode($code)и->getByExternalId($id)- получение первого попавшегося элемента или раздела ИБ по CODE/EXTERNAL_ID
Управление выборкой
-
->sort($array)- аналог$arSort(первого параметраCIBlockElement::GetList)Примеры:
->sort(['NAME' => 'ASC', 'ID => 'DESC'])->sort('NAME', 'DESC') // = ->sort(['NAME' => 'DESC'])->sort('NAME') // = ->sort(['NAME' => 'ASC']) ->filter($array)- аналог$arFilter->navigation($array)-
->select(...)- аналог$arSelectПримеры:
->select(['ID', 'NAME'])->select('ID', 'NAME')select()поддерживает два дополнительных значения -'FIELDS'(выбрать все поля),'PROPS'(выбрать все свойства). Для пользователей также можно указать'GROUPS'(добавить группы пользователя в выборку). Значение по-умолчанию дляElementModel-['FIELDS', 'PROPS'] ->limit($int),->take($int),->page($int),->forPage($page, $perPage)- для навигации
Fetch и GetNext
По-умолчанию, внутри моделей для итерации по полученным из базы элементам/разделам/юзерам используется производительный метод ->Fetch()
В отличии от ->GetNext(), он не приводит данные в html безопасный вид и не преобразует DETAIL_PAGE_URL, SECTION_PAGE_URL в реальные урлы элементов и категорий.
Если в результате выборки вам нужны эти преобразования, то можно переключиться на этот метод.
-
Можно переключить сразу всю модель задав ей свойство
- Можно переключить для одного единственного запроса.
Некоторые дополнительные моменты:
- Для ограничения выборки добавлены алиасы
limit($value)(соответсвуетnPageSize) иpage($num)(соответсвуетiNumPage) - В некоторых местах API более дружелюбный чем в самом Битриксе. Допустим в фильтре по пользователям не обязательно использовать
'GROUP_IDS'. При передаче'GROUP_ID'(именно такой ключ требует Битрикс допустим при создании пользователя) или'GROUPS'результат будет аналогичен. - При создании или изменении элементов и разделов инфоблоков Битрикс перестраивает поисковый индекс и позволяет пропустить это перестаривание для конкретного вызова
Add/Updateдля увеличения производиельности. В моделях вы можете добиться того же эффекта либо сразу одним махом установив непосредственно в классе-моделиprotected static $updateSearch = false;, либо уже непосредственно перед добавлением/обновлением вызвав отдельный статический методProduct::setUpdateSearch(false). - Флагами $bWorkFlow и $bResizePictures для CIBlockElement::Add/Update можно управлять аналогичным образом.
Query Scopes
Построитель запросов можно расширять добавляя "query scopes" в модель.
Для этого необходимо создать публичный метод начинаюзищися со scope.
Пример "query scope"-a уже присутсвующего в пакете.
В "query scopes" можно также передавать дополнительные параметры.
Данные скоупы уже присутсвуют в пакете, ими можно пользоваться.
Остановка действия
Иногда требуется остановить выборку из базы из query scope. Для этого достаточно вернуть false. Пример:
В результате запрос в базу не будет сделан - getList() вернёт пустую коллекцию, getById() - false, а count() - 0.
Того же самого эффекта можно добиться вызвав вручную метод
->stopQuery()
Кэширование запросов
Для всех вышеупомнянутых Битрикс-моделей есть простой встроенный механизм кэширования.
Достаточно лишь добавитьв цепочку вызовов ->cache($minutes)-> и результат выборки из базы будет закэширован на указанное количество минут.
Пример: $products = Products::query()->cache(30)->filter(['ACTIVE' => 'Y'])->getList()
Под капотом кэширование происходит используя стандартный механизм из d7 Битрикса. Клоюч кэширования зависит от модели и всех параметров запроса.
Accessors
Временами возникает потребность как то модифицировать данные между выборкой их из базы и получением их из модели. Для этого используются акссессоры. Также как и для "query scopes", для добавления аксессора необходимо добавить метод в соответсвующую модель.
Правило именования метода - $methodName = "get".camelCase($field)."Attribute".
Пример:
Этим надо пользоваться с осторожностью, потому оригинальное значение становится недоступным.
Аксессоры можно создавать также и для несуществущих (виртуальных) полей, например:
Для того чтобы такие виртуальные аксессоры отображались в toArray() и toJson(), их необходимо явно указать в поле $appends модели.
Языковые аксессоры
Для многоязычных сайтов типичным является подход, когда для каждого языка создается своё свойство, например, UF_TITLE_RU, UF_TITLE_BY В этом случае для каждого такого поля можно создать аксессор:
Так как эти аксессоры однотипны и имеют неприятную особенность засорять модели, то для них можно использовать специальный краткий синтаксис
События моделей (Model Events)
События позволяют вклиниваться в различные точки жизненного цикла модели и выполнять в них произвольный код. Например, автоматически проставлять символьный код при создании элемента. События моделей не используют событийную модель Битрикса (ни старого ядра, ни D7) и касаются лишь того, что происходит внутри моделей. Использование Битриксовых событий покрывает больше кейсов.
Обработчик события задается переопределением соответсвующего метода в классе-модели.
Сигнатуры обработчиков других событий совпадают с приведенными выше.
Список доступных эвентов:
onBeforeCreate- перед добавлением записиonAfterCreate(bool $result)- после добавления записиonBeforeUpdate- перед обновлением записиonAfterUpdate(bool $result)- после обновления записиonBeforeSave- перед добавлением или обновлением записиonAfterSave(bool $result)- после добавления или обновления записиonBeforeDelete- перед удалением записиonAfterDelete(bool $result)- после удаления записи
Если сделать return false; из обработчика onBefore...() то последующее действие будет отменено.
В обработчиках можно получить дополнительную информацию используя свойства объекта текущей модели.
Например, в onBefore...() обработчиках доступны все поля через $this->fields
Во всех onAfter...() доступен массив ошибок через $this->eventErrors;
В onBeforeUpdate() и onBeforeSave() доступен массив $this->fieldsSelectedForSave, в котором содержатся ключи полей которые мы собираемся обновлять.
D7 Model
Немного особняком стоит D7Model
В отличии от предыдущих моделей она вместо старых GetList-ов и т д использует в качестве бэкэнда D7
Через неё можно работать как с обычными сущностями D7, так и с хайлоадблоками
Пример для хайлоадблока:
Понятно, что логика получения класса хайлоадблока может быть любой, но важно не забыть скомпилировать его, иначе он не будет работать. Пожалуй самый удобный вариант - использовать вспомогательный пакет https://github.com/arrilot/bitrix-iblock-helper/ С ним мы получаем следующее:
Если мы работаем не с хайлоадблоком, а с полноценной сущностью D7 ORM, то просто возвращаем в этом методе полное название класса этой сущности. Цепочки вызовов и названия методов для D7Model такие же как и для предыдущих моделей. Всё что мы передаем в эти методы будет передано далее в D7.
Пример получения всех подписчиков с именем John и с кэшированием на 5 минут:
Полный список методов следующий
За подробностями смотрите vendor/arrilot/bitrix-models/src/Models/D7Model.php и vendor/arrilot/bitrix-models/src/Queries/D7Query.php
Связи между моделями (Relations)
Помимо работы с отдельными Битрикс-моделями, также имеется возможность и строить связи между моделями, что делает их легко-доступными для получения через основные объекты данных. Например, товар связан с вопросами о товарах. С помощью объявления этой связи вы можете получить объекты модели вопроса с помощью выражения $product->questions, которое возвращает информацию о вопросах в виде колелкции объектов класса Question (дочерний класс BaseBitrixModel).
Объявление связей
Имена связей чувствительны к регистру.
При объявлении связи, вы должны указать следующую информацию:
- кратность связи: указывается с помощью вызова метода hasMany() или метода hasOne().
- название связного класса: указывается в качестве первого параметра для метода hasMany() / hasOne().
- связь между двумя типами данных: второй аргумент - поле во внешней модели, третий - поле во внутренней (по-умолчанию ID).
- в отличии от других ORM множественные связи не используют промежуточные (pivot) таблицы. Вместо этого используются множественные свойства Битрикса.
Доступ к связным данным
После объявления связей вы можете получать доступ к связным данным с помощью имён связей. Доступ осуществляется по свойствам объекта. Название свойства = название метода связи (но без скобочек метода конечно)
Отложеная и жадная загрузка
В примерах выше используется отложенная загрузка (данные загружаются при первом обращении к ним). Когда идет работа с массивами данных, получаем запросы в цикле, проблему n + 1.
Чтобы избежать это необходимо использовать жадную загрузку:
- Метод with можно вызывать несколько раз.
- В качестве параметра принимает строку - название связи или несколько строк/массив таких строк
->with('brand', 'questions')/->with(['brand', 'questions']). - Вы можете указать вложенные связи
->with('questions.answers')(в таком случае загрузится сразу и список вопрос и для каждого вопроса список ответов. Всего 3 запроса - на товары, на вопросы, на ответы). - Для модификации запросов можно использовать колбеки. Например чтобы загрузить только активные вопросы, и для них загрузить ответы:
Использование моделей Eloquent
Вторая часть пакета - интеграция ORM Eloquent для пользовательских таблиц в Битриксе, то есть созданных при разработке вручную, а не поставляемых вместе системой. По сути это альтернатива прямым запросам в базу, D7 ORM и моделям D7Model из этого пакета.
Через Eloquent можно работать не только с пользовательскими таблицами, но и с Highload-блоками, что очень удобно.
При этом мы работаем с таблицей Highload-блока минуя какое-бы то ни было API Битрикса.
Стоит учитывать, что в отличии от Битрикса, Eloquent использует в качестве расширения PHP для доступа к mysql не mysql/mysqli, а PDO.
А это значит что:
- необходимо чтобы PDO был установлен и настроен
- будут создаваться два подключения к базе на запрос.
Заметка: Вопрос: Зачем в одном пакете Eloquent если уже есть D7Model? Что лучше выбрать? Ответ: Выбор между ними зависит от условий проекта и личных предпочтений. Eloquent удобнее и функциональнее чем всё что есть в Битриксе и в D7Model Например там есть полноценные связи между моделями через промежуточные таблицы и т д С другой стороны это большая внешняя зависимость со своими требованиями
Недостатки
Установка
Первым делом нужно поставить еще одну зависимость - composer require illuminate/database
После этого добавляем в init.php еще одну строку - Arrilot\BitrixModels\ServiceProvider::registerEloquent();
Теперь уже можно создавать Eloquent модели наследуясь от EloquentModel
Если таблица называется products (множественная форма названия класса), то protected $table = 'products'; можно опустить - это стандартное для Eloquent поведение.
Из нестандартного
- Первичным ключом является
ID, а неid - Поля для времени создания и обновления записи называются
UF_CREATED_ATиUF_UPDATED_AT, а неcreated_atиupdated_at
Если вы решили не добавлять в таблицу поля UF_CREATED_AT и UF_UPDATED_AT, то в модели нужно задать
public $timestamps = false;
Работа с хайлоадблоком через Eloquent
Представим что мы создали Highload-блок для брендов Brands, задали для него таблицу brands и добавили в него свойство UF_NAME.
Тогда класс-модель для него будет выглядеть так:
А для добавления новой записи в него достаточно следующего кода:
Для полноценной работой с Eloquent-моделями важно ознакомиться с официальной документацией данной ORM (ссылка еще раз)
В заключение обращу внимание на то что, несмотря на то что API моделей Битрикса и моделей Eloquent очень похожи (во многом из-за того что bitrix-models разрабатывались под влияением Eloquent) это всё же разные вещи и внутри они совершенно независимые. Нельзя допустим сделать связь (relation) Eloquent модели и Битриксовой модели.
Множественные свойства highload-блока и Eloquent модели
Множественные свойства в хайлоадблоках реализованы немного хитро. Данные хранятся сразу в 2х местах:
- непосредственно в таблице хайлоадблока в сериализованном виде.
- в дополнительной таблице для этого свойства.
К счастью пакет умеет довольно неплохо решает эту проблему.
При добавлении множественного свойства достаточно добавить в модель код этого свойства в поле-массив
$multipleHighloadBlockFieldsмодели Напримерpublic $multipleHighloadBlockFields = ['UF_SOME_MULTIPLE_FIELD'];После этого: $model['UF_SOME_MULTIPLE_FIELD']будет возвращать десериализованный массив- Для добавления/обновления значения поля, тоже достаточно положить в
$model['UF_SOME_MULTIPLE_FIELD']массив, вручную сериализовать его не нужно. - При добавлении/обновлении значения поля изменения будут автоматически применены к вспомогательной таблице. Вручную в ней ничего менять не требуется.
Чтобы последний пункт работал требуется установить дополнительную зависимость -
composer require illuminate/events. Без этой зависимости вспомогательные таблицы обновляться не будут. Немного подробнее про неё написано в следующем абзаце.
События в Eloquent моделях
В Eloquent есть так называемые События моделей / Model events, которые позволяют вклиниться в какой-то момент работы модели.
В целом довольно похожая на Битриксовые события OnBeforeIblockElementUpdate и т д вещь.
Если они вам нужны, то вместе с illuminate/database вам нужно поставить еще и зависимость illuminate/events:
composer require illuminate/events
Query Builder
При подключении Eloquent мы бесплатно получаем и Query Builder от Laravel https://laravel.com/docs/master/queries, который очень полезен если необходима прямая работа с БД минуя уровень абстракции моделей.
Он удобнее и несравнимо безопаснее чем $DB->Query() и прочее.
Работа с билдером проводится через глобально доступный класс DB.
Например добавление элемента бренда в HL-блок будет выглядеть так:
Постраничная навигация (pagination)
И Битрикс-модели и Eloquent-модели поддерживают ->paginate() и ->simplePaginate() (см. http://laravel.com/docs/master/pagination)
Для того чтобы затем отобразить постраничную навигацию через ->links() нужно
- Установить https://github.com/arrilot/bitrix-blade/
- Скопировать дефолтные вьюшки из https://github.com/laravel/framework/tree/master/src/Illuminate/Pagination/resources/views в
local/views/pagination
После этого вьюшки можно модифицировать или создавать новые.
Подключение к нескольким БД, детальная настройка подключения.
По умолчанию, для подключения к БД, используется стандартная конфигурация, с которой работает Битрикс.
Выглядеть она может, например, вот так (файл .settings.php.):
Для того, чтобы иметь возможность задействовать весь функционал Eloquent, необходимо добавить в файл .settings.php массив bitrix-models.illuminate-database с описанием подключения:
Важно! Если задан массив bitrix-models.illuminate-database, то все настройки подключения к БД для Eloquent берутся только из него, а стандартные - игнорируются.
Больше примеров настройки подключения (connections): https://github.com/laravel/laravel/blob/5.7/config/database.php
Активность элементов в D7Model/EloquentModel
В инфоблоках битрикса есть поле ACTIVE = 'Y'/'N', фильтрация по которому очень часто используется. В хайлоадблоках и кастомных таблицах такого поля по-умолчанию нет, однако пакет предоставляет trait который помогает в создании подобной функциональности. Как это работает:
- Добавляем поле UF_DEACTIVATED_AT типа datetime в таблицу/хайлоадблок.
- Добавляем в D7Model/EloquentModel трейт
use \Arrilot\BitrixModels\Models\Traits\DeactivationTrait; -
Теперь в модели доступны следуюющие методы:
3.1.
$model->deactivate()и$model->activate()- деактивирует или активирует элемент в БД.3.2.
$model->markForDeactivation()и$model->markForActivation()- тоже самое, но только меняет php переменную, не выполняет ->save(). Полезно если вместе с активацией нужно сделать и другие изменения в таблице и не хочется делать дополнительный запрос в БД.3.3. Скоупы
->active()и->deactivated(). НапримерSomeD7Model::query()->active()->getList().
All versions of bitrix-models with dependencies
illuminate/support Version ^5.4,<5.8
illuminate/pagination Version ^5.4,<5.8
