Создание API в IO7
Внимание
Данная статья документации написана нейросетью. Возможны некоторые расхождения и неточность информации!
Базовый API (CoreApi)
Создайте класс, наследующий IO\Api\CoreApi:
namespace App\Api;
use IO\Api\CoreApi;
use IO\Api\ApiResult;
class NotificationApi extends CoreApi
{
/**
* Разрешения для вызова через IO\Core::callAction()
*/
public static function rules(): array
{
return [
'static' => [
'actionSend' => 1,
'actionList' => 1,
],
'object' => [],
];
}
/**
* Инициализация (вызывается перед каждым action).
* Верните не-EIO_OK для прерывания.
*/
public function onInit(): int
{
$session = $this->app()->session;
if (!$session || !$session->isAuth()) {
return EIO_DENY;
}
return EIO_OK;
}
/**
* Отправка уведомления
*/
public function doSend(ApiResult $result, array $params): ApiResult
{
$to = $params['to'] ?? '';
$message = $params['message'] ?? '';
if (empty($to) || empty($message)) {
return $result->showResult([], 'Обязательные поля: to, message', EIO_VALIDATION);
}
NotificationService::send($to, $message);
return $result->showSuccess(['sent' => true]);
}
/**
* Список уведомлений
*/
public function doList(ApiResult $result, array $params): ApiResult
{
$items = Notification::search([
'filter' => ['user_id' => $this->app()->session->getUserID()],
'order' => ['pkid' => 'desc'],
'limit' => $params['limit'] ?? 20,
]);
return $result->showSuccess([
'items' => $items['res'],
'total' => $items['count'],
]);
}
}
Вызов:
// Отправка
$result = \IO\Core::callAction('app.api.notification.send', [
'to' => 'user@example.com',
'message' => 'Привет!',
]);
// Список
$result = \IO\Core::callAction('app.api.notification.list', [
'limit' => 10,
]);
CRUD API (CrudApi)
Наследуйтесь от IO\Api\CrudApi для стандартных операций:
namespace App\Api;
use IO\Api\CrudApi;
use IO\Api\ApiResult;
class ProductApi extends CrudApi
{
public static function struct(): array
{
return [
'class' => \App\Models\Product::class,
'fields' => [
'pkid' => ['type' => 'int'],
'name' => ['type' => 'string', 'label' => 'Название'],
'price' => ['type' => 'float', 'label' => 'Цена'],
'category_id' => ['type' => 'int', 'label' => 'Категория'],
'description' => ['type' => 'string', 'label' => 'Описание'],
'is_active' => ['type' => 'int'],
'is_deleted' => ['type' => 'int', 'virtual' => true],
'gmtimeAdd' => ['type' => 'string', 'readonly' => true],
'gmtimeChange' => ['type' => 'string', 'readonly' => true],
],
'primary' => ['pkid'],
];
}
public static function rules(): array
{
return [
'static' => [
'actionSearch' => 1,
'actionGetByID' => 1,
'actionCreate' => 1,
'actionSave' => 1,
'actionDelete' => 1,
],
'object' => [],
];
}
public function onSearchFilter(array $search_filter): array
{
$search_filter['is_deleted'] = 0;
return $search_filter;
}
public function onSearchParams(array $search_params): array
{
$search_params['join'][] = 'LEFT JOIN categories ON products.category_id = categories.pkid';
return $search_params;
}
public function onUpdateData(array $save_data, array $update_data): array
{
if (!isset($update_data['gmtimeChange'])) {
$update_data['gmtimeChange'] = iogetdbtimeutc(time());
}
return $update_data;
}
public function doUpdatePrices(ApiResult $result, array $params): ApiResult
{
$category_id = $params['category_id'] ?? 0;
$multiplier = $params['multiplier'] ?? 1.0;
if ($category_id <= 0 || $multiplier <= 0) {
return $result->showResult([], 'Невалидные параметры', EIO_VALIDATION);
}
\IO\Core::call('app.class.product.updatePricesByCategory', [
'category_id' => $category_id,
'multiplier' => $multiplier,
]);
return $result->showSuccess(['updated' => true]);
}
}
Вызов CRUD:
// Поиск
$result = \IO\Core::callAction('app.model.product.search', [
'filter' => ['category_id' => 5],
'limit' => 20,
]);
// Получение по ID
$result = \IO\Core::callAction('app.model.product.getByID', ['pkid' => 42]);
// Создание
$result = \IO\Core::callAction('app.model.product.create', [
'item' => [
'name' => 'Товар',
'price' => 1500.00,
'category_id' => 5,
],
]);
// Обновление
$result = \IO\Core::callAction('app.model.product.save', [
'pkid' => 42,
'item' => ['price' => 2000.00],
]);
// Удаление
$result = \IO\Core::callAction('app.model.product.delete', ['pkid' => 42]);
// Кастомный action
$result = \IO\Core::callAction('app.model.product.updatePrices', [
'category_id' => 5,
'multiplier' => 1.1,
]);
Жизненный цикл action
1. onBefore() ← логирование, подготовительные операции
2. onInit() ← проверка прав, валидация
3. doXxx() ← основная логика
4. onAfter() ← уведомления, очистка кеша
public function onInit(): int
{
$this->user = $this->app()->session->getUserInfo();
if (!$this->user) {
return EIO_DENY;
}
return EIO_OK;
}
public function onBefore(): void
{
error_log("Action started: {$this->action}");
}
public function onAfter(): void
{
$this->app()->cache->delete("product_list");
}
Правила доступа (rules)
public static function rules(): array
{
return [
'static' => [
'*' => 1, // все методы разрешены
'actionSearch' => 1, // конкретный метод
],
'object' => [], // методы экземпляра (не используются)
];
}
Доступ к сервисам
Внутри API доступ к сервисам через $this->app():
$db = $this->app()->iodb;
$this->app()->cache->set('key', 'value', 3600);
$value = $this->app()->cache->get('key');
$session = $this->app()->session;
$user_id = $session->getUserID();
$html = $this->app()->twig->render('@app/email.twig', ['user' => $user]);
Межсерверные вызовы
API автоматически поддерживают вызовы между серверами:
// Если 'product' зарегистрирован в $ioProjects как отдельный сервер,
// вызов будет отправлен через HTTP
$result = \IO\Core::callAction('product.model.product.search', [
'filter' => ['is_active' => 1],
]);