Задать вопрос
@Mad-cote

Для чего нужен wakeUp в Битрикс?

Добрый день, есть небольшое непонимание по поводу метода wakeUp в Битриксе. Зачем он нужен и для чего?

К примеру, мы можем получить сущность инфоблока таким образом:
$classEntity = \Bitrix\Iblock\Iblock::wakeUp(IBLOCK_CATALOG_ID)->getEntityDataClass();


Также, из документации Битрикс мы видим, что можно обратиться таким образом к своим классам:
$book = \Bitrix\Main\Test\Typography\Book::wakeUp(
	['ID' => 1, 'TITLE' => 'Title 1']
);


Но, зачем? Если мы можем напрямую обратиться к сущности и получить все данные
\Bitrix\Iblock\Elements\ElementProductsTable::getByPrimary...


В документации написано, что "для инициализации объекта необязательно выбирать их повторно из базы данных. Объект можно восстановить, имея как минимум значения первичного ключа", но откуда тогда он берет данные? Где они хранятся в таком случае? В кеше, в сессии, в памяти? Откуда и куда он восстанавливает сущность? В общем, один простой вопрос - нафига он нужен?

Буду благодарен за любые подсказки и мало-мальские примеры в реальной практике для понимания.
  • Вопрос задан
  • 402 просмотра
Подписаться 1 Простой Комментировать
Пригласить эксперта
Ответы на вопрос 3
Compolomus
@Compolomus Куратор тега PHP
Комполом-быдлокодер
Ответ написан
Комментировать
если посмотреть код, то видно, что при wakeUp не выполняется никаких обращений к БД.
Метод в файле bitrix/modules/main/lib/orm/objectify/entityobject.php
смотрите разницу:
fill*() - подгружает значение из БД
set*() - задаёт значение свойства, но при несовпадении со старым значением переводит объект в состояние State::CHANGED
wakeUp() -создаёт объект с указанными значениями свойств, не переводя объект в изменённое состояние, но только при условии, что в аргументе есть первичный ключ.
Ответ написан
Комментировать
gromdron
@gromdron
Работаю с Bitrix24
Метод wakeUp достаточно полезен в различных сценариях работы.
Вот несколько примеров его использования:

1. Использование в целях тестирования.

Предположим у вас есть некоторый класс объекта Record, который является объектом для RecordTable.
Вы написали некоторую функицю, которая выполняет сложную проверку (например что дата записи не в прошедшем времени, что указанный пользователь является активным и т.п.).
Допустим вот такая:
{
	public function isAcceptable( Record $record): bool
	{
		if ( $this->container()->getDateSerivce()->isEarlyDate($record->getDate()) )
		{
			return false;
		}

		if ( !$this->container()->getUserRepository()->isActiveUser($record->getUserId()))
		{
			return false;
		}

		// ... 

		return true;
	}
}


Как вам ее протестировать?
Наверное нужно получить объект класса Record из БД и отправить в функцию, но что делать если такой записи нет? Создать ее? Но это же тест, а создание это запись в БД, которая может отразиться над ругих тестах.
Решение - wakeUp:

// Now - 20.03.2025
$earlyDateObject = Record::wakeUp([
	'ID' => -1,
	'DATE' => DateTime::createFromTimestamp(strtotime("10.03.2025"))
]);

assertFalse( $checker->isAcceptable($earlyDateObject) );

$unexistedUserRecord = Record::wakeUp([
	'ID' => -1,
	'DATE' => DateTime::createFromTimestamp(strtotime("21.03.2025")),
	'USER_ID' => -1
]);

assertFalse( $checker->isAcceptable($earlyDateObject) );

$normalRecord = Record::wakeUp([
	'ID' => -1,
	'DATE' => DateTime::createFromTimestamp(strtotime("21.03.2025")),
	'USER_ID' => 1
]);

assertTrue( $checker->isAcceptable($earlyDateObject) );


Таким образмо мы написали псевдо-юнит тест который покрывает нашу функцию и при этом не создает и не использует ничего лишнего из БД.

2. Восстановление данных из кеша.

Предположим, заходя на страницу вы закешировали ID ресурсов отображаемых на ней, но есть некоторая произвольная величина закешировать которую вы ну никак не можете.
Механизм вычисления этой величины описан в функции calculatePriority, но она принимает объект Record, а у вас массив из кеша.

Таким образом получается некоторый такой код:

$myCachedIds = Container::getInstance()->getCacheService()->get('MY_SUPER_KEY');

$records = RecordTable::createCollection();

foreach($myCachedIds as $recordId)
{
	$records->add(
		Record::wakeUp([
			'ID' => $recordId
		])
	);
}

$records->fill();

foreach( $records as $record )
{
	$arResult['RECORDS'][] = [
		'ID' => $record->getId(),
		'PRIORITY' => getPriority($record) 
	];
}


3. Получение данных из других источников.

Допустим у нас есть несколько табилц: Автор и Книга.

Автор содержит:
- ID
- ФИО (NAME)
- Город (CITY)

Книга содержит:
- ID
- ID автора
- Название

Предположим мы делаем постраничную навигацию для каталога книг и нам нужно вывести ФИО автора.

$authorCollection = AuthorTable::createCollection();

$books = BookTable::getList()->fetchCollection();

foreach( $books as $book)
{
	$authorCollection->add(
		Author::wakeUp([
			'ID' => $book->getAuthorId()
		])
	);
}

$authorCollection->fill(['CITY', 'NAME']);


Согласен, здесь пример кажется немного отстраненным, ведь для связи 1:n можно было бы просто дозапросить данные из БД в 1 запрос, но если у вас таких таблиц уже не 2, а скажем 4-6, то последовательные запросы на получение только необходимой информации серьезно сократят время на получение данных.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы