Appearance
Модуль новостей. Детальная страница раздела
Содержание
- Формулировка задачи
- Создание контроллера
- Модель разделов инфоблока
- Реализация основного метода контроллера
- Основной запрос в БД
- Сущность раздела \Realweb\Api\Model\Iblock\Section\Entity
- Объединение контроллеров
- Пример кода
Формулировка задачи
Согласно спецификации необходимо вывести данные раздела новостей Параметры запроса:
- Уникальный символьный код раздела
codeВывести следующие поля:
- Название
- Описание
- Детальная картинка
- Свойство типа список выбора "Тема"
- Множественное свойство типа привязка к элементам "Баннер" - вывести картинку для анонса, название и ссылку
- Ссылка на детальную страницу раздела. Ссылка на детальную страницу строится из символьных кодов родительских разделов + символьный код раздела
- Хлебные крошки
- Количество активных элементов в разделе
- Метатеги - title, h1, description, keywords
Для лучшего понимания реализации задачи рекомендуем ознакомиться с модулем вывода списка элементов ИБ
Создание контроллера
Расположим контроллер в модуле News в пространстве Section. Т.к. контролер будет выводить детальную инофрмацию о сущности, то так и назовем контроллер \Realweb\Api\Module\News\Controller\Section\EntityController
php
<?php
namespace Realweb\Api\Module\News\Controller\Section;
/**
* Class \Realweb\Api\Module\News\Controller\Section\EntityController
*/
class EntityController extends \Realweb\Api\Controller
{
public function get()
{
}
}т.к обязательным параметром вызова контроллера является символьный код, то предварительно проверим наличие этого параметра
Согласно документации перед вызовом метода get будет вызван метод checkGetParams, если он реализован. После этого, если в основном компоненте будут содержаться ошибки, метод get выполняться не будет, а ошибки выведутся в браузер. В любом контроллере объект основного компонента realweb.api доступен через вызов метода getComponent.
php
<?php
namespace Realweb\Api\Module\News\Controller\Section;
/**
* Class \Realweb\Api\Module\News\Controller\Section\EntityController
*/
class EntityController extends \Realweb\Api\Controller
{
public function checkGetParams(): void
{
$strCode = $this->getRequestParam('code','');
if(strlen($strCode) == 0){
$this->getComponent()->addErrorNoParams();
}
}
public function get()
{
}
}Модель разделов инфоблока
В пространстве Model\Section создадим модель для работы с разделом с методом получения раздела по символьному коду и получением тега для кеша
php
<?php
namespace Realweb\Api\Module\News\Model\Section;
/**
* Class \Realweb\Api\Module\News\Model\Section\Database
*/
class Database
{
public static function getByCode($strCode): array
{
}
public static function getCacheTag(): string
{
return "iblock_id_" . IBLOCK_CONTENT_NEWS;
}
}Реализация основного метода контроллера
Для реализации основного метода сразу будем использовать кеширование данных
Для передачи символьного кода в коллбек воспользуемся объектом пагинации/параметров \Realweb\Api\Model\Main\Pagination
Отметим, что помимо прочих, у объекта пагинации/параметров есть удобный метод
getParamStringкоторый приводит переменную к нужному типу
php
<?php
namespace Realweb\Api\Module\News\Controller\Section;
use Realweb\Api\Model\Main\Cache;
use Realweb\Api\Model\Main\Pagination;
use Realweb\Api\Module\News;
/**
* Class \Realweb\Api\Module\News\Controller\Section\EntityController
*/
class EntityController extends \Realweb\Api\Controller
{
public function checkGetParams(): void
{
$strCode = $this->getRequestParam('code', '');
if (strlen($strCode) == 0) {
$this->getComponent()->addErrorNoParams();
}
}
public function get()
{
$obNav = $this->getPagination();
$obNav->setParam('code', $this->getRequestParam('code', ''));
$arCacheResult = Cache::getInstance()
// Установка уникального ID кеша, объект $obNav сериализуется и хешируется
->setId($obNav)
// Установка директории хранения кеша
->setDir(__CLASS__)
// Добавление тега кеша для сброса, как только произойдут изменения в ИБ - кеш сбросится
->addTag(News\Model\Section\Database::getCacheTag())
// Вызов коллбека, возвращающий данные
->get(fn() => $this->_getEntity($obNav));
return $arCacheResult['entity'];
}
private function _getEntity(Pagination $obNav): array
{
//Получение массива с данными
if ($arSection = News\Model\Section\Database::getByCode($obNav->getParamString('code', ''))) {
return array(
'entity' => $arSection,
);
}
Cache::getInstance()->abortCache();
return array(
'entity' => null,
);
}
}Основной запрос в БД
Составим основной запрос для получения раздела по символьному коду. Подробнее о составлении запроса.
php
<?php
namespace Realweb\Api\Module\News\Model\Section;
use Bitrix\Main\DB\SqlExpression;
use Bitrix\Main\ORM\Fields\Relations\Reference;
use Bitrix\Main\ORM\Fields\ExpressionField;
/**
* Class \Realweb\Api\Module\News\Model\Section\Database
*/
class Database
{
public static function getByCode(string $strCode): ?array
{
//Подзапрос для получения количества элементов
$obSubQueryCount = \Realweb\Api\Model\Iblock\Element\Table::query()
->setSelect(array('ELEMENTS_COUNT'))
->where("IBLOCK_SECTION_ID", '=', new SqlExpression('%s'))
->where('ACTIVE', '=', 'Y')
->registerRuntimeField(
new ExpressionField('ELEMENTS_COUNT', 'COUNT(%s)', array('ID'))
)
->setTableAliasPostfix('_count');
$obQuery = \Realweb\Api\Model\Iblock\Section\Table::queryIblock(IBLOCK_CONTENT_NEWS);
$obQuery
->setSelect(array("ID", "CODE", "NAME", "DESCRIPTION", "UF_BANNER"))
->addSelect("IBLOCK.SECTION_PAGE_URL", "SECTION_PAGE_URL_TEMPLATE")
->where("ACTIVE", true)
->where("CODE", "=", $strCode)
->registerDetailPicture()
->registerSectionCodePath()
->registerSectionNamePath()
->registerSectionEnumField("UF_THEME")
->registerRuntimeField(
new ExpressionField(
"ELEMENTS_COUNT",
'(' . $obSubQueryCount->getQuery() . ')',
array('ID')
)
)
->addSelect('ELEMENTS_COUNT')
;
$rsResult = $obQuery->exec();
if ($arSection = $rsResult->fetch()) {
return $arSection;
}
return null;
}
public static function getCacheTag(): string
{
return "iblock_id_" . IBLOCK_CONTENT_NEWS;
}
}php
array(15)
[
"ID" => string(3) "118"
"CODE" => string(9) "iyul-2023"
"NAME" => string(13) "Июль 2023"
"DESCRIPTION" => string(31) "Описание раздела"
"UF_BANNER" => array(2)
[
0 => int(5041)
1 => int(5040)
]
"SECTION_PAGE_URL_TEMPLATE" => string(36) "#SITE_DIR#/news/#SECTION_CODE_PATH#/"
"DETAIL_PICTURE_SRC" => string(55) "/upload/iblock/1d9/sf3ah97d22v2rkon2bnln9iqb85ih008.png"
"SECTION_CODE_PATH" => string(4) "2023"
"SECTION_NAME_PATH" => string(4) "2023"
"UF_THEME_ID" => string(2) "10"
"UF_THEME_USER_FIELD_ID" => string(2) "45"
"UF_THEME_VALUE" => string(22) "Пресса о нас"
"UF_THEME_DEF" => string(1) "N"
"UF_THEME_SORT" => string(3) "500"
"UF_THEME_XML_ID" => string(32) "8e3b4fd44a61a744120cd0c299f9cdb2"
"ELEMENTS_COUNT" => string(1) "3"
]Как мы видим, в результирующем массиве имеются лишние данные (UF_THEME_ID, UF_THEME_USER_FIELD_ID, UF_THEME_DEF, UF_THEME_SORT, UF_THEME_XML_ID, SECTION_CODE_PATH, SECTION_NAME_PATH, SECTION_PAGE_URL_TEMPLATE) а также отсутствуют необходимые поля - ссылки на файлы баннеров, ссылка на детальную страницу, хлебные крошки.
Проведем преобразование получаемой сущности согласно философии разработки.
Сущность раздела \Realweb\Api\Model\Iblock\Section\Entity
Создадим в пространстве имен Section класс для сущности раздела и класс для коллекции таких сущностей. Используем уже подготовленные классы \Realweb\Api\Model\Iblock\Section\Entity и \Realweb\Api\Model\Iblock\Section\Collection - они обладают многими полезными методами.
php
<?php
namespace Realweb\Api\Module\News\Model\Section;
/**
* Class \Realweb\Api\Module\News\Model\Section\Entity
*/
class Entity extends \Realweb\Api\Model\Iblock\Section\Entity
{
protected static function _getDatabase(): string
{
return Database::class;
}
}php
<?php
namespace Realweb\Api\Module\News\Model\Section;
/**
* Class \Realweb\Api\Module\News\Model\Section\Collection
*/
class Collection extends \Realweb\Api\Model\Iblock\Section\Collection
{
/**
* @param string $strKey
* @return \Realweb\Api\Module\News\Model\Section\Entity|null
*/
public function getByKey($strKey): ?Entity
{
return parent::getByKey($strKey);
}
/**
* @return Entity[]
*/
public function getCollection(): array
{
return parent::getCollection();
}
}В коллекции мы объявили 2 метода getByKey и getCollection - они необходимы для того, чтобы IDE "знала" с какими сущностями мы работаем в рамках этой коллекции.
При попытке создать экземпляр класса \Realweb\Api\Module\News\Model\Section\Entity мы увидим исключение
php
$rsResult = $obQuery->exec();
if ($arSection = $rsResult->fetch()) {
$obSection = new Entity($arSection);
return $obSection;
}text
[Error]
Call to undefined method Realweb\Api\Module\News\Model\Section\Database::getOrmEntity() (0)Так происходит потому, что модель БД для раздела "не знает" с какими сущностями она работает. Добавим описание сущностей в модель и расширим ее от базового класса \Realweb\Api\Model\Iblock\Section\Database. Нам необходимо реализовать следующие обязательные методы:
getIblockId- получение ID ИБ для основного запросаgetCollection- получение класса коллекцииgetEntity- получение класса сущностиgetQuery- если требуется возвращение объекта основного запроса
Теперь модель БД не нуждается в методе getByCode - основной класс "умеет" получать объекты сущностей методом getObject
php
<?php
namespace Realweb\Api\Module\News\Model\Section;
use Bitrix\Main\DB\SqlExpression;
use Bitrix\Main\ORM\Fields\ExpressionField;
use Realweb\Api\Model\Iblock\Section\Query;
use Realweb\Api\Model\Main\Pagination;
/**
* Class \Realweb\Api\Module\News\Model\Section\Database
*/
class Database extends \Realweb\Api\Model\Iblock\Section\Database
{
public static array $arFields = array(
'ID',
'IBLOCK_ID',
'IBLOCK_SECTION_ID',
'LEFT_MARGIN',
'RIGHT_MARGIN',
'DEPTH_LEVEL',
'NAME',
'CODE',
'DESCRIPTION',
'UF_BANNER'
);
protected const QUERY_WITH_UF = true;
public static function getFields(): array
{
return static::$arFields;
}
public static function getIblockId(): int
{
return IBLOCK_CONTENT_NEWS;
}
/**
* @return Collection|string
*/
public static function getCollection(): string
{
return Collection::class;
}
/**
* @return Entity|string
*/
public static function getEntity(): string
{
return Entity::class;
}
public static function getObject(?Pagination $obNav = null): Entity
{
return parent::getObject($obNav);
}
public static function getQuery(?Pagination $obNav = null): Query
{
$obQuery = parent::getQuery($obNav)
->addSelect('IBLOCK.SECTION_PAGE_URL', 'SECTION_PAGE_URL_TEMPLATE')
->where("ACTIVE", true)
->registerDetailPicture()
->registerSectionCodePath()
->registerSectionEnumField("UF_THEME")
;
$obSubQueryCount = \Realweb\Api\Model\Iblock\Element\Table::query()
->setSelect(array('ELEMENTS_COUNT'))
->where("IBLOCK_SECTION_ID", '=', new SqlExpression('%s'))
->where('ACTIVE', '=', 'Y')
->registerRuntimeField(
new ExpressionField('ELEMENTS_COUNT', 'COUNT(%s)', array('ID'))
)
->setTableAliasPostfix('_count')
;
$obQuery
->registerRuntimeField(
new ExpressionField(
"ELEMENTS_COUNT",
'(' . $obSubQueryCount->getQuery() . ')',
array('ID')
)
)
->addSelect('ELEMENTS_COUNT')
;
return $obQuery;
}
}Условие ->where("CODE", "=", $strCode) писать не требуется, оно уже есть в базовом классе \Realweb\Api\Model\Iblock\Section\Database. Такэе обратим внимание и на константу QUERY_WITH_UF - по умолчанию она false и пользовательские поля не выбираются.
Теперь код контроллера будет выглядеть следующим образом
php
<?php
namespace Realweb\Api\Module\News\Controller\Section;
use Realweb\Api\Model\Main\Cache;
use Realweb\Api\Model\Main\Pagination;
use Realweb\Api\Module\News;
/**
* Class \Realweb\Api\Module\News\Controller\Section\EntityController
*/
class EntityController extends \Realweb\Api\Controller
{
public function checkGetParams(): void
{
$strCode = $this->getRequestParam('code', '');
if (strlen($strCode) == 0) {
$this->getComponent()->addErrorNoParams();
}
}
public function get(): ?array
{
$obNav = $this->getPagination();
$obNav->setParam('code', $this->getRequestParam('code', ''));
$arCacheResult = Cache::getInstance()
// Установка уникального ID кеша, объект $obNav сериализуется и хешируется
->setId($obNav)
// Установка директории хранения кеша
->setDir(__CLASS__)
// Добавление тега кеша для сброса, как только произойдут изменения в ИБ - кеш сбросится
->addTag(News\Model\Section\Database::getCacheTag())
// Вызов коллбека, возвращающий данные
->get(fn () => $this->_getEntity($obNav))
;
/** @var News\Model\Section\Entity $obSection */
if ($obSection = $arCacheResult['entity']) {
return $obSection->toJson();
}
return null;
}
private function _getEntity(Pagination $obNav): array
{
// Получение сущности раздела
$obSection = News\Model\Section\Database::getObject($obNav);
if ($obSection->isExist()) {
return array(
'entity' => $obSection,
);
}
Cache::getInstance()->abortCache();
return array(
'entity' => null,
);
}
}Можно заметить, что метод getObject всегда возвращает объект раздела, даже если он не найден в БД. Так сложилось исторически и по аналогии с методом getObjectCollection. По этой причине стоит проверка существования раздела isExist.
Реализуем метод toJson сущности раздела
php
<?php
namespace Realweb\Api\Module\News\Model\Section;
/**
* Class \Realweb\Api\Module\News\Model\Section\Entity
*
* @method string getDescription()
* @method string getUfThemeValue()
* @method int getElementsCount()
*/
class Entity extends \Realweb\Api\Model\Iblock\Section\Entity
{
public function toJson(): array
{
return array(
'id' => $this->getId(),
'name' => $this->getName(),
'description' => (string)$this->getDescription(),
'image' => $this->toJsonDetailPicture(),
'theme' => (string)$this->getUfThemeValue(),
'link' => $this->getUrl(),
'elementsCount' => $this->getElementsCount(),
);
}
protected static function _getDatabase(): string
{
return Database::class;
}
}Для доступа к некоторым нестандартным полям объявили магические методы в аннотации. Подробнее про магические методы.
json
{
"id": 118,
"name": "Июль 2023",
"description": "Описание раздела",
"image": {
"src": "/upload/iblock/1d9/sf3ah97d22v2rkon2bnln9iqb85ih008.png",
"alt": "Июль 2023"
},
"theme": "Пресса о нас",
"link": "/news/2023/iyul-2023/",
"elementsCount": "3"
}Все еще не хватает хлебных крошек и баннеров.
Получение хлебных крошек
У класса \Realweb\Api\Model\Iblock\Section\Entity есть встроенные методы:
processChain- построение хлебных крошекgetChain- получение коллекции родительских разделов, без предварительного вызоваprocessChainвернет пустую коллекцию
"Посчитаем" хлебные крошки и выведем их в итоговый json.
Для получения всего "пути" хлебных крошек также понадобится и объект Инфоблока. Получим его с помощью встроенного в ядро модуля Iblock
php
<?php
namespace Realweb\Api\Module\News\Controller\Section;
use Realweb\Api\Model\Main\Cache;
use Realweb\Api\Model\Main\Pagination;
use Realweb\Api\Module\News;
/**
* Class \Realweb\Api\Module\News\Controller\Section\EntityController
*/
class EntityController extends \Realweb\Api\Controller
{
public function checkGetParams(): void
{
...
}
public function get(): ?array
{
...
}
private function _getEntity(Pagination $obNav): array
{
// Получение сущности раздела
$obSection = News\Model\Section\Database::getObject($obNav);
if ($obSection->isExist()) {
//Считаем хлебные крошки
$obSection->processChain();
return array(
'entity' => $obSection,
);
}
Cache::getInstance()->abortCache();
return array(
'entity' => null,
);
}
}php
<?php
namespace Realweb\Api\Module\News\Model\Section;
use Realweb\Api\Module\Iblock;
/**
* Class \Realweb\Api\Module\News\Model\Section\Entity
*
* @method string getDescription()
* @method string getUfThemeValue()
* @method int getElementsCount()
*/
class Entity extends \Realweb\Api\Model\Iblock\Section\Entity
{
public function toJson(): array
{
return array(
'id' => $this->getId(),
'name' => $this->getName(),
'description' => (string)$this->getDescription(),
'image' => $this->toJsonDetailPicture(),
'theme' => (string)$this->getUfThemeValue(),
'link' => $this->getUrl(),
'elementsCount' => $this->getElementsCount(),
'breadcrumbs' => $this->toJsonBreadcrumbs(),
);
}
public function toJsonBreadcrumbs(): array
{
$arResult = array();
$arResult[] = array(
'name' => 'Главная',
'link' => SITE_DIR,
);
// Получение объекта инфоблока для разводящей страницы
if ($obIblock = (new Iblock\Controller\IndexController())->setParam('id', $this->getIblockId())->getEntity()) {
$arResult[] = array(
'name' => $obIblock->getName(),
'link' => $obIblock->getUrl(),
);
}
// Цепочка родительских разделов
foreach ($this->getChain()->getCollection() as $obSection) {
$arResult[] = array(
'name' => $obSection->getName(),
'link' => $obSection->getUrl(),
);
}
// Сам раздел
$arResult[] = array(
'name' => $this->getName(),
'link' => $this->getUrl(),
);
return $arResult;
}
protected static function _getDatabase(): string
{
return Database::class;
}
}json
{
"id": 118,
"name": "Июль 2023",
"description": "Описание раздела",
"image": {
"src": "/upload/iblock/1d9/sf3ah97d22v2rkon2bnln9iqb85ih008.png",
"alt": "Июль 2023"
},
"theme": "Пресса о нас",
"link": "/news/2023/iyul-2023/",
"elementsCount": "3",
"breadcrumbs": [
{
"name": "Главная",
"link": "/"
},
{
"name": "Новости",
"link": "/news/"
},
{
"name": "2023",
"link": "/news/2023/"
},
{
"name": "Июль 2023",
"link": "/news/2023/iyul-2023/"
}
]
}Получение множественных свойств типа "Привязка к элементу"
Для получения коллекции привязанных элементов необходимо предварительно реализовать модуль баннеров. Пример реализации такого модуля
Пусть такой модуль реализован в пространстве Catalog\Banner
php
<?php
namespace Realweb\Api\Module\News\Model\Section;
use Realweb\Api\Module\Iblock;
use Realweb\Api\Module\Catalog;
/**
* Class \Realweb\Api\Module\News\Model\Section\Entity
*
* @method string getDescription()
* @method string getUfThemeValue()
* @method int getElementsCount()
* @method array getUfBanner()
*/
class Entity extends \Realweb\Api\Model\Iblock\Section\Entity
{
private ?Catalog\Model\Banner\Collection $_banners = null;
public function toJson(): array
{
return array(
'id' => $this->getId(),
'name' => $this->getName(),
'description' => (string)$this->getDescription(),
'image' => $this->toJsonDetailPicture(),
'theme' => (string)$this->getUfThemeValue(),
'link' => $this->getUrl(),
'elementsCount' => $this->getElementsCount(),
'breadcrumbs' => $this->toJsonBreadcrumbs(),
'banners' => $this->getBanners()->toJson(),
);
}
public function toJsonBreadcrumbs(): array
{
$arResult = array();
$arResult[] = array(
'name' => 'Главная',
'link' => SITE_DIR,
);
// Получение объекта инфоблока для разводящей страницы
if ($obIblock = $this->getIblock()) {
$arResult[] = array(
'name' => $obIblock->getName(),
'link' => $obIblock->getUrl(),
);
}
// Цепочка родительских разделов
foreach ($this->getChain()->getCollection() as $obSection) {
$arResult[] = array(
'name' => $obSection->getName(),
'link' => $obSection->getUrl(),
);
}
// Сам раздел
$arResult[] = array(
'name' => $this->getName(),
'link' => $this->getUrl(),
);
return $arResult;
}
public function getBanners(): Catalog\Model\Banner\Collection
{
if ($this->_banners === null) {
$arBannerIds = $this->getUfBanner();
if (is_array($arBannerIds) && count($arBannerIds) > 0) {
$this->_banners = (new Catalog\Controller\Backend\Banner\CollectionController())
->setParam('ids', $arBannerIds)
->getCollection();
} else {
$this->_banners = new Catalog\Model\Banner\Collection();
}
}
return $this->_banners;
}
protected static function _getDatabase(): string
{
return Database::class;
}
}json
{
"id": 118,
"name": "Июль 2023",
"description": "Описание раздела",
"image": {
"src": "/upload/iblock/1d9/sf3ah97d22v2rkon2bnln9iqb85ih008.png",
"alt": "Июль 2023"
},
"theme": "Пресса о нас",
"link": "/news/2023/iyul-2023/",
"elementsCount": "3",
"breadcrumbs": [
{
"name": "Главная",
"link": "/"
},
{
"name": "Новости",
"link": "/news/"
},
{
"name": "2023",
"link": "/news/2023/"
},
{
"name": "Июль 2023",
"link": "/news/2023/iyul-2023/"
}
],
"banners": [
{
"title": "Наращиваем волосы в нашей студии",
"link": {
"link": "/uslugi/narashchivanie-volos/kapsulnoe-narashchivanie/",
"label": "Узнать подробнее"
},
"image": {
"src": "/upload/iblock/15f/frvw1169vxcy8egvsj6ww5nfltj0vklh.png",
"alt": "Наращиваем волосы в нашей студии"
}
},
{
"title": "2",
"link": {
"link": "/test/",
"label": "test"
},
"image": {
"src": "/upload/iblock/33c/mbsm0j8qplp0kszwd76v80zvi6zakt9l.png",
"alt": "2"
}
}
]
}Необходимо обратить внимание, что для баннеров используется другой Контроллер с независимым кешем.
Если бы мы получали баннеры "внутри" кешируемой части получения сущности, необходимо было бы добавить его ID к общему кешу
php
<?php
namespace Realweb\Api\Module\News\Controller\Section;
use Realweb\Api\Model\Main\Cache;
use Realweb\Api\Model\Main\Pagination;
use Realweb\Api\Module\News;
use Realweb\Api\Module\Catalog;
/**
* Class \Realweb\Api\Module\News\Controller\Section\EntityController
*/
class EntityController extends \Realweb\Api\Controller
{
public function checkGetParams(): void
{
...
}
public function get(): ?array
{
$obNav = $this->getPagination();
$obNav->setParam('code', $this->getRequestParam('code', ''));
$arCacheResult = Cache::getInstance()
// Установка уникального ID кеша, объект $obNav сериализуется и хешируется
->setId($obNav)
// Установка директории хранения кеша
->setDir(__CLASS__)
// Добавление тега кеша для сброса, как только произойдут изменения в ИБ - кеш сбросится
->addTag(News\Model\Section\Database::getCacheTag())
->addTag(Catalog\Model\Banner\Database::getCacheTag())
// Вызов коллбека, возвращающий данные
->get(fn () => $this->_getEntity($obNav))
;
/** @var News\Model\Section\Entity $obSection */
if ($obSection = $arCacheResult['entity']) {
return $obSection->toJson();
}
return null;
}
private function _getEntity(Pagination $obNav): array
{
// Получение сущности раздела
$obSection = News\Model\Section\Database::getObject($obNav);
if ($obSection->isExist()) {
$obSection->processChain();
$obSection->getBanners(); //Получение баннеров внутри кеша
return array(
'entity' => $obSection,
);
}
Cache::getInstance()->abortCache();
return array(
'entity' => null,
);
}
}Получение метатегов
Для получения метатегов существует метод сущности раздела processMetaValues
php
<?php
namespace Realweb\Api\Module\News\Controller\Section;
use Realweb\Api\Model\Main\Cache;
use Realweb\Api\Model\Main\Pagination;
use Realweb\Api\Module\News;
/**
* Class \Realweb\Api\Module\News\Controller\Section\EntityController
*/
class EntityController extends \Realweb\Api\Controller
{
public function checkGetParams(): void
{
...
}
public function get(): ?array
{
...
}
private function _getEntity(Pagination $obNav): array
{
// Получение сущности раздела
$obSection = News\Model\Section\Database::getObject($obNav);
if ($obSection->isExist()) {
//Считаем хлебные крошки
$obSection->processChain();
//Получаем метатеги
$obSection->processMetaValues();
return array(
'entity' => $obSection,
);
}
Cache::getInstance()->abortCache();
return array(
'entity' => null,
);
}
}php
<?php
namespace Realweb\Api\Module\News\Model\Section;
/**
* Class \Realweb\Api\Module\News\Model\Section\Entity
*/
class Entity extends \Realweb\Api\Model\Iblock\Section\Entity
{
public function toJsonDetail(): array
{
return array(
'id' => $this->getId(),
'name' => $this->getName(),
'description' => (string)$this->getDescription(),
'image' => $this->toJsonDetailPicture(),
'theme' => (string)$this->getUfThemeValue(),
'link' => $this->getUrl(),
'elementsCount' => $this->getElementsCount(),
'breadcrumbs' => $this->toJsonBreadcrumbs(),
'banners' => $this->getBanners()->toJson(),
'meta' => $this->getMetaValues()->toJsonMeta(),
);
}
protected static function _getDatabase(): string
{
return Database::class;
}
}Внимание! Если не выполнить метод
processMetaValues-$this->getMetaValues()вернет пустую коллекцию.
Объединение контроллеров
В рамках одного проекта потребуется выводить разные данные - детальные страницы элементов и разделов разных ИБ. По сути функциональность большинства контроллеров сводится к следующему:
- Создать объект Пагинации/Фильтра и установить параметры из запроса (
IDилиCODE) - Вызвать метод получения коллекции соответствующей модели
- Опционально провести некоторые манипуляции с коллекцией
- Получить первый элемент коллекции (Т.к. параметр
IDилиCODEуникальны) - Закешировать результат
- Отдать результат в браузер методом
toJsonDetail(МетодtoJsonзарезервирован для списков)
Попробуем объединить данную функциональность в один контроллер, учитывая следующие особенности
- Каждый контроллер имеет свою модель
- Каждый контроллер имеет свою обработку и установку входящих параметров
- Каждый контроллер может иметь свои манипуляции с коллекцией
- Каждый контроллер может иметь свои манипуляции с сущностью
php
<?php
namespace Realweb\Api\Controller;
use Realweb\Api\Controller;
use Realweb\Api\Model\Data\Collection;
use Realweb\Api\Model\Data\Entity;
use Realweb\Api\Model\Main\Cache;
use Realweb\Api\Model\Main\Pagination;
/**
* Class \Realweb\Api\Module\Controller\EntityController
*/
abstract class EntityController extends Controller
{
/**
* Для каждого контроллера будет своя модель
* @return \Realweb\Api\Model\Data\Database
*/
abstract protected function _getDatabase(): string;
// Объект сущности для всех общий, удобно получать данные на бекенде
protected ?Entity $_entity = null;
// Метод получения данных для бекенда
public function getEntity(): ?Entity
{
if ($this->_entity === null) {
$this->setReturnJsonData(false);
$this->get();
}
return $this->_entity;
}
// Основной метод получения данных общий для всех
public function get(): ?array
{
$this->_initParams();
$obCache = Cache::getInstance()
->setId($this->getPagination())
// Время кеширования может отличаться
->setTime($this->_getCacheTime())
->setDir(__CLASS__)
;
// Теги для сброса кеша разные у всех, по умолчанию тег инфоблока, но можно и переопределить, если требуется
if ($mTag = $this->_getCacheTag()) {
if (is_array($mTag)) {
foreach ($mTag as $strTag) {
$obCache->addTag($strTag);
}
} else {
$obCache->addTag($mTag);
}
}
// Получение закешированного результата
$arCacheResult = $obCache->get(fn () => $this->_getEntity($this->getPagination()));
if (array_key_exists('entity', $arCacheResult)) {
$obEntity = $arCacheResult['entity'];
$this->_setEntity($obEntity);
// Если требуется отдать данные в браузер - отдаем
if ($this->isReturnJsonData()) {
return $obEntity->toJsonDetail();
}
} else {
$this->getComponent()->addErrorNoEntity();
}
return null;
}
// Основной запрос данных из модели
protected function _getEntity(Pagination $obNav): array
{
$obCollection = $this->_getDatabase()::getObjectCollection($obNav);
// Если нужны какие-то манипуляции с коллекцией - делаем их
$this->_processCollection($obCollection);
if ($obEntity = $obCollection->current()) {
// Если нужны какие-то манипуляции с сущностью - делаем их
$this->_processEntity($obEntity);
return array(
'entity' => $obEntity,
);
}
return array();
}
// Установка сущности в экземпляр класса контроллера
protected function _setEntity(Entity $obEntity): void
{
$this->_entity = $obEntity;
}
// Инициализация параметров, можно переопределить в контроллере проекта
protected function _initParams(): void
{
$this->getPagination();
}
protected function _getCacheTag(): array | string | null
{
return $this->_getDatabase()::getCacheTag();
}
protected function _getCacheTime(): int
{
return Cache::DURATION;
}
protected function _processCollection(Collection $obCollection): void
{
}
protected function _processEntity(Entity $obEntity): void
{
}
}Таким образом контроллер получения раздела принимает вид:
php
<?php
namespace Realweb\Api\Module\News\Controller\Section;
use Realweb\Api\Module\Controller;
use Realweb\Api\Module\News\Model\Section;
/**
* Class \Realweb\Api\Module\News\Controller\Section\EntityController
*/
class EntityController extends Controller\EntityController
{
public function checkGetParams(): bool
{
$strCode = $this->getRequestParam('code', $this->getParamString('code',''));
if (strlen($strCode) == 0) {
$this->getComponent()->addErrorNoParams();
return false;
}
return true;
}
/**
* @return Section\Entity
*/
public function getEntity(): ?\Realweb\Api\Model\Data\Entity
{
return parent::getEntity();
}
protected function _getDataBase(): string
{
return Section\Database::class;
}
protected function _initParams(): void
{
parent::_initParams();
$this->getPagination()
->setParam('code', $this->getRequestParam('code', $this->getParamString('code','')))
;
}
/**
* @param Section\Entity $obEntity
*/
protected function _processEntity(\Realweb\Api\Model\Data\Entity $obEntity): void
{
$obEntity->processChain();
}
}Контроллер CollectionController уже создан в ядре realweb.api
Метод getEntity необходим для получения раздела на бекенде, например
php
use Realweb\Api\Module\News;
$obSection = (new News\Controller\Section\EntityController())->setParam('code','iyul-2023')->getEntity();
//Теперь можно работать с этим разделомПример кода
Пример кода можно найти по ссылке