/bitrix-orm-bundle

Bitrix ORM bundle [fork]

Primary LanguagePHP

Bitrix ORM bundle

Форк. Упростил маленько, избавился от лишних зависимостей и т.п.

Интересный пример работы Symfony + Битрикс. Но на мой взгляд, перекручено с абстракциями. Что не отменяет качественной работы автора оригинального пакета.

Оригинальная документация

Оглавление

Установка

Добавить в composer.json:

  "repositories": [
    {
      "type": "git",
      "url": "https://github.com/proklung/bitrix-orm-bundle"
    }
  ]

Выполнить:

$ composer require proklung/bitrix-orm-bundle

Функциональность

  • Определение необходимых параметров моделей bitrix-orm через аннотации
  • Автоматический поиск моделей, имеющих аннотации, поддерживаемые бандлом
  • Автоматическая регистрация репозиториев как сервисов
  • Автоматическое заполнение зависимостей модели
  • Кеширование объектов в памяти (использование везде одного и того же объекта)
  • Встроенное кеширование битрикс (в т.ч. с использованием тегов)

Список доступных аннотаций

Файлы с аннотациями подгружаются из всех папок в директории src проекта, имеющих название Model (и их поддиректориях).

Фабрики и репозитории

Каждая аннотация имеет параметры:

  • factory - класс фабрики моделей. Обязательный параметр
  • repository - класс репозитория. Является обязательным для всех аннотаций, кроме CatalogGroup и HlbReference, что классы репозиториев для остальных моделей, указанные по умолчанию, являются абстрактными

1. IblockElement

Аннотация для элементов инфоблоков

Список параметров:

  • iblockType - тип инфоблока (IBLOCK_TYPE_ID). Обязательный параметр
  • iblockCode - код инфоблока (CODE). Обязательный параметр

2. IblockSection

Аннотация для разделов инфоблоков

Список параметров:

  • iblockType - тип инфоблока (IBLOCK_TYPE_ID). Обязательный параметр
  • iblockCode - код инфоблока (CODE). Обязательный параметр

3. D7Item

Аннотация для собственных таблиц

Список параметров:

  • table - класс, расширяющий Bitrix\Main\Entity\DataManager. Обязательный параметр

4. HlbItem

Аннотация для элементов HL-блоков

Список параметров:

  • hlBlockName - Имя HL-блока, которому принадлежит данная модель. Обязательный параметр
  • entityCache (bool) - Использование хака, ускоряющего инициализацию hl-блоков путем кеширования hlblockentity. По умолчанию включен

5. HlbReferenceItem

Аннотация для элементов HL-блоков-справочников

Список параметров:

  • hlBlockName - Имя HL-блока, которому принадлежит данная модель. Обязательный параметр
  • entityCache (bool) - Использование хака, ускоряющего инициализацию hl-блоков путем кеширования hlblockentity. По умолчанию включен

6. CatalogGroup

Аннотация для типов цен. Дополнительных параметров нет.

Кеширование

Данные аннотации работают только тогда, когда задана аннотация "фабрики и репозитория". При указании этих аннотаций регистрируются сервисы, являющиеся слоями кеширования. RepositoryRegistry в таком случае возвращает данный сервис вместо репозитория. Работают данные слои на ReflectionClass следующим образом:

  1. Заданы методы add, update, delete, deleteById, которые проксируются в репозиторий. При этом в объектном кеше удаляется/обновляется/добавляется элемент
  2. Для остальных методов:
  • Если заданы excludedMethods, то данные методы просто проксируются в репозиторий
  • Происходит анализ входных параметров с помощью \ReflectionMethod, вызов метода, кеширование результата:
    • Если количество параметров == 1, то
      • Если имя параметра id, то метод считается "получением элемента по ID" для BitrixCache на результат вешается tag . ':' . $id
      • Если имя параметра xmlId, то метод считается "получением элемента по XML_ID" для BitrixCache на результат вешается tag . ':' . $xmlId
      • Если имя параметра code, то метод считается "получением элемента по CODE", для BitrixCache на результат вешается tag . ':' . $code
      • Если параметр является объектом BitrixArrayItemBase, то метод считается "получением объекта/ов по родительскому объекту" (цен по офферу, остатков по офферу), для BitrixCache на результат вешается collectionTag . ':' . $item->getId()
    • Если параметров != 1, то происходит кеширование по ключу кеша = \get_class($model) . json_encode($parameters)

Можно указывать и Cache и BitrixCache, либо один из них, либо вовсе не указывать

1. Cache

Параметры:

  • class - класс, реализующий KantShop\BitrixOrmBundle\Cache\CacheInterface (ArrayCache по умолчанию)
  • excludedMethods - массив методов репозитория, результаты которых кешироваться не будут

2. BitrixCache
  • class - класс, реализующий KantShop\BitrixOrmBundle\Cache\BitrixCacheInterface (BitrixCache по умолчанию)
  • excludedMethods - массив методов репозитория, результаты которых кешироваться не будут
  • tag - тег кеша
  • collectionTag - тег коллекции элементов
  • cacheTime - время кеширования (1 час по умолчанию)

Гидратация объектов

Данные аннотации работают только тогда, когда задана аннотация "фабрики и репозитория". При указании этих аннотаций регистрируются сервисы, которые заполняют необходимые данные для модели. Гидратор вызывается после получения данных из репозитория/кеша. Для каждого инстанса модели он вызывается один раз (используется \SplObjectStorage). Если репозиторий вернул ArrayCollection, то гидратор вызывается для каждого элемента поочередно.

1. Hydrator

Параметры:

  • class - класс, реализующий KantShop\BitrixOrmBundle\HYdrator\HydratorInterface

RepositoryRegistry

Сервис, позволяющий получить нужный репозиторий по классу модели

<?php

use Acme\AppBundle\Model\City;

$registry = $serviceContainer->get('bitrix_orm.repository_registry');
$cityRepository = $registry->get(City::class);

Примеры использования

Элемент HL-блока

Модель:

<?php

namespace Acme\AppBundle\Model;

use KantShop\BitrixOrm\Model\D7Item;
use KantShop\BitrixOrmBundle\Annotation as ORM;
use Acme\AppBundle\Repository\CityRepository;
use Acme\AppBundle\Factory\CityFactory;
use Acme\AppBundle\Hydrator\CityHydrator;

/**
 * @ORM\HlbItem(
 *     hlBlockName="Cities",
 *     repository=CityRepository::class,
 *     factory=CityFactory::class
 * )
 * @ORM\BitrixCache(tag="city",cacheTime=3600)
 * @ORM\Cache()
 * @ORM\Hydrator(class=CityHydrator::class)
 */
class City extends D7Item
{
    // необходимые свойства модели
}

Фабрика:

<?php

namespace Acme\AppBundle\Factory;

use Acme\AppBundle\Model\City;
use KantShop\BitrixOrm\Factories\D7ItemFactory;
use KantShop\BitrixOrm\Model\D7Item;

class CityFactory extends D7ItemFactory
{
    public function createItem(array $data): D7Item
    {
        return new City($data);
    }
    
    
    public function getSelect(): array
    {
        return ['*'];
    }
}

Репозиторий:

<?php

namespace Acme\AppBundle\Repository;

use KantShop\BitrixOrm\Repository\D7Repository;

class CityRepository extends D7Repository
{

}

Гидратор:

<?php

namespace Acme\AppBundle\Hydrator;

use Acme\AppBundle\Model\City;
use Acme\AppBundle\Service\StreetProvider;
use KantShop\BitrixOrmBundle\Hydrator\HydratorInterface;

class CityHydrator implements HydratorInterface
{
    /**
     * @var StreetProvider
     */
    protected $streetProvider;
    
    public function __construct(StreetProvider $streetProvider) {
        $this->streetProvider = $streetProvider;
    }

    /**
     * @param City $object
     * @return City
     */
    public function fill($object){
        $object->setStreets($this->streetProvider->getCityStreets($object));
        return $object;
    }
}

Сервис:

<?php

namespace Acme\AppBundle\Service;

use Acme\AppBundle\Model\City;
use KantShop\BitrixOrmBundle\Registry\RepositoryRegistryInterface;

class CityService
{
    protected $repository;
    
    public function __construct(RepositoryRegistryInterface $repository)
    {
        $this->repository = $repository->get(City::class);
    }
}

Элемент инфоблока

Модель:

<?php

namespace Acme\AppBundle\Model;

use KantShop\BitrixOrm\Model\IblockElement;
use KantShop\BitrixOrmBundle\Annotation as ORM;

/**
 * Class Product
 * @package Acme\AppBundle\Model
 *
 * @ORM\IblockElement(
 *     iblockType="catalog",
 *     iblockCode="products",
 *     factory="Acme\AppBundle\Factory\ProductFactory",
 *     repository="Acme\AppBundle\Repository\ProductRepository",
 * )
 */
class Product extends IblockElement
{
    // ...
}

Можно указать iblockСode и iblockType, используя константы:

<?php

namespace Acme\AppBundle\Model;

use KantShop\BitrixOrm\Model\IblockElement;
use KantShop\BitrixOrmBundle\Annotation as ORM;
use Acme\AppBundle\Enum\IblockType;
use Acme\AppBundle\Enum\IblockCode;

/**
 * Class Product
 * @package Acme\AppBundle\Model
 *
 * @ORM\IblockElement(
 *     iblockType=IblockType::CATALOG,
 *     iblockCode=IblockCode::PRODUCTS,
 *     factory="Acme\AppBundle\Factory\ProductFactory",
 *     repository="Acme\AppBundle\Repository\ProductRepository",
 * )
 */
class Product extends IblockElement
{
    // ...
}

Репозиторий:

<?php

namespace Acme\AppBundle\Repository;

use KantShop\BitrixOrm\Repository\IblockElementRepository;

class ProductRepository extends IblockElementRepository
{

}

Тип цен

Модель:

<?php

namespace Acme\AppBundle\Model;

use KantShop\BitrixOrm\Model\CatalogGroup as BaseCatalogGroup;
use KantShop\BitrixOrmBundle\Annotation as ORM;

/**
 * Class CatalogGroup
 * @package Acme\AppBundle\Model
 *
 * @ORM\CatalogGroup()
 */
class CatalogGroup extends BaseCatalogGroup
{

}

Автовайринг RepositoryRegistry
<?php

namespace Acme\AppBundle\Service;

use Acme\AppBundle\Model\City;
use Acme\AppBundle\Repository\CityRepository;
use KantShop\BitrixOrmBundle\Registry\RepositoryRegistryInterface;

class CityService
{
    /**
     * @var CityRepository
     */
    protected $repository;

    /**
     * @var FileRepository
     */
    protected $fileRepository;

    public function __construct(RepositoryRegistryInterface $registry)
    {
        $this->repository = $registry->get(City::class);
        $this->fileRepository = $registry->get(File::class);
    }
}