Commit 8db24bbd authored by Telman Mazhlumov's avatar Telman Mazhlumov

init

parents
build
composer.lock
vendor
phpcs.xml
phpunit.xml
.idea
\ No newline at end of file
# defa-public\bx-scheme
[![Latest Stable Version](https://poser.pugx.org/defa-public/bx-scheme/v/stable)](https://packagist.org/packages/defa-public/bx-scheme)
[![Total Downloads](https://poser.pugx.org/defa-public/bx-scheme/downloads)](https://packagist.org/packages/defa-public/bx-scheme)
[![Latest Unstable Version](https://poser.pugx.org/defa-public/bx-scheme/v/unstable)](https://packagist.org/packages/defa-public/bx-scheme)
[![License](https://poser.pugx.org/defa-public/bx-scheme/license)](https://packagist.org/packages/defa-public/bx-scheme)
Схема для абстрагирования CRUD операций над стандартными сущностями 1С-Битрикс.
## Установка
Composer
``` bash
$ composer require defa-public/bx-scheme
```
## Возможности
* Упрощеное управление (СRUD) сущностями 1С-Битрикс
* Создание связанных сущностей, если этого требует система
* Генерация кода миграций для существующих сущностей
## Драйверы
Нужны для поддержки разных частей системы (к примеру: инфоблоков, пользовательских полей и т.д.)
Текущие драйверы:
* Highload-блоки (hl) ***- в разработке***
* Валюта (currency) ?***- в разработке***
* Группы пользователей (userGroup) ***- в разработке***
* Группы свойств (salePropGroup) ***- в разработке***
* Инфоблоки (iblock) ***- в разработке***
* Пользовательские поля (uf) ***- в разработке***
* Почтовые события (mailEvent) ***- в разработке***
* Почтовые шаблоны (mailTemplate) ***- в разработке***
* Сайт (site) ?***- в разработке***
* Свойства (saleProp) ***- в разработке***
* Свойства инфоблока (iblockProp) ***- в разработке***
* Свойства инфоблока типа список (iblockPropEnum) ***- в разработке***
* Типы плательщиков (salePersonType) ***- в разработке***
* Типы цен (catalogPriceType) ***- в разработке***
* Типы инфоблоков (iblockType) ***- в разработке***
* Языки (lang) ?***- в разработке***
Для создания собственных драйверов, нужно унаследоваться от класса ``Defa\BxScheme\BaseDriver``
и указать его в конфигурационном объекте ``Defa\BxScheme\Config\BaseConfig`` в соответствующем поле
## Конфигурирование
Конфигурирование осуществляется путём создания объекта класса ``Defa\BxScheme\Config\BaseConfig``
и передачи этого объекта на вход объекту класса ``Defa\BxScheme\Scheme``
## Поддерживаемые системы миграций
Нет ограничений. Для корректного процесса генерации кода, нужно правильно сконфигурировать
конфигурационный объект и передать его конструктору базового класса.
## Примеры
```php
<?php
//Example Code
```
\ No newline at end of file
{
"name": "defa-public/bx-data-abstraction-layer",
"type": "library",
"description": "Библиотека DAL (data abstraction layer) для 1С-Битрикс. Позволяет получить доступ к данным в упрощенном виде",
"keywords": [
"defa",
"bitrix",
"bx-data-abstraction-layer",
"bx-dal"
],
"homepage": "https://gitlab.idefa.ru/defa-public/bx-data-abstraction-layer",
"license": "MIT",
"authors": [
{
"name": "Telman Mazhlumov",
"email": "telnac11@gmail.com",
"homepage": "https://github.com/Quadrotell",
"role": "Developer"
}
],
"require": {
"php" : "~7.0"
},
"require-dev": {
"nette/php-generator": "^3.0",
"phpunit/phpunit" : "^5.4"
},
"autoload": {
"psr-4": {
"Defa\\BxDal\\": "src"
}
}
}
\ No newline at end of file
<?php
namespace Defa\BxDal;
use Defa\BxDal\Interfaces\IDataDriver;
use Defa\BxDal\Interfaces\IVOGenerator;
/**
* Class BaseDriver
* @package Defa\BxDal
*/
abstract class BaseDriver implements Interfaces\IDataDriver
{
/**
* @var string Класс, отвечающий за генерацию VO
*/
const VO_GENERATOR = '';
/**
* @var int|string последний добавленный идентификатор
*/
protected $lastAddedUniqueID;
/**
* Возвращает объект, который сгенерирует нужный текущему драйверу ValueObject
*
* @return IVOGenerator
*/
public static function getVOGenerator(): IVOGenerator
{
if (class_exists(static::VO_GENERATOR)) {
return new {static::VO_GENERATOR}();
}
throw new \RuntimeException('Не найден класс генератор VO');
}
/**
* Позволяет закэшировать выборку с помощью нужного драйвера. Должен вызываться перед fetch
* Итоговый вывод будет зависеть от наличия данных в кэше
*
* @param string $cacheDriver ? - нужен интерфейс кэширования + добавить реализацию для редиса
* @param string $tag
* @param int $time
* @return IDataDriver
*/
public function byCache(string $cacheDriver, string $tag, int $time = 3600): IDataDriver
{
return $this;
}
/**
* Возвращает последний идентификатор добавленного элемента
*
* @return string
*/
public function lastAddedUniqueID(): string
{
return (string)$this->lastAddedUniqueID;
}
}
\ No newline at end of file
<?php
namespace Defa\BxDal;
use Defa\BxDal\Interfaces\IVOGenerator;
/**
* Class SchemeGenerator
* @package Defa\BxDal
*/
abstract class BaseVOGenerator implements IVOGenerator
{
}
\ No newline at end of file
<?php
namespace Defa\BxDal;
use Defa\BxDal\Interfaces\IValueObject;
abstract class BaseValueObject implements IValueObject
{
/**
* Вызывает внутри себя метод initFromArray, для установки всех необходимых полей
*
* IValueObject constructor.
* @param array $rawData ['fieldName' => 'fieldVal']
*/
public function __construct(array $rawData)
{
foreach ($rawData as $key => $value) {
if ($this->offsetExists($key)) {
$this->offsetSet($key, $value);
}
}
}
/**
* Whether a offset exists
* @link https://php.net/manual/en/arrayaccess.offsetexists.php
* @param mixed $offset <p>
* An offset to check for.
* </p>
* @return boolean true on success or false on failure.
* </p>
* <p>
* The return value will be casted to boolean if non-boolean was returned.
* @since 5.0.0
*/
public function offsetExists($offset): bool
{
return ($offset !== '' ? property_exists(static::class, $offset) : false);
}
/**
* Offset to retrieve
* @link https://php.net/manual/en/arrayaccess.offsetget.php
* @param mixed $offset <p>
* The offset to retrieve.
* </p>
* @return mixed Can return all value types.
* @since 5.0.0
*/
public function offsetGet($offset)
{
if ($offset !== '' && property_exists(static::class, $offset)) {
return $this->{$offset};
}
throw new \InvalidArgumentException('Был передан некорректный ключ (пустой или не существующий');
}
/**
* Offset to set
* @link https://php.net/manual/en/arrayaccess.offsetset.php
* @param mixed $offset <p>
* The offset to assign the value to.
* </p>
* @param mixed $value <p>
* The value to set.
* </p>
* @return void
* @since 5.0.0
*/
public function offsetSet($offset, $value)
{
if ($offset !== '' && property_exists(static::class, $offset)) {
$this->{$offset} = $value;
} else {
throw new \InvalidArgumentException('Был передан некорректный ключ (пустой или не существующий');
}
}
/**
* Offset to unset
* @link https://php.net/manual/en/arrayaccess.offsetunset.php
* @param mixed $offset <p>
* The offset to unset.
* </p>
* @return void
* @since 5.0.0
*/
public function offsetUnset($offset)
{
if ($offset !== '' && property_exists(static::class, $offset)) {
unset($this->{$offset});
} else {
throw new \InvalidArgumentException('Был передан некорректный ключ (пустой или не существующий');
}
}
/**
* Конвертирует ключи в нужный формат
*
* @return self
* @throws \RuntimeException
*/
public function convertKeys(): IValueObject
{
// TODO: Implement convertKeys() method.
}
/**
* Возвращает префикс, в случае если свойство дублирует поле
*
* @return string
*/
public function prefixForDuplicateProp(): string
{
// TODO: Implement prefixForDuplicateProp() method.
}
/**
* Возвращает обязательные поля
*
* @return array
*/
public function getRequiredFields(): array
{
// TODO: Implement getRequiredFields() method.
}
/**
* Инициализирует объект из массива
*
* @param array $rawData
* @return IValueObject
*/
public function initFromArray(array $rawData): IValueObject
{
// TODO: Implement initFromArray() method.
}
/**
* Конвертирует текущий объект в массив с ключами и значениями нужного формата
*
* @return array
*/
public function convertToArray(): array
{
// TODO: Implement convertToArray() method.
}
}
\ No newline at end of file
<?php
namespace Defa\BxDal;
use Defa\BxDal\Drivers\HLBlock;
use Defa\BxDal\Drivers\IBlock\{IBlockElement, IBlockSection};
/**
* Class Config
* @package Defa\BxDal
*/
class Config
{
/**
* @var array хранилище значений
*/
private static $storage = [
'drivers' => [
IBlockElement::class,
IBlockSection::class,
HLBlock::class,
],
'vo' => [
'generation' => [
'classSuffix' => '%sVO',
'namespace' => 'Defa\BxDal\ValueObjects\\',
'filepath' => __DIR__ . '/ValueObjects/%s'
]
]
];
/**
* Получить значение из конфига
*
* @param string $key
* @param null $defaultValue
* @return array|mixed|null
*/
public static function get(string $key, $defaultValue = null)
{
static::loadConfig();
if (strpos($key, '.') === false) {
return static::$storage[$key] ?? $defaultValue;
}
$tmpStorage = static::$storage;
foreach (explode('.', $key) as $segment) {
if (\array_key_exists($segment, $tmpStorage)) {
$tmpStorage = $tmpStorage[$segment];
} else {
return $defaultValue;
}
}
return $tmpStorage;
}
/**
* Загружает сторонний конфиг, если он был указан через переменные окружения (BX_DAL_CONFIG)
*/
private static function loadConfig()
{
$configPath = getenv('BX_DAL_CONFIG');
if ($configPath !== false && file_exists($configPath)) {
try {
/** @var array $configPath */
$customConfig = require $configPath;
if (\is_array($customConfig) && !empty($customConfig)) {
static::$storage = array_merge_recursive( static::$storage, $customConfig);
}
} catch (\Exception $err){}
}
}
}
\ No newline at end of file
<?php
/**
* Class DataLayer
*/
class DataLayer
{
/**
* Служебные методы
*/
const SERVICE_METHODS = [
'getDriverClass'
];
/**
* @var array
*/
private static $customDrivers = [];
/**
* @param $method
* @param $args
* @return \Defa\BxDal\BaseDriver
*/
public static function __callStatic($method, $args): \Defa\BxDal\BaseDriver
{
if (!\in_array($method, static::SERVICE_METHODS, true)) {
$driverClass = static::getDriverClass($method);
if (!empty($driverClass)) {
return new $driverClass($args);
}
}
throw new BadMethodCallException('Метод не существует или не может быть вызван');
}
/**
* Ищет класс-драйвер. Сначала выборка идёт по "кастомным" и только потом по встроенным
*
* @param $name
* @return string
*/
private static function getDriverClass($name): string
{
$sources = [static::$customDrivers, \Defa\BxDal\Config::get('drivers')];
foreach ($sources as $driverSource) {
if (!empty($driverSource)) {
foreach ($driverSource as $driver) {
$driverName = array_pop($tmp = explode('\\', $driver));
if (strtolower($driverName) === strtolower($name)) {
return $driver;
}
}
}
}
throw new RuntimeException(sprintf('Драйвер для %s не найден', $name));
}
/**
* Добавляет кастомный драйвер. Может заменить встроенный
*
* @param string $driverClass
*/
public static function addDriver(string $driverClass)
{
if (!\in_array($driverClass, static::$customDrivers, true)){
static::$customDrivers[] = $driverClass;
}
}
/**
* @return array
*/
public static function getDrivers(): array
{
return array_values(array_unique(array_merge([static::$customDrivers, \Defa\BxDal\Config::get('drivers')])));
}
}
\ No newline at end of file
<?php
namespace Defa\BxDal\Drivers;
use Defa\BxDal\BaseDriver;
class HLBlock extends BaseDriver
{
}
\ No newline at end of file
<?php
namespace Defa\BxDal\Drivers\IBlock;
use Bitrix\Main\Loader;
use Defa\BxDal\BaseDriver;
use Defa\BxDal\Interfaces\IDataDriver;
use Defa\BxDal\Interfaces\IValueObject;
class IBlockElement extends BaseDriver
{
/**
* @var int $iblockID идентификатор инфоблока
*/
private $iblockID;
/**
* @var array $filter Фильтр
*/
private $filter = [];
/**
* @var array $order сортировка
*/
private $order = [];
/**
* @var int|null $offset Сдвиг
*/
private $offset;
/**
* @var int|null $limit лимит
*/
private $limit;
/**
* @var array $select Выборка
*/
private $select = ['IBLOCK_ID', 'ID', 'NAME'];
/**
* IBlockElement constructor.
* @param int $iblockID
*/
public function __construct(int $iblockID)
{
if (Loader::includeModule('iblock')) {
if ($iblockID > 0) {
$this->iblockID = $iblockID;
} else {
throw new \InvalidArgumentException('Передан некорректный идентификатор инфоблока');
}
} else {
throw new \RuntimeException('Не удалось загрузить модуль: iblock');
}
}
/**
* Возвращает не обработанные данные (аналог Fetch)
*
* @param bool $sanitize
* @return IValueObject[]
*/
public function fetch(bool $sanitize = false): array
{
$args = [
$this->order,
$this->filter,
false,
$this->offset !== null || $this->limit !== null ? : false,
$this->select
];
$res = \CIBlockElement::GetList(...$args);
}
/**
* Возвращает количество элементов по выбранным параметрам
*
* @return int
*/
public function count(): int
{
}
/**
* Позволяет модифицировать выборку с помощью указанния нужных полей. Должен вызываться перед fetch
*
* @param IValueObject $select
* @return IDataDriver
*/
public function bySelect(IValueObject $select): IDataDriver
{
return $this;
}
/**
* Позволяет модифицировать выборку с помощью фильтрации. Должен вызываться перед fetch
*
* @param array $filter
* @return IDataDriver
*/
public function byFilter(array $filter): IDataDriver
{
return $this;
}
/**
* Позволяет модифицировать выборку с помощью сортировки. Должен вызываться перед fetch
*
* @param array $order