Commit 7ab5678a authored by Telman Mazhlumov's avatar Telman Mazhlumov

#dev iblock driver

parent 15853864
......@@ -64,16 +64,4 @@ abstract class Driver implements IDataDriver
{
return (string)$this->lastAddedUniqueID;
}
/**
* @param array $attributes
* @return ValueObject
*/
abstract protected function convertToVO(array $attributes): ValueObject;
/**
* @param ValueObject $vo
* @return array
*/
abstract protected function convertFromVO(ValueObject $vo): array;
}
\ No newline at end of file
......@@ -9,17 +9,6 @@ namespace Defa\BxDal\Base\Traits;
*/
trait HasFields
{
/**
* @var array Список полей
*/
protected $fields = [];
/**
* @var array $requiredFields обязательные поля
*/
protected $requiredFields = [];
/**
* @var array $hiddenFields Поля которые скрываются при вызове методов toArray|toJson
*/
......@@ -35,6 +24,21 @@ trait HasFields
*/
protected $fieldCasts = [];
/**
* Вернёт список полей, которые разрешены для показа "наружу"
*
* @param array $fields
* @return array
*/
protected function filterAllowFields(array $fields): array
{
return array_filter($fields, function ($key) {
return !\in_array($key, $this->hiddenFields, true) && \in_array($key, $this->visibleFields, true);
}, ARRAY_FILTER_USE_KEY);
}
/**
* @param $attributeKey
* @param $attributeValue
......
......@@ -9,21 +9,6 @@ namespace Defa\BxDal\Base\Traits;
*/
trait HasProperties
{
/**
* @var null|string Префикс при генерации свойств (заменяется "property_" или "uf_")
*/
protected $propPrefix = '_';
/**
* @var null|string Чем заменяется внешний префикс для свойств
*/
protected $innerPrefix = 'property_';
/**
* @var array Список свойств
*/
protected $properties = [];
/**
* @var array $hiddenProps Свойства которые скрываются при вызове методов toArray|toJson
*/
......@@ -39,6 +24,20 @@ trait HasProperties
*/
protected $propCasts = [];
/**
* Вернёт список свойств, которые разрешены для показа "наружу"
*
* @param array $props
* @return array
*/
protected function filterAllowProps(array $props): array
{
return array_filter($props, function ($key) {
return !\in_array($key, $this->hiddenProps, true) && \in_array($key, $this->visibleProps, true);
}, ARRAY_FILTER_USE_KEY);
}
/**
* @param $attributeKey
* @param $attributeValue
......
......@@ -20,6 +20,11 @@ abstract class IBlockElement extends Driver
*/
protected $iblockID;
/**
* @var CIBlockElement $iblockObj
*/
protected $iblockObj;
/**
* @var array $filter Фильтр
*/
......@@ -54,6 +59,8 @@ abstract class IBlockElement extends Driver
if (Loader::includeModule('iblock')) {
if ($iblockID > 0) {
$this->iblockID = $iblockID;
$this->iblockObj = new CIBlockElement;
$this->filter = ['IBLOCK_ID' => $this->iblockID];
} else {
throw new \InvalidArgumentException('Not valid iblock identifier');
}
......@@ -71,7 +78,7 @@ abstract class IBlockElement extends Driver
{
$args = [
$this->order ?? [],
$this->filter ?? [],
$this->filter,
false,
$this->offset !== null || $this->limit !== null ? : false,
$this->select ?? []
......@@ -79,6 +86,7 @@ abstract class IBlockElement extends Driver
$res = \CIBlockElement::GetList(...$args);
$res
}
......@@ -95,11 +103,20 @@ abstract class IBlockElement extends Driver
/**
* Позволяет модифицировать выборку с помощью указанния нужных полей. Должен вызываться перед fetch
*
* @param IValueObject $select
* @param array $select
* @return IDataDriver
*/
public function bySelect(IValueObject $select): IDataDriver
public function bySelect(array $select): IDataDriver
{
$preparedSelect = recursiveArrayModify($select, function ($key, $val) {
return [
Mapper::convertAttributeToBx($key),
$val
];
});
$this->select = array_merge($this->select, $preparedSelect);
return $this;
}
......@@ -111,7 +128,14 @@ abstract class IBlockElement extends Driver
*/
public function byFilter(array $filter): IDataDriver
{
$preparedFilter = recursiveArrayModify($filter, function ($key, $val) {
return [
Mapper::convertAttributeToBx($key),
$val
];
});
$this->filter = array_merge($this->filter, $preparedFilter);
return $this;
}
......@@ -124,6 +148,14 @@ abstract class IBlockElement extends Driver
*/
public function byOrder(array $order): IDataDriver
{
$preparedOrder = recursiveArrayModify($order, function ($key, $val) {
return [
Mapper::convertAttributeToBx($key),
$val
];
});
$this->order = array_merge($this->order, $preparedOrder);
return $this;
}
......@@ -136,8 +168,7 @@ abstract class IBlockElement extends Driver
*/
public function byOffset(int $offset): IDataDriver
{
$this->offset = $offset >= 0 ? $offset : 0;
return $this;
}
......@@ -149,8 +180,7 @@ abstract class IBlockElement extends Driver
*/
public function byLimit(int $limit): IDataDriver
{
$this->limit = $limit >= 1 ? $limit : 1;
return $this;
}
......@@ -161,7 +191,36 @@ abstract class IBlockElement extends Driver
*/
public function add(IValueObject $element)
{
// TODO: Implement add() method.
$convertedAttrs = $element->convertToArray();
$fields = [];
$props = [];
foreach ($convertedAttrs as $attrCode => $attrVal) {
if (strtolower($attrCode) === 'props') {
$props = $attrVal;
} else {
$fields[$attrCode] = $attrVal;
}
}
if (!empty($fields)) {
$preparedFields = Mapper::convertAttributeValuesToBx($fields);
if (empty($preparedFields['IBLOCK_ID'])) {
$preparedFields['IBLOCK_ID'] = $this->iblockID;
}
$elId = $this->iblockObj->Add($preparedFields);
if ($elId > 0) {
$this->lastAddedUniqueID = $elId;
if (!empty($props)) {
$preparedProps = Mapper::convertAttributeValuesToBx($props);
CIBlockElement::SetPropertyValuesEx($elId, $preparedFields['IBLOCK_ID'], $preparedProps, ['NewElement' => true]);
}
} else {
throw new \RuntimeException($this->iblockObj->LAST_ERROR);
}
}
}
/**
......@@ -171,18 +230,47 @@ abstract class IBlockElement extends Driver
*/
public function delete(string $uniqueID)
{
// TODO: Implement delete() method.
if (!CIBlockElement::Delete($uniqueID)) {
throw new \RuntimeException("Не удалось удалить товар с ID: {$uniqueID}");
}
}
/**
* @param string $uniqueID
* @param int $uniqueID
* @param IValueObject $element
* @return void
* @throws \RuntimeException
*/
public function update(string $uniqueID, IValueObject $element)
public function update(int $uniqueID, IValueObject $element)
{
// TODO: Implement update() method.
if ($uniqueID > 0) {
$convertedAttrs = $element->convertToArray();
$fields = [];
$props = [];
foreach ($convertedAttrs as $attrCode => $attrVal) {
if (strtolower($attrCode) === 'props') {
$props = $attrVal;
} else {
$fields[$attrCode] = $attrVal;
}
}
if (!empty($fields)) {
$preparedFields = Mapper::convertAttributeValuesToBx($fields);
if (!$this->iblockObj->Update($uniqueID, $preparedFields)) {
throw new \RuntimeException($this->iblockObj->LAST_ERROR);
}
}
if (!empty($props)) {
$preparedProps = Mapper::convertAttributeValuesToBx($props);
CIBlockElement::SetPropertyValuesEx($uniqueID, $preparedFields['IBLOCK_ID'] ?? false, $preparedProps);
}
} else {
throw new \RuntimeException("Передан некорректный идентификатор сущности {$uniqueID}");
}
}
}
\ No newline at end of file
......@@ -50,11 +50,35 @@ class Mapper extends BaseMapper
*/
public static function convertAttributeToBx(string $attributeName): string
{
if (static::isProperty($attributeName)) {
$conditionPrefixes = ['!', '<', '>', '=', '?', '%'];
$rawAttributeName = str_replace($conditionPrefixes, '', $attributeName);
$condition = str_replace($rawAttributeName, '', $attributeName);
if (static::isProperty($attributeName)) {
$rawAttributeName = camelToSnakeCase($rawAttributeName);
$rawAttributeName = static::PROP_INNER_PREFIX . single_str_replace([static::PROP_INNER_PREFIX, static::PROP_PREFIX], '', strtoupper($rawAttributeName));
$preparedAttributeName = $condition . $rawAttributeName;
} else {
$preparedAttributeName = strtoupper(camelToSnakeCase($rawAttributeName));
}
return $preparedAttributeName;
}
/**
* @param string $attributeName
* @return string
*/
public static function convertAttributeFromBx(string $attributeName): string
{
if (static::isProperty($attributeName)) {
$attributeName = single_str_replace(static::PROP_INNER_PREFIX, '', strtoupper($attributeName));
$preparedAttributeName = static::PROP_PREFIX . toCamelCase($attributeName);
} else {
$preparedAttributeName = toCamelCase($attributeName);
}
return $preparedAttributeName;
}
/**
......@@ -67,8 +91,13 @@ class Mapper extends BaseMapper
foreach ($values as $valueCode => $value)
{
$newVal = static::isProperty($value) ? static::processProp($value) : static::processField($valueCode, $value);
$returnData = array_merge($returnData, $newVal);
$newVal = [static::convertAttributeToBx($valueCode) => $value];
if (static::isProperty($valueCode)) {
$newVal['PROPS'] = $newVal;
}
$returnData = array_merge_recursive($returnData, $newVal);
}
return $returnData;
......@@ -84,8 +113,17 @@ class Mapper extends BaseMapper
foreach ($values as $valueCode => $value)
{
$newVal = static::isProperty($value) ? static::processProp($value) : static::processField($valueCode, $value);
$returnData = array_merge($returnData, $newVal);
if (static::isProperty($value)) {
$newVal = [
'props' => [
static::convertAttributeFromBx($valueCode) => static::processProp($value)
]
];
} else {
$newVal = [ static::convertAttributeFromBx($valueCode) => static::processField($valueCode, $value)];
}
$returnData = array_merge_recursive($returnData, $newVal);
}
return $returnData;
......@@ -113,6 +151,7 @@ class Mapper extends BaseMapper
protected static function processProp($value)
{
$propTypeCode = trim(implode(':', [$value['PROPERTY_TYPE'], $value['USER_TYPE']]));
if (isset(static::PROP_TYPE_MAPPER_HANDLERS[$propTypeCode])) {
$callMethod = static::PROP_TYPE_MAPPER_HANDLERS[$propTypeCode];
return static::{$callMethod}($value);
......@@ -131,7 +170,10 @@ class Mapper extends BaseMapper
return static::PROPERTY_EXIST && \is_array($value) && isset($value['PROPERTY_TYPE']);
}
$conditionPrefix = ['!', '<', '>', '=', '?', '%'];
$value = str_replace($conditionPrefix, '', $value);
$valToUpper = strtoupper($value);
return strpos($valToUpper, static::PROP_PREFIX) === 0 || strpos($valToUpper, static::PROP_INNER_PREFIX) === 0;
}
......@@ -144,7 +186,6 @@ class Mapper extends BaseMapper
return \is_array($value) && isset($value['MULTIPLE']) && $value['MULTIPLE'] === 'Y';
}
/**
* @param $value
* @return string
......
......@@ -6,11 +6,100 @@ namespace Defa\BxDal\Drivers\IBlockElement;
use Defa\BxDal\Base\Traits\HasFields;
use Defa\BxDal\Base\Traits\HasProperties;
use \Defa\BxDal\Base\ValueObject as BaseValueObject;
use Defa\BxDal\Interfaces\IValueObject;
abstract class ValueObject extends BaseValueObject
{
use HasFields,
HasProperties;
/**
* Конвертирует текущий объект в массив с ключами и значениями нужного формата
*
* @return array
*/
public function toArray(): array
{
$returnData = $this->convertToArray();
if (trait_exists(HasFields::class)) {
$returnData = $this->filterAllowFields($returnData);
}
if (trait_exists(HasProperties::class)) {
$returnData['props'] = $this->filterAllowFields($returnData['props']);
}
return $returnData;
}
/**
* Инициализирует поля объекта из массива
*
* @param array $rawData
* @return IValueObject
*/
public function initFromArray(array $rawData): IValueObject
{
// TODO: Implement initFromArray() method.
}
/**
* Конвертирует текущий объект в массив с ключами и значениями нужного формата
* Отличется от toArray тем, что не учитывает скрытые и открытые ключи
*
* @return array
*/
public function convertToArray(): array
{
$returnData = [];
foreach ($this->publicProperties as $prop) {
$val = $this->{$prop};
if (strpos($prop, Mapper::PROP_PREFIX) === 0) {
$preparedPropName = single_str_replace('_', '', $prop);
$returnData['props'][$preparedPropName] = $val;
} else {
$returnData[$prop] = $val;
}
}
return $returnData;
}
/**
* @param $offset
* @param $value
*/
public function offsetSet($offset, $value)
{
if (strpos($offset, Mapper::PROP_PREFIX) === 0 && trait_exists(HasProperties::class)) {
$value = $this->propCast($offset, $value);
} else if (trait_exists(HasFields::class)) {
$value = $this->fieldCast($offset, $value);
}
parent::offsetSet($offset, $value);
}
/**
* @param $offset
* @return mixed
*/
public function offsetGet($offset)
{
$value = parent::offsetGet($offset);
if (strpos($offset, Mapper::PROP_PREFIX) === 0 && trait_exists(HasProperties::class)) {
$value = $this->propCast($offset, $value);
} else if (trait_exists(HasFields::class)) {
$value = $this->fieldCast($offset, $value);
}
return $value;
}
}
\ No newline at end of file
......@@ -19,10 +19,10 @@ interface IDataDriver
/**
* Позволяет модифицировать выборку с помощью указанния нужных полей. Должен вызываться перед fetch/get
*
* @param IValueObject $select
* @param array $select
* @return IDataDriver
*/
public function bySelect(IValueObject $select): IDataDriver;
public function bySelect(array $select): IDataDriver;
/**
* Позволяет модифицировать выборку с помощью фильтрации. Должен вызываться перед fetch/get
......@@ -66,12 +66,12 @@ interface IDataDriver
public function delete(string $uniqueID);
/**
* @param string $uniqueID
* @param int $uniqueID
* @param IValueObject $element
* @return void
* @throws \RuntimeException
*/
public function update(string $uniqueID, IValueObject $element);
public function update(int $uniqueID, IValueObject $element);
/**
* Возвращает последний идентификатор добавленного элемента
......
......@@ -16,13 +16,6 @@ interface IValueObject extends \ArrayAccess
*/
public function __construct(array $rawData);
/**
* Возвращает обязательные поля
*
* @return array
*/
public function getRequiredFields(): array;
/**
* Инициализирует объект из массива
*
......@@ -36,5 +29,14 @@ interface IValueObject extends \ArrayAccess
*
* @return array
*/
public function toArray(): array;
/**
* Конвертирует текущий объект в массив с ключами и значениями нужного формата
* Отличется от toArray тем, что не учитывает скрытые и открытые ключи
*
* @return array
*/
public function convertToArray(): array;
}
\ No newline at end of file
<?php
if (!function_exists('recursiveArrayModify')) {
/**
* Рекурсивно изменяет ключи и значения исходного массива
*
* @param array $array
* @param Closure $processor
* @return array
*/
function recursiveArrayModify(array $array, Closure $processor): array
{
$returnData = [];
foreach ($array as $key => $val) {
if (is_array($val)) {
$val = recursiveArrayModify($val, $processor);
}
list($key, $val) = $processor($key, $val);
$returnData[$key] = $val;
}
return $returnData;
}
}
if (!function_exists()) {
if (!function_exists('toCamelCase')) {
function ():
/**
*
*
* @param string $string
* @return string
*/
function toCamelCase(string $string): string
{
$string = ucwords(str_replace(['-', '_'], ' ', $string));
return lcfirst(str_replace(' ', '', $string));
}
}
if (!function_exists('camelToSnakeCase')) {
/**
*
*
* @param string $string
* @return string
*/
function camelToSnakeCase(string $string): string
{
if (!ctype_lower($string)) {
$string = strtolower(preg_replace('/(.)(?=[A-Z])/u', '$1'.'_', $string));
}
return $string;
}
}
if (!function_exists('single_str_replace')) {
/**
*