Skip to content
This repository was archived by the owner on Jan 10, 2023. It is now read-only.
Closed
14 changes: 13 additions & 1 deletion src/Adapters/D7Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

namespace Arrilot\BitrixModels\Adapters;

use Bitrix\Main\Entity\ExpressionField;

/**
* Class D7Adapter
*
* @method \Bitrix\Main\DB\Result getList(array $parameters = [])
* @method int getCount(array $filter = [])
* @method \Bitrix\Main\Entity\UpdateResult update(int $id, array $fields)
* @method \Bitrix\Main\Entity\DeleteResult delete(int $id)
* @method \Bitrix\Main\Entity\AddResult add(array $fields)
Expand Down Expand Up @@ -53,4 +54,15 @@ public function __call($method, $parameters)

return $className::$method(...$parameters);
}

public function getCount($filter)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не хочу добавлять костыли для старых версий Битрикса.
Если кому-то это нужно - могут добавить это в базовую модель своего приложения

{
$version = explode('.', SM_VERSION);
if ($version[0] <= 15) {
$result = call_user_func_array([$this->className, 'query'], [])->addSelect(new ExpressionField('CNT', 'COUNT(1)'))->setFilter($filter)->exec()->fetch();
return $result['CNT'];
} else {
return $this->__call('getCount', $filter);
}
}
}
82 changes: 82 additions & 0 deletions src/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Arrilot\BitrixModels;

use Arrilot\BitrixModels\Models\BaseBitrixModel;
use Illuminate\Support\Collection;

class Helpers
{
/**
Expand All @@ -15,4 +18,83 @@ public static function startsWith($haystack, $needle)
{
return strncmp($haystack, $needle, strlen($needle)) === 0;
}

/**
* @param Collection|BaseBitrixModel[] $primaryModels первичные модели
* @param Collection|BaseBitrixModel[] $relationModels подгруженные связанные модели
* @param string $primaryKey ключ связи в первичной модели
* @param string $relationKey ключ связи в связанной модели
* @param string $relationName название связи в первичной модели
* @param bool $multiple множественная ли это свзязь
*/
public static function assocModels($primaryModels, $relationModels, $primaryKey, $relationKey, $relationName, $multiple)
{
$buckets = static::buildBuckets($relationModels, $relationKey, $multiple);

foreach ($primaryModels as $i => $primaryModel) {
if ($multiple && is_array($keys = $primaryModel[$primaryKey])) {
$value = [];
foreach ($keys as $key) {
$key = static::normalizeModelKey($key);
if (isset($buckets[$key])) {
$value = array_merge($value, $buckets[$key]);
}
}
} else {
$key = static::normalizeModelKey($primaryModel[$primaryKey]);
$value = isset($buckets[$key]) ? $buckets[$key] : ($multiple ? [] : null);
}

$primaryModel->populateRelation($relationName, is_array($value) ? (new Collection($value))->keyBy(function ($item) {return $item->id;}) : $value);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function ($item) {return $item->id;} можно заменить на 'id'

}
}

/**
* Сгруппировать найденные модели
* @param array $models
* @param string $linkKey
* @param bool $multiple
* @return array
*/
protected static function buildBuckets($models, $linkKey, $multiple)
{
$buckets = [];

foreach ($models as $model) {
$key = $model[$linkKey];
if (is_scalar($key)) {
$buckets[$key][] = $model;
} elseif (is_array($key)){
foreach ($key as $k) {
$k = static::normalizeModelKey($k);
$buckets[$k][] = $model;
}
} else {
$key = static::normalizeModelKey($key);
$buckets[$key][] = $model;
}
}

if (!$multiple) {
foreach ($buckets as $i => $bucket) {
$buckets[$i] = reset($bucket);
}
}

return $buckets;
}

/**
* @param mixed $value raw key value.
* @return string normalized key value.
*/
protected static function normalizeModelKey($value)
{
if (is_object($value) && method_exists($value, '__toString')) {
// ensure matching to special objects, which are convertable to string, for cross-DBMS relations, for example: `|MongoId`
$value = $value->__toString();
}

return $value;
}
}
3 changes: 3 additions & 0 deletions src/Models/BaseBitrixModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ abstract class BaseBitrixModel extends ArrayableModel
* @var array
*/
protected $fieldsSelectedForSave = [];

/** @var array Поля, хранящиеся в сериализованном виде */
public static $serializedFields = [];

/**
* Array of errors that are passed to model events.
Expand Down
36 changes: 34 additions & 2 deletions src/Queries/BaseQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Arrilot\BitrixModels\Queries;

use Arrilot\BitrixModels\Models\BaseBitrixModel;
use Arrilot\BitrixModels\ServiceProvider;
use BadMethodCallException;
use Bitrix\Main\Data\Cache;
use Closure;
Expand All @@ -15,7 +16,9 @@
abstract class BaseQuery
{
use BaseRelationQuery;


public static $cacheDir = '/bitrix-models';

/**
* Query select.
*
Expand Down Expand Up @@ -518,7 +521,7 @@ protected function rememberInCache($key, $minutes, Closure $callback)
}

$cache = Cache::createInstance();
if ($cache->initCache($minutes * 60, $key, '/bitrix-models')) {
if ($cache->initCache($minutes * 60, $key, static::$cacheDir)) {
$vars = $cache->getVars();
return !empty($vars['isCollection']) ? new Collection($vars['cache']) : $vars['cache'];
}
Expand Down Expand Up @@ -577,4 +580,33 @@ protected function prepareMultiFilter(&$key, &$value)
{

}

/**
* Проверка включен ли тегированный кеш
* @return bool
*/
protected function isManagedCacheOn()
{
$config = ServiceProvider::configProvider();
return $config::GetOptionString('main', 'component_managed_cache_on', 'N') == 'Y';
}

public function exec($query)
Copy link
Owner

@arrilot arrilot Nov 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тоже не хочу это мержить.
Через этот метод можно сделать какой угодно прямо запрос к базе.
Формат ответа меня тоже смущает, в моделях мы оперируем не только коллекциями, но и единичными объектами, числами (count) и т д.

У моделей и так слишком много ответственностей, это уже явный перебор.
Если нужно сделать суперэффективный прямой запрос к БД минуя то как модели (а по-сути Битрикс под ними) строит запрос можно ну взять и написать этот прямой запрос в БД вне моделей или сделать в конкретной модели FooModel метод getBarCollection(), внутри которого делать уже всё что душе угодно.

{
$queryType = 'BaseQuery::exec';

$callback = function () use ($query) {
$rows = [];
$result = \Bitrix\Main\Application::getConnection()->query($query);
$modelName = $this->modelName;
$result->setSerializedFields($modelName::$serializedFields ?: []);
while ($row = $result->fetch()) {
$this->addItemToResultsUsingKeyBy($rows, new $this->modelName($row['ID'], $row));
}

return new Collection($rows);
};

return $this->handleCacheIfNeeded(compact('queryType', 'query'), $callback);
}
}
82 changes: 5 additions & 77 deletions src/Queries/BaseRelationQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace Arrilot\BitrixModels\Queries;


use Arrilot\BitrixModels\Helpers;
use Arrilot\BitrixModels\Models\BaseBitrixModel;
use Illuminate\Support\Collection;

Expand Down Expand Up @@ -107,6 +108,7 @@ protected function filterByModels($models)
}
}

$values = array_filter($values);
if (empty($values)) {
$this->stopQuery();
}
Expand Down Expand Up @@ -134,7 +136,7 @@ protected function filterByModels($models)
public function findWith($with, &$models)
{
// --- получаем модель, на основании которой будем брать запросы релейшенов
$primaryModel = reset($models);
$primaryModel = $models->first();
if (!$primaryModel instanceof BaseBitrixModel) {
$primaryModel = $this->model;
}
Expand Down Expand Up @@ -195,83 +197,9 @@ public function populateRelation($name, &$primaryModels)
$this->filterByModels($primaryModels);

$models = $this->getList();
$buckets = $this->buildBuckets($models, $this->localKey);

foreach ($primaryModels as $i => $primaryModel) {
if ($this->multiple && is_array($keys = $primaryModel[$this->foreignKey])) {
$value = [];
foreach ($keys as $key) {
$key = $this->normalizeModelKey($key);
if (isset($buckets[$key])) {
$value = array_merge($value, $buckets[$key]);
}
}
} else {
$key = $this->getModelKey($primaryModel, $this->foreignKey);
$value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null);
}

$primaryModel->populateRelation($name, is_array($value) ? (new Collection($value))->keyBy(function ($item) {return $item->id;}) : $value);
}

Helpers::assocModels($primaryModels, $models, $this->foreignKey, $this->localKey, $name, $this->multiple);

return $models;
}

/**
* Сгруппировать найденные модели
* @param array $models
* @param array|string $linkKeys
* @param bool $checkMultiple
* @return array
*/
private function buildBuckets($models, $linkKeys, $checkMultiple = true)
{
$buckets = [];

foreach ($models as $model) {
$key = $this->getModelKey($model, $linkKeys);
$buckets[$key][] = $model;
}

if ($checkMultiple && !$this->multiple) {
foreach ($buckets as $i => $bucket) {
$buckets[$i] = reset($bucket);
}
}

return $buckets;
}

/**
* Получить значение атрибутов в виде строки
* @param BaseBitrixModel $model
* @param array|string $attributes
* @return string
*/
private function getModelKey($model, $attributes)
{
$key = [];
foreach ((array)$attributes as $attribute) {
$key[] = $this->normalizeModelKey($model[$attribute]);
}
if (count($key) > 1) {
return serialize($key);
}
$key = reset($key);
return is_scalar($key) ? $key : serialize($key);
}

/**
* @param mixed $value raw key value.
* @return string normalized key value.
*/
private function normalizeModelKey($value)
{
if (is_object($value) && method_exists($value, '__toString')) {
// ensure matching to special objects, which are convertable to string, for cross-DBMS relations, for example: `|MongoId`
$value = $value->__toString();
}

return $value;
}
}
Loading