...
Поскольку практически каждая функция администратора должна иметь CRUD-составляющую, то вот как это выглядит для /portal/articles
Code Block |
---|
language | perl |
---|
title | CRUD роуты |
---|
collapse | true |
---|
|
return [
# Создать статью
{
method => 'POST',
path => '/portal/articles/',
# Параметры до валидатора
params => POST_PORTAL_ARTICLES,
controller => 'Portal::Api::admin::Articles',
endpoint => \&Portal::Api::admin::Articles::post_portal_articles,
credentials => [
'ADMIN', 'ADMINSID'
]
},
# Получить статьи
{
method => 'GET',
path => '/portal/articles/',
controller => 'Portal::Api::admin::Articles',
endpoint => \&Portal::Api::admin::Articles::get_portal_articles,
credentials => [
'ADMIN', 'ADMINSID'
]
},
# Получить конкретную статью
{
method => 'GET',
path => '/portal/articles/:id/',
controller => 'Portal::Api::admin::Articles',
endpoint => \&Portal::Api::admin::Articles::get_portal_articles_id,
credentials => [
'ADMIN', 'ADMINSID'
]
},
# Изменить конкретную статью
{
method => 'PUT',
path => '/portal/articles/:id/',
controller => 'Portal::Api::admin::Articles',
endpoint => \&Portal::Api::admin::Articles::put_portal_articles_id,
credentials => [
'ADMIN', 'ADMINSID'
]
},
# Удалить конкретную статью
{
method => 'DELETE',
path => '/portal/articles/:id/',
controller => 'Portal::Api::admin::Articles',
endpoint => \&Portal::Api::admin::Articles::delete_portal_articles_id,
credentials => [
'ADMIN', 'ADMINSID'
]
},
] |
...
Code Block |
---|
language | perl |
---|
title | USER API роуты |
---|
collapse | true |
---|
|
#**********************************************************
=head2 user_routes() - Returns available USER API paths
=cut
#**********************************************************
sub user_routes {
my $self = shift;
return [
{
method => 'GET',
# Для USER API ОБЯЗАТЕЛЬНО начинаем абсолютный путь с /user/*.
path => '/user/portal/menu/',
# Подключаем "контроллер" для API /user/portal/*
controller => 'Portal::Api::user::News',
# Даём ссылку на функцию-эндпойнт контроллера
endpoint => \&Portal::Api::user::News::get_user_portal_news,
credentials => [
# Определяем нужные параметры для авторизации.
# USER - авторизация по header, полученном с /user/login
# USERSID - авторизация по cookie (в том числе для api_call)
# PUBLIC - без авторизации
# Тоесть, в данном случае путь может работать как и с авторизованными пользователями, так и нет.
# Внутри хэндлера можно определять какой пользователь, об этом ниже.
'USER', 'USERSID', 'PUBLIC'
]
},
]
} |
...
Code Block |
---|
language | perl |
---|
title | контроллер Portal::Api::user::News |
---|
collapse | true |
---|
|
package Portal::Api::user::News;
=head1 NAME
User Portal
# Рекомендуем в подах записывать к каким
# группам эндпойнтов относится данный контроллер
Endpoints:
/user/portal/news*
/user/portal/menu
=cut
use strict;
use warnings FATAL => 'all';
use Control::Errors;
# Импортируем объект Portal для работы с базой
# он должен находиться в /usr/abills/Abills/mysql/Portal.pm
use Portal;
my Portal $Portal;
my Control::Errors $Errors;
#**********************************************************
=head2 new($db, $admin, $conf)
=cut
#**********************************************************
sub new {
my ($class, $db, $admin, $conf, $attr) = @_;
my $self = {
db => $db,
admin => $admin,
conf => $conf,
attr => $attr
};
bless($self, $class);
$Portal = Portal->new($db, $admin, $conf);
# Определяем словарь ошибок, который нам пришёл выше
$Errors = $self->{attr}->{Errors};
return $self;
}
# здесь определять пути
1; |
...
В целом, определение роутов для USER API ничем не отличается от ADMIN API, кроме одной важной детали - в $path_params при авторизации будет приходить uid.
Очень важная составляющая.
Code Block |
---|
|
#**********************************************************
=head2 get_user_portal_news($path_params, $query_params)
Endpoint GET /user/portal/news
=cut
#**********************************************************
sub get_user_portal_news {
my $self = shift;
my ($path_params, $query_params) = @_;
# Не обязательно писать всю логику прямо внутри эндпоинта, как в примере с ADMIN API
# Вы можете делить логику в бизнес-функции, для сокращения использования.
# Но для простоты понимания, с самого начала лучше писать всё в эндпоинтах
return $self->_portal_menu({
# Если пользователь авторизован - в $path_params->{uid} будет UID пользователя.
# Если нет - поле будет пустое.
UID => $path_params->{uid} || '',
DOMAIN_ID => $query_params->{DOMAIN_ID},
PORTAL_MENU_ID => $query_params->{PORTAL_MENU_ID},
MAIN_PAGE => $query_params->{MAIN_PAGE},
LIST => 1
});
} |
...
Для правильной работы сортировки мы рекомендуем делать это не вручну, а с помощью search_former которые находятся внутри любого современного модуля.
Он выглядит приблизительно так, с точки зрения пути GET /portal/menus
Code Block |
---|
|
#**********************************************************
=head2 get_portal_menus($path_params, $query_params)
Endpoint GET /portal/menus
=cut
#**********************************************************
sub get_portal_menus {
my $self = shift;
my ($path_params, $query_params) = @_;
# Проверяем, определяем стандартные параметры для вызова
my %PARAMS = (
COLS_NAME => 1,
PAGE_ROWS => $query_params->{PAGE_ROWS} ? $query_params->{PAGE_ROWS} : 25,
SORT => $query_params->{SORT} ? $query_params->{SORT} : 1,
PG => $query_params->{PG} ? $query_params->{PG} : 0,
DESC => $query_params->{DESC},
);
foreach my $param (keys %{$query_params}) {
$query_params->{$param} = ($query_params->{$param} || "$query_params->{$param}" eq '0')
? $query_params->{$param}
: '_SHOW';
}
my $list = $Portal->portal_menu_list({
ID => '_SHOW',
NAME => '_SHOW',
URL => '_SHOW',
DATE => '_SHOW',
STATUS => '_SHOW',
# С помощью внутренней деструктуризации присваиваем
%$query_params,
# Оверрайдим параметры
%PARAMS,
});
return {
list => $list,
total => $Portal->{TOTAL}
};
} |
И смотрим что происходит под капотом у portal_menu_list
Code Block |
---|
|
#**********************************************************
=head2 function portal_menu_list() - get menu section list
Arguments:
$attr
Returns:
\@list -
Examples:
my $list = $Portal->portal_menu_list({COLS_NAME=>1});
=cut
#**********************************************************
sub portal_menu_list {
my $self = shift;
my ($attr) = @_;
# Преопределяем параметры, если их нет
my $SORT = ($attr->{SORT}) ? $attr->{SORT} : 1;
my $DESC = ($attr->{DESC}) ? $attr->{DESC} : '';
my $PG = $attr->{PG} ? $attr->{PG} : 0;
my $PAGE_ROWS = $attr->{PAGE_ROWS} ? $attr->{PAGE_ROWS} : 25;
# Эта функция на основе пришедших параметров и паттерном формирует $WHERE clause.
my $WHERE = $self->search_former($attr, [
[ 'ID', 'INT', 'pm.id', 1 ],
[ 'NAME', 'STR', 'pm.name', 1 ],
[ 'URL', 'STR', 'pm.url', 1 ],
[ 'DATE', 'STR', 'DATE(pm.date) as date', 1 ],
[ 'STATUS', 'INT', 'pm.status', 1 ],
], { WHERE => 1 });
$self->query(
"SELECT $self->{SEARCH_FIELDS} pm.id
FROM portal_menu pm
$WHERE
ORDER BY $SORT $DESC LIMIT $PG, $PAGE_ROWS;;",
undef, $attr
);
my $list = $self->{list} || [];
# Берём общий count
$self->query("SELECT COUNT(*) AS total FROM portal_menu pm
$WHERE;",
undef,
{ INFO => 1 }
);
return $list || [];
} |
...