Дата последнего изменения: 21.04.2022
Рассмотрим на примере ajax-действие, в котором есть параметры:
public function renameUserAction($userId, $newName = 'guest', array $groups = array(2))
{
$user = User::getById($userId);
...
$user->rename($newName);
return $user;
}
Скалярные параметры $userId, $newName, $groups будут получены автоматически из REQUEST.
$_POST, после в $_GET.Если параметр не удалось найти, то действие не будет запущено, сервер пошлёт ответ с сообщением об ошибке, что не указан обязательный параметр.
По умолчанию внедрить можно:
При этом имя параметра может быть произвольное. Связывание идёт по классу:
public function listChildrenAction(Folder $folder, PageNavigation $pageNavigation); public function listChildrenAction(Folder $folder, PageNavigation $navigation); public function listChildrenAction(Folder $folder, PageNavigation $nav, \CRestServer $restServer);
Начнем с примера:
class Folder extends Controller
{
public function renameAction($folderId)
{
$folder = Folder::getById($folderId);
if (!$folder)
{
return null;
}
...
}
public function downloadAction($folderId)
{
$folder = Folder::getById($folderId);
...
}
public function deleteAction($folderId)
{
$folder = Folder::getById($folderId);
...
}
}
У нас есть обычный ajax-контроллер для некой папки Folder. Но все действия у нас в итоге производятся над объектом и везде у нас идёт попытка загрузки папки и т.д. Было бы здорово получать на вход метода сразу Folder $folder.
class Folder extends Controller
{
public function renameAction(Folder $folder);
public function downloadAction(Folder $folder);
public function deleteAction(Folder $folder);
}
И теперь это возможно:
class Folder extends Controller
{
public function getPrimaryAutoWiredParameter()
{
return new ExactParameter(
Folder::class, //полное имя класса подклассы, которого нужно создавать
'folder', //конкретное имя параметра, который будет внедряться
function($className, $id){ //функция, которая создаст объект для внедрения. На вход приходит конкретный класс и $id
return Folder::loadById($id);
}
);
}
}
В js вызов:
BX.ajax.runAction('folder.rename', {
data: {
id: 1
}
});
Важно, в создающем замыкании после $className можно указывать сколько угодно параметров, которые требуются для создания объекта. При этом они будут связываться с данными из $_REQUEST так же, как и скаляры в обычных методах-действиях.
class Folder extends Controller
{
public function getPrimaryAutoWiredParameter()
{
return new ExactParameter(
Folder::class,
'folder',
function($className, $entityId, $entityType){
return $className::buildByEntity($entityId, $entityType);
}
);
}
public function workAction(Folder $folder);
}
В js вызов:
BX.ajax.runAction('folder.work', {
data: {
entityId: 1,
entityType: 'folder-type'
}
});
Если требуется описать несколько параметров, которые нужно создавать:
class Folder extends Controller
{
/**
* @return Parameter[]
*/
public function getAutoWiredParameters()
{
return [
new ExactParameter(
Folder::class,
'folder',
function($className, $id){
return $className::loadById($id);
}
),
new ExactParameter(
File::class,
'file',
function($className, $fileId){
return $className::loadById($fileId);
}
),
];
}
public function workAction(Folder $folder, File $file);
}
Есть ещё обобщенный способ описания внедрений:
new \Bitrix\Main\Engine\AutoWire\Parameter(
Folder::class,
function($className, $mappedId){
return $className::buildById($mappedId);
}
);
Чуть подробнее: сначала объявили имя класса, подклассы которого мы будем пытаться создавать, когда встретим их в ajax-действиях. Анонимная функция будет заниматься созданием экземпляра.
$className - это конкретное имя класса, которое указано в type-hinting'e.$mappedId - это значение, которое получено из $_REQUEST. При этом в $_REQUEST будет искаться folderId. Имя параметра, который мы будем искать в $_REQUEST, по умолчанию создается как {имя переменной} + Id.
Folder $folder => folderId
Folder $nene => neneId
File $targetFile => targetFileId
Таким образом, если в модуле есть класс, например, Model от которого наследуются все сущности, то можно описать тип:
new \Bitrix\Main\Engine\AutoWire\Parameter(
Model::class,
function($className, $mappedId){
/** @var Model $className */
return $className::buildById($mappedId);
}
);
И в дальнейшем легко и просто использовать type-hinting в своих ajax-действиях, сразу оперируя сущностями.