iswin / borm
ORM Library for Bitrix iblocks and highload blocks
Requires
- php: >=7.0.0
This package is not auto-updated.
Last update: 2024-06-05 15:14:08 UTC
README
Модуль позволяет работать с разделами и элементами инфоблока, используя ORM. По синтаксису идентичный Bitrix ORM. Также модуль расширяет Bitrix ORM, добавляя возможность использовать подзапросы и получать на выходе объекты, а не массивы.
Сущности объектов
Перед началом использования, необходимо подготовить сущности объектов используемых в вашем проекте. Сущности должны располагаться в папке вашего модуля.
Сущности объектов highload блоков должны располагаться в папке:
/local/modules/vendor.module/lib/entities/hlblock
или/bitix/modules/vendor.module/lib/entities/hlblock
. Все сущности объектов highload блоков, должны наследовать абстрактный класс\Iswin\Borm\HlBlock\HighLoadEntity
и включать методыgetTableName
,getMap
иgetName
. Пример сущности:<?php namespace Vendor\Module\Entities\HlBlock; use Iswin\Borm\HlBlock\HighLoadEntity; use Bitrix\Main\Entity; class ColorTable extends HighLoadEntity { public static function getTableName () { return "project_colors"; } public static function getMap() { return [ new Entity\IntegerField('ID', [ 'title' => 'ID', 'primary' => true, 'autocomplete' => true ]), new Entity\StringField('UF_NAME', ['title' => 'UF_NAME']), new Entity\StringField('UF_XML_ID', ['title' => 'UF_XML_ID']) ]; } public function getName() { return $this->getField('UF_NAME'); } public function setName($name) { return $this->setField('UF_NAME', $name); } //... рекомендуется на все значимые поля создать гетеры // и сеттеры, аналогичные getName() и setName($name) }
Сущности разделов ифноблоков должны располагаться в папке:
/local/modules/vendor.module/lib/entities/iblock
или/bitix/modules/vendor.module/lib/entities/iblock
. Все сущности разделов инфоблоков, должны наследовать абстрактный класс\Iswin\Borm\Iblock\Section\SectionEntity
и содержать методgetIblockCode
. Пример сущности:<?php namespace Vendor\Module\Entities\Iblock; use Iswin\Borm\Iblock\Section\SectionEntity; class CatalogSection extends SectionEntity { const IBLOCK_CODE = 'catalog'; public static function getIblockCode() { return self::IBLOCK_CODE; } public function getName() { return $this->getField('UF_NAME'); } public function setName($name) { return $this->setField('UF_NAME', $name); } //... рекомендуется на все значимые поля создать гетеры // и сеттеры, аналогичные getName() и setName($name) }
Сущности элементов ифноблоков должны располагаться в папке:
/local/modules/vendor.module/lib/entities/iblock
или/bitix/modules/vendor.module/lib/entities/iblock
. Все сущности разделов инфоблоков, должны наследовать абстрактный класс\Iswin\Borm\Iblock\Element\ElementEntity
и содержать методgetIblockCode
. Пример сущности:<?php namespace Vendor\Module\Entities\Iblock; use Iswin\Borm\Iblock\Element\ElementEntity; class CatalogElement extends ElementEntity { const IBLOCK_CODE = 'catalog'; public static function getIblockCode() { return self::IBLOCK_CODE; } public function getName() { return $this->getField('UF_NAME'); } public function setName($name) { return $this->setField('UF_NAME', $name); } //... рекомендуется на все значимые поля создать гетеры // и сеттеры, аналогичные getName() и setName($name) }
Выборка элементов и разделов инфоблока
Пример простого запроса выбирающий только названий всех активных разделов инфоблока, отсортированных по полю SORT:
<?php use \Vendor\Module\Entities\Iblock\CatalogSection; /** @var CatalogSection[] $activeSections */ $activeSections = CatalogSection::query() ->addSelect('NAME') ->addFilter('ACTIVE', 'Y') ->setOrder(['SORT' => 'asc']) ->fetchAll();
2. Пример активных разделов, со связанным с ним справочником через пользовательское свойство UF_DIRECTORY
(использование подзапросов для выборки связанных объектов, позволяет не осуществлять запросы в цикле, все связанные значения выбираеются одним запросом):
<?php
use \Vendor\Module\Entities\Iblock\CatalogSection;
use \Vendor\Module\Entities\HlBlock\DirectoryTable;
/** @var CatalogSection[] $activeSections */
$activeSections = CatalogSection::query()
->addSelect('NAME')
//таким образом можно добваить в выборку любое пользовательское свойство
->addSelectProp('UF_TEST_PROP')
->addSelectLink(
'UF_DIRECTORY',
//в подзапросе нужно указать какие поля связанного объекта нужно выбрать
DirectoryTable::query()
->addSelect('UF_NAME')
->addSelect('UF_DESCRIPTION')
)
->addFilter('ACTIVE', 'Y')
->fetchAll();
/** @var CatalogSection $activeSection */
foreach($activeSections as $activeSection) {
//выведет название раздела
print $activeSection->getName() . "\n";
//выведет название связанного через свойство UF_DIRECTORY элемента highload блока
//если свойство множественное, нужно использовать getLinkObjectByCode - чтобы получить массив
print $activeSection->getOneLinkObjectByCode('UF_DIRECTORY')->getName();
//выведет описание связанного через свойство UF_DIRECTORY элемента highload блока
print $activeSection->getOneLinkObjectByCode('UF_DIRECTORY')->getField('UF_DESCRIPTION');
//Выведет значение пользовательского свойства UF_TEST_PROP
print $activeSection->getField('UF_TEST_PROP') . "\n";
}
3. Структура запросов для элементов инфоблока, ничем не отличается от запросов для разделов:
<?php
use \Vendor\Module\Entities\Iblock\CatalogProduct;
use \Vendor\Module\Entities\HlBlock\BrandTable;
/** @var CatalogProduct[] $activeSections */
$activeProducts = CatalogProduct::query()
->addSelect('NAME')
//таким образом можно добваить в выборку любое свойство элемента инфоблока
->addSelectProp('TEST_PROPERTY')
->addSelectLink(
'BRAND',
//в подзапросе нужно указать какие поля связанного объекта нужно выбрать
BrandTable::query()
->addSelect('UF_NAME')
->addSelect('UF_DESCRIPTION')
)
->addFilter('ACTIVE', 'Y')
->fetchAll();
/** @var CatalogProduct $activeProduct */
foreach($activeProducts as $activeProduct) {
//выведет название товара
print $activeProduct->getName() . "\n";
//выведет название связанного через свойство BRAND элемента highload блока
//если свойство множественное, нужно использовать getLinkObjectByCode - чтобы получить массив
print $activeProduct->getOneLinkObjectByCode('BRAND')->getName();
//выведет описание связанного через свойство UF_DIRECTORY элемента highload блока
print $activeProduct->getOneLinkObjectByCode('BRAND')->getField('UF_DESCRIPTION');
//Выведет значение свойства TEST_PROPERTY, такая работа со свойствами - это единственное отличие от разделов
print $activeProduct
->getPropsCollection() //\Iswin\Borm\Iblock\Element\PropsCollection
->getProperty('TEST_PROPERTY') //\Iswin\Borm\Iblock\Element\Property
->getValue();
//это объект для работы с данными каталога продукта, в т.ч. и ценами
//см. класс \Iswin\Borm\Iblock\Element\CatalogData
$catalogData = $activeProduct->getCatalogData();
}
4. Постраничная навигация в разделах и элементах инфоблока (работает по одинаковому принципу):
<?php
use \Vendor\Module\Entities\Iblock\CatalogSection;
use \Iswin\Borm\Iblock\PageNav;
$currentPageNum = 1; //текущий номер страницы
$pageSize = 10; //кол-во элементов на странице
$nav = PageNav::getInstance($currentPageNum, $pageSize);
/** @var CatalogSection[] $activeSections */
$activeSections = CatalogSection::query()
->addSelect('NAME')
->addFilter('ACTIVE', 'Y')
->setOrder(['SORT' => 'asc'])
->setNav($nav)
->fetchAll();
var_dump($nav->getAllCount()); //выведет общее кол-во элементов попадающих под фильтр
var_dump($nav->getPageCount()); //выведет общее кол-во страниц для постраничной навигации
Выборка элементов highload блоков
--------
1. Пример сущности инфоблока, имеющего связь с другим хайлоад инфоблока, с элементов инфоблока, а также с разделом инфоблока:
<?php
namespace Vendor\Module\Entities\HlBlock;
use Iswin\Borm\HlBlock\HighLoadEntity;
use Iswin\Borm\HlBlock\References\IblockReference;
use Bitrix\Main\Entity;
class BrandTable extends HighLoadEntity {
public static function getTableName ()
{
return "brands";
}
public static function getMap() {
return [
new Entity\IntegerField('ID', [
'title' => 'ID',
'primary' => true,
'autocomplete' => true
]),
new Entity\StringField('UF_NAME', ['title' => 'UF_NAME']),
new Entity\StringField('UF_XML_ID', ['title' => 'UF_XML_ID']),
//Обычный битриксовый функционал объявления связи между highload блоками
new Entity\IntegerField('UF_COUNTRY', ['title' => 'UF_COUNTRY']),
new Entity\ReferenceField(
'COUNTRY',
'\Vendor\Module\Entities\HlBlock\Country',
array('=this.UF_COUNTRY' => 'ref.ID')
),
//Объявление связи с разделом инфоблока с помощью модуля borm
new Entity\IntegerField('UF_SECTION', ['title' => 'Какой-то раздел инфоблока']),
new IblockReference(
'SECTION',
'\Vendor\Module\Entities\Iblock\CatalogSection',
array('=this.UF_SECTION' => 'ref.ID')
),
//Объявление связи с элементом инфоблока с помощью модуля borm
new Entity\IntegerField('UF_ELEMENT', ['title' => 'Какой-то элемент инфоблока']),
new IblockReference(
'ELEMENT',
'\Vendor\Module\Entities\Iblock\CatalogProduct',
array('=this.UF_ELEMENT' => 'ref.ID')
)
];
}
public function getName() {
return $this->getField('UF_NAME');
}
public function setName($name)
{
return $this->setField('UF_NAME', $name);
}
}
2. Выборка всех брендов со всеми связанными объектами:
<?php
use \Vendor\Module\Entities\Iblock\CatalogSection;
use \Vendor\Module\Entities\Iblock\СatalogProudct;
use \Vendor\Module\Entities\HlBlock\CountryTable;
use \Vendor\Module\Entities\HlBlock\BrandTable;
$brands = BrandTable::query()
->addSelect('UF_NAME')
->addSelect('UF_DESCTIPTION')
//добавим стандартным битриксовым способом в выборку все поля связанной страны
//отличие будет заключаться только в результате выборки, в постороении запроса
//выборка связанных таблиц происходит в соответствии с правилами Bitrix Orm
->addSelect('COUNTRY.*')
//Выберем связанный раздел инфоблока
->addSelect(
'SECTION',
//как и в случае с объектами инфоблоков перечислим какие
// поля из связанного объекта нам нужны
CatalogSection::query()
->addSelect('NAME')
->addSelect('DESCRIPTION')
)
//выберем связанный элемент инфоблока
->addSelect(
'ELEMENT',
//как и в случае с объектами инфоблоков перечислим какие
// поля из связанного объекта нам нужны
CatalogProduct::query()
->addSelect('NAME')
->addSelect('PREVIEW_TEXT')
);
/** @var BrandTable $brand */
foreach($brands as $brand) {
var_dump($brand->getField('UF_NAME')); //выведет название бренда
var_dump($brand->getField('UF_DESCRIPTION')); //выведет описание бренда
/** @var CountryTable $country */
$country = $brand->getField('COUNTRY');
var_dump($country->getName()); //выведет название страны
/** @var CatalogSection $iblockSection */
$iblockSection = $brand->getField('SECTION');
var_dump($iblockSection->getName()); //выведет название связанного раздела инфоблока
/** @var CatalogProduct $iblockProduct */
$iblockProduct = $brand->getField('ELEMENT');
var_dump($iblockProduct->getName()); //выведет название связанного элемента инфоблока
}
Сохранение объектов
--------
1. Простой пример сохранения основных полей (справедлив и для объектов инфоблоков и для объектов highload блоков):
<?php
use \Vendor\Module\Entities\Iblock\CatalogSection;
/** @var CatalogSection $activeSection */
$activeSection = CatalogSection::query()
->addSelect('NAME')
->addFilter('ACTIVE', 'Y')
->addFilter('ID', 123)
->setOrder(['SORT' => 'asc'])
->getOneResult(); //вернет null если раздел не будет найден
//обновит название выбранного раздела
$activeSection
//setField в сущностях рекомендуется заменять на сетеры для простоты
// тогда код будет более читаемым: ->setName('Новое имя')->save();
->setName('Новое имя')
->save();
$newSection = CatalogSection::getInstance()
->setName('Новый раздел')
//установим значение пользовательского свойства
->setField('UF_TEST_PROP', 'test value')
//проставляет флаг активности
->enable()
//генерирует символьный код из названия
->makeCode()
->save();
var_dump($newSection->getId()); //выведет ID вновь созданного раздела
2. Пример обновления свойств и данных каталога для элемента инфоблока:
<?php
use \Vendor\Module\Entities\Iblock\CatalogProduct;
use \Iswin\Borm\Iblock\Element\CatalogPrice;
/** @var CatalogProduct $activeProduct */
$activeProduct = CatalogProduct::query()
->addSelect('NAME')
->addFilter('ACTIVE', 'Y')
->addFilter('ID', 123)
->setOrder(['SORT' => 'asc'])
->getOneResult(); //вернет null если раздел не будет найден
//обновит название выбранного раздела
$activeProduct
//установим значение свойства элемента инфоблока
->getPropsCollection()->setValue('TEST_STRING_PROPERTY', 'new prop value');
//если мы не уверены в том, что элемент инфоблока - продукт
//но нам нужно выставить ему цены (или другие данные каталога), то необходима проверка
$catalogData = $activeProduct->getCatalogData() ? : $activeProduct->makeCatalogData();
//выставим настройки продукта, подробнее см. в классе \Iswin\Borm\Iblock\Element\CatalogData
$catalogData
->setProductTypeWithOffers()
->setPriceTypeSingle();
$basePrice = $catalogData->getBasePrice();
//если по каким то причинам базовой цены у продукта нет, создадим ее
if (!$basePrice) {
//создадим пустой инстанс цены
$basePrice = CatalogPrice::getInstance(false, []);
//выставим тип цены, как базовый см.
$basePrice->setType(CatalogProduct::getBasePriceType());
$catalogData->addPrice($basePrice);
}
//сохраняет продукт, включая все свойства и данные каталога
$activeProduct->save();