Как подружить ORM D7 1С-Битрикс и Sphinx или новый пакет olegpro/bitrix-sphinx
Написал пакет olegpro/bitrix-sphinx для composer, который содержит набор классов для работы с выборками из индексов sphinx через ORM D7 в привычном виде.
В комплекте пока 4 класса:
- \Olegpro\BitrixSphinx\DB\SphinxConnection - класс для указания в /bitrix/.settings.php в качестве подключения к sphinx (наследуется от Bitrix\Main\DB\MysqliConnection)
- \Olegpro\BitrixSphinx\DB\SphinxSqlHelper - класс-хелпер (наследуется от Bitrix\Main\DB\MysqliSqlHelper)
- \Olegpro\BitrixSphinx\Entity\SphinxDataManager - класс для работы с объектами данных (наследуется от Bitrix\Main\Entity\DataManager)
- \Olegpro\BitrixSphinx\Entity\SphinxQuery - класс запросов (наследуется от Bitrix\Main\Entity\Query)
Установка
composer require olegpro/bitrix-sphinx
Настройка подключения к sphinx
В файле /bitrix/.settings.php секцию connections нужно изменить на что-то вроде:
'connections' => array( 'value' => array( 'default' => array( // ... ) ), 'sphinx' => array( 'className' => '\\Olegpro\\BitrixSphinx\\DB\\SphinxConnection', 'host' => '127.0.0.1:9306', 'database' => '', 'login' => '', 'password' => '', 'options' => 1, ), ), 'readonly' => true, ),
Описываем класс-сущность
На примере индекса sample_index.
use Bitrix\Main; use Bitrix\Main\Localization\Loc; use Olegpro\BitrixSphinx\Entity\SphinxDataManager; use Olegpro\BitrixSphinx\Entity\SphinxQuery; Loc::loadMessages(__FILE__); class SampleTable extends SphinxDataManager { /** * Returns index sphinx name for entity. * * @return string */ public static function getTableName() { return 'sample_index'; } /** * Returns sphinx-connection name for entity * * @return string */ public static function getConnectionName() { return 'sphinx'; } /** * Creates and returns the Query object for the entity * * @return SphinxQuery */ public static function query() { return new SphinxQuery(static::getEntity()); } /** * Returns entity map definition. * * @return array */ public static function getMap() { return [ new Main\Entity\IntegerField('id', [ 'primary' => true, ]), new Main\Entity\StringField('name'), new Main\Entity\BooleanField('available', [ 'values' => [0, 1], ]) ]; } }
Как вариант, можно в методе getMap получать все поля из информации о индексе в формировать массив полей на лету. Получится что-то вроде:
use Bitrix\Main; use Bitrix\Main\Localization\Loc; use Olegpro\BitrixSphinx\Entity\SphinxDataManager; use Olegpro\BitrixSphinx\Entity\SphinxQuery; Loc::loadMessages(__FILE__); class SampleTable extends SphinxDataManager { /** * Returns index sphinx name for entity. * * @return string */ public static function getTableName() { return 'sample_index'; } /** * Returns sphinx-connection name for entity * * @return string */ public static function getConnectionName() { return 'sphinx'; } /** * Creates and returns the Query object for the entity * * @return SphinxQuery */ public static function query() { return new SphinxQuery(static::getEntity()); } /** * Returns entity map definition. * * @return array */ public static function getMap() { $fields = []; $connection = Main\Application::getConnection(self::getConnectionName()); $result = $connection->query( sprintf( 'DESCRIBE %s', $connection->getSqlHelper()->forSql(self::getTableName()) ) )->fetchAll(); foreach ($result as $item) { $name = $item['Field']; switch ($item['Type']){ case 'int': case 'uint': case 'bigint': case 'mva': $fields[] = new Main\Entity\IntegerField($name, [ 'primary' => ($name === 'id'), ]); break; case 'bool': $fields[] = new Main\Entity\BooleanField($name, [ 'values' => [0, 1], ]); break; case 'string': $fields[] = new Main\Entity\StringField($name, [ ]); break; default: break; } } return $fields; } }
Примеры выборок
1) Ищем по слову «книга», добавляем фильтр по атрибуту available, сортируем по весу, ограничиваем выборку лимитов в 10 записей.
use Bitrix\Main\Application; use Bitrix\Main\Entity\ExpressionFieldd; require($_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php'); Application::getConnection(SampleTable::getConnectionName())->startTracker(true); $iterator = SampleTable::getList([ 'select' => [ '*', new ExpressionField('weight', 'WEIGHT()', 'id'), ], 'match' => 'книга', 'filter' => [ '=available' => 1, ], 'limit' => 10, 'order' => [ 'weight' => 'DESC', ], 'option' => [ 'max_matches' => 50000, ], ]); echo '<pre>';print_r($iterator->getTrackerQuery()->getSql());echo '</pre>'; echo '<pre>';print_r($iterator->fetchAll());echo '</pre>';
2) Тоже самое, но с постраничной навигацией
use Bitrix\Main\Application; use Bitrix\Main\Entity\ExpressionFieldd; use Bitrix\Main\UI\PageNavigation; require($_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php'); Application::getConnection(SampleTable::getConnectionName())->startTracker(true); $nav = new PageNavigation('s'); $nav->allowAllRecords(false) ->setPageSize(10) ->initFromUri(); $iterator = SampleTable::getList([ 'select' => [ '*', new ExpressionField('weight', 'WEIGHT()', 'id'), ], 'match' => 'книга', 'filter' => [ '=available' => 1, ], 'count_total' => true, 'offset' => $nav->getOffset(), 'limit' => $nav->getLimit(), 'order' => [ 'weight' => 'DESC', ], 'option' => [ 'max_matches' => 50000, ], ]); $nav->setRecordCount($iterator->getCount()); echo '<pre>';print_r($iterator->getTrackerQuery()->getSql());echo '</pre>'; echo '<pre>';print_r($iterator->fetchAll());echo '</pre>'; $APPLICATION->IncludeComponent( "bitrix:main.pagenavigation", "", array( "NAV_OBJECT" => $nav, "SEF_MODE" => "N", ), false );
Пакет olegpro/bitrix-sphinx:
1 комментарий
Есть пример как работать с сущьностью Bitrix\Iblock\ElementTable?