Этот текст - переводстатьи из нового портала документации для разработчиков Joomla, раздел "Основные концепции". Ранее уже был опубликован перевод раздела, описывающего принципы Dependency Injection Containers в Joomla 4. Первоначально эта статья была опубликована в моём блоге на Хабре. Копирую к себе.
Концепция веб-сервисов
-
Веб-сервисы используются для взаимодействия различных систем друг с другом с помощью протокола HTTP, а в настоящее время и через TLS (безопасность транспортного уровня).
-
Веб-сервисы действуют как контракт между поставщиком сервиса и потребителем сервиса через Endpoint.
-
Веб-сервисы подобны дверям и окнам в доме, они являются ВХОДАМИ (inputs) и ВЫХОДАМИ (outputs) во ВНЕШНИЙ мир.
-
В контексте Joomla как системы в целом, Joomla Web services API позволяет Joomla взаимодействовать с внешними источниками данных. Например, веб-приложениями, мобильными приложениями и т.д.
Взаимодействие с Web Services API Joomla 4.x
Взаимодействие с API веб-сервисов Joomla 4 происходит через определённые endpoint'ы:
-
Список endpoint'ов ядра Joomla (официальная документация Joomla)
Использование Joomla API
Чаще всего, при использовании Joomla API, под капотом по-прежнему используется cURL или php. Чаще всего cURL доступен на Вашем хостинге. В противном случае проверьте с помощью phpinfo();
Определим переменные
Сначала мы определим некоторые переменные, которые будем использовать во всех наших запросах:
-
URL нашего сайта на Joomla 4.x
-
Токен Joomla API учетной записи суперпользователя или учетной записи, у которой есть, по крайней мере, разрешение
core.login.api
иcore.login.site
, чтобы иметь возможность видеть изменение токена текущего пользователя, вошедшего в систему.
<?php
// Before passing the HTTP METHOD to CURL
use Joomla\Http\HttpFactory;
use Joomla\Uri\Uri;
$http = (new HttpFactory())->getAvailableDriver();
$url = 'https://example.org/api/index.php/v1';
$uri = new Uri($url);
// Поместите Ваш токен Joomla! Api token в безопасное место,
// например, в менеджер паролей или хранилище.
// Мы не рекомендуем использовать переменные окружения
// для хранения паролей.
// Здесь описано почему: https://www.trendmicro.com/en_us/research/22/h/analyzing-hidden-danger-of-environment-variables-for-keeping-secrets.html
$token = '';
[Статья Создание внешних запросов с использованием HttpFactory (Joomla) на Хабре - Т.С.]
Поместите Ваш токен Joomla! Api token в безопасное место, например, в менеджер паролей или хранилище. Мы не рекомендуем использовать переменные окружения для хранения паролей. Здесь описано почему - статья "Analyzing the Hidden Danger of Environment Variables for Keeping Secrets" на TrendMicro.com.
POST - Создание материала Joomla в категории "Uncategorized" ("Без категории") по REST API (ID категории = 2)
<?php
use Joomla\Http\HttpFactory;
use Joomla\Uri\Uri;
$http = (new HttpFactory())->getAvailableDriver();
$url = 'https://example.org/api/index.php/v1';
$uri = new Uri($url);
$token = '';
$categoryId = 2; // Категория "Без категории" Joomla по умолчанию
$data = [
'title' => 'Как добавить материал Joomla с помощью REST API?',
'alias' => 'кak-dobavit-material-joomla-s-pomoshhju-rest-api',
'articletext' => 'Я пока не знаю... Поищу-ка я новенькие статьи о Joomla на Хабре.',
'catid' => $categoryId,
'language' => '*',
'metadesc' => '',
'metakey' => '',
];
$dataString = json_encode($data);
// HTTP request headers
$headers = [
'Accept: application/vnd.api+json',
'Content-Type: application/json',
'Content-Length: ' . mb_strlen($dataString),
sprintf('X-Joomla-Token: %s', trim($token)),
];
// Таймаут в секундах
$timeout = 30;
// Установите endpoint для работы с материалами.
// Это часть $uri.
$uri->setPath('content/articles');
// Это будет PSR-7 совместимый Response
$response = $http->request('POST', $uri, $dataString, $headers, $timeout);
// В результате запроса мы имеем stream, поэтому делаем просто:
echo $response->body;
Небольшое отступление о классе Uri в Joomla (от переводчика)
Для большего понимания происходящего в коде и дальнейшей более удобной работы с Joomla API позволю себе внедрить краткую справку о классах Uri в Joomla:
-
Доки для Joomla 3, но класс Uri в Joomla 4 остался в целом тем же.
-
В документации по ссылке указан класс
Joomla\CMS\Uri\Uri
, который расширяет классJoomla\Uri\Uri
, который в свою очередь, расширяет классJoomla\Uri\AbstractUri
. Методы родительского класса доступны в дочернем. -
Класс
Joomla\Uri\Uri
предназначен для работы с url и позволяет работать с ним как с объектом:$url->setPath
,$url->setScheme
,$url->setScheme
,$url->setScheme
и другими методами (список смотрим в файле класса: libraries/vendor/joomla/uri/src/Uri.php). Соответствующие геттеры заданы в классеJoomla\Uri\AbstractUri
-
Класс
Joomla\CMS\Uri\Uri
добавляет несколько методов, которые необходимы для больше внутренней работы Joomla: определение внешний ли или внутренний url, текущий url без GET-параметров и т.д.
GET - Получение списка всех материалов Joomla из категории "Uncategorized" ("Без категории")
<?php
use Joomla\Http\HttpFactory;
use Joomla\Uri\Uri;
$http = (new HttpFactory())->getAvailableDriver();
$url = 'https://example.org/api/index.php/v1';
$uri = new Uri($url);
$token = '';
$categoryId = 2; // Категория "Без категории" Joomla по умолчанию
// Не будем отправлять payload на сервер
$dataString = null;
// HTTP заголовки запроса
$headers = [
'Accept: application/vnd.api+json',
'Content-Type: application/json',
sprintf('X-Joomla-Token: %s', trim($token)),
];
// Таймаут в секундах
$timeout = 30;
// Установите endpoint для работы с материалами.
// Это часть $uri.
$uri->setPath('content/articles');
// Это будет PSR-7 совместимый Response
$response = $http->request('GET', $uri, $dataString, $headers, $timeout);
// В результате запроса мы имеем stream, поэтому делаем просто:
echo $response->body;
GET - Получение материала Joomla по id (REST API)
<?php
use Joomla\Http\HttpFactory;
use Joomla\Uri\Uri;
$http = (new HttpFactory())->getAvailableDriver();
$url = 'https://example.org/api/index.php/v1';
$uri = new Uri($url);
$token = '';
$articleId = 1; // ID нужной статьи в Joomla
// Не будем отправлять payload на сервер
$dataString = null;
// HTTP заголовки запроса
$headers = [
'Accept: application/vnd.api+json',
'Content-Type: application/json',
sprintf('X-Joomla-Token: %s', trim($token)),
];
// Таймаут в секундах
$timeout = 30;
// Устанавливаем path-часть Url для получения конкретного материала.
$uri->setPath(sprintf('content/articles/%d', $articleId));
// Это будет PSR-7 совместимый Response
$response = $http->request('GET', $uri, $dataString, $headers, $timeout);
// В результате запроса мы имеем stream, поэтому делаем просто:
echo $response->body;
PATCH - Изменение материала Joomla по id (REST API)
<?php
use Joomla\Http\HttpFactory;
use Joomla\Uri\Uri;
$http = (new HttpFactory())->getAvailableDriver();
$url = 'https://example.org/api/index.php/v1';
$uri = new Uri($url);
$token = '';
$articleId = 1; // ID нужной статьи в Joomla
$data = [
'id' => $articleId,
'title' => 'Как добавить материал Joomla с помощью REST API?',
'introtext' => 'При использовании PATCH текст статьи должен быть разделен на две части или использовать, по крайней мере, только introtext, чтобы сохранить данные должным образом',
'fulltext' => 'Больше контента богу контента!',
];
$dataString = json_encode($data);
// HTTP заголовки запроса
$headers = [
'Accept: application/vnd.api+json',
'Content-Type: application/json',
'Content-Length: ' . mb_strlen($dataString),
sprintf('X-Joomla-Token: %s', trim($token)),
];
// Таймаут в секундах
$timeout = 30;
// Устанавливаем path-часть Url для изменения конкретного материала.
$uri->setPath(sprintf('content/articles/%d', $articleId));
// Это будет PSR-7 совместимый Response
$response = $http->request('PATCH', $uri, $dataString, $headers, $timeout);
// Здесь получим status code запроса
echo $response->code;
Примечание переводчика о структуре сущности материала Joomla
Примечание переводчика для разработчиков, не знакомых со структурой компонента материалов (com_content) Joomla. Каждый материал (article) имеет вступительный текст и вводный текст. Вступительный текст отображается в списке материалов в виде блога (с картинкой вступительного текста, парой абзацев и кнопкой "подробнее"). Полный текст материала отображается, соответственно, когда пользователь хочет читать статью полностью и открывает её.
В базе данных в таблице для материалов Joomla реализовано 2 ячейки: introtext
и fulltext
типа MEDIUMTEXT
соответственно.
Если контент-менеджер с помощью вставки линии "подробнее" в админке указал Joomla, что текст нужно разбить на вступительную и полную часть, то текст будет разрезан на 2 части и каждая из частей будет храниться в своей ячейке. Если же линия "подробнее" не вставлена, то весь текст хранится в ячейке introtext.
Таким образом при работе с REST API нужно представлять КАК происходит работа с текстом на всех этапах: создание, редактирование, отображение редактору, отображение пользователю сайта. Если Joomla используется как бэкенд, а фронт - это мобильное приложение или фронт сделан на js-фреймворке, то отображение зависит уже не от Joomla, а от реализации приложения и тогда весь текст материала можно хранить в introtext. Если же REST API используется при редактировании, а для посетителей используется HTML-вывод Joomla, то соблюдение правил игры CMS будет важным, так как это будет влиять на отображение для пользователей сайта.
DELETE - Удаление материала Joomla по id (REST API)
<?php
use Joomla\Http\HttpFactory;
use Joomla\Uri\Uri;
$http = (new HttpFactory())->getAvailableDriver();
$url = 'https://example.org/api/index.php/v1';
$uri = new Uri($url);
$token = '';
$articleId = 1; // ID нужной статьи в Joomla
// Не будем отправлять payload на сервер
$dataString = null;
// HTTP заголовки запроса
$headers = [
'Accept: application/vnd.api+json',
'Content-Type: application/json',
sprintf('X-Joomla-Token: %s', trim($token)),
];
// Таймаут в секундах
$timeout = 30;
// Устанавливаем path-часть Url для удаления конкретного материала.
$uri->setPath(sprintf('content/articles/%d', $articleId));
// Это будет PSR-7 совместимый Response
$response = $http->request('DELETE', $uri, $dataString, $headers, $timeout);
// Здесь получим status code запроса
echo $response->code;
Запросы к REST API Joomla с использованием чистого PHP cURL
Перед использованием этого кода удостоверьтесь, что cURL установлен на Вашем хостинге, проверьте с помощью phpinfo()
. [Данный раздел документации может пригодится для non-Joomla сайтов, которые будут взаимодействовать с Joomla 4+ сайтами - Т.С.]
Определим переменные
Сначала мы определим некоторые переменные, которые будем использовать во всех наших cURL запросах:
-
URL нашего сайта на Joomla 4.x
-
Токен Joomla API учетной записи суперпользователя или учетной записи, у которой есть, по крайней мере, разрешение
core.login.api
иcore.login.site
, чтобы иметь возможность видеть изменение токена текущего пользователя, вошедшего в систему.
<?php
// Before passing the HTTP METHOD to CURL
$curl = curl_init();
$url = 'https://example.org/api/index.php/v1';
// Поместите Ваш токен Joomla! Api token в безопасное место,
// например, в менеджер паролей или хранилище.
// Мы не рекомендуем использовать переменные окружения
// для хранения паролей.
// Здесь описано почему: https://www.trendmicro.com/en_us/research/22/h/analyzing-hidden-danger-of-environment-variables-for-keeping-secrets.html
$token = '';
POST - Создание материала Joomla в категории "Uncategorized" ("Без категории") по REST API с помощью cURL
<?php
// Before passing the HTTP METHOD to CURL
$curl = curl_init();
$url = 'https://example.org/api/index.php/v1';
$token = '';
$categoryId = 2; // Joomla's default "Uncategorized" Category
$data = [
'title' => 'How to add an article to Joomla via the API?',
'alias' => 'how-to-add-article-via-joomla-api',
'articletext' => 'I have no idea...',
'catid' => $categoryId,
'language' => '*',
'metadesc' => '',
'metakey' => '',
];
$dataString = json_encode($data);
// HTTP request headers
$headers = [
'Accept: application/vnd.api+json',
'Content-Type: application/json',
'Content-Length: ' . mb_strlen($dataString),
sprintf('X-Joomla-Token: %s', trim($token)),
];
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/%s',$url,'content/articles'),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => 'utf-8',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2TLS,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $dataString,
CURLOPT_HTTPHEADER => $headers,
]
);
$response = curl_exec($curl);
curl_close($curl);
echo $response;
GET - Получение списка всех материалов Joomla из категории "Uncategorized" ("Без категории") с помощью cURL по REST API
<?php
// Before passing the HTTP METHOD to CURL
$curl = curl_init();
$url = 'https://example.org/api/index.php/v1';
$token = '';
$categoryId = 2; // Joomla's default "Uncategorized" Category
// HTTP request headers
$headers = [
'Accept: application/vnd.api+json',
'Content-Type: application/json',
sprintf('X-Joomla-Token: %s', trim($token)),
];
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/content/articles?filter[category]=%d',$url,$categoryId),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => 'utf-8',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2TLS,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => $headers,
]
);
$response = curl_exec($curl);
curl_close($curl);
echo $response;
GET - Получение конкретного материала Joomla по id с помощью cURL (REST API)
<?php
// Before passing the HTTP METHOD to CURL
$curl = curl_init();
$url = 'https://example.org/api/index.php/v1';
$token = '';
$articleId = 1; // The Article ID of a specific Article
// HTTP request headers
$headers = [
'Accept: application/vnd.api+json',
'Content-Type: application/json',
sprintf('X-Joomla-Token: %s', trim($token)),
];
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/content/articles/%d',$url,$articleId),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => 'utf-8',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2TLS,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => $headers,
]
);
$response = curl_exec($curl);
curl_close($curl);
echo $response;
PATCH - Изменение материала Joomla по id с помощью cURL (REST API)
<?php
// Before passing the HTTP METHOD to CURL
$curl = curl_init();
$url = 'https://example.org/api/index.php/v1';
$token = '';
$articleId = 1; // The Article ID of a specific Article
$data = [
'id' => $articleId,
'title' => 'How to add an article via the Joomla 4 API?',
'introtext' => 'When using PATCH, articletext MUST be splitted in two parts or use at least just introtext in order to work properly',
'fulltext' => 'MORE CONTENT if you wish',
];
$dataString = json_encode($data);
// HTTP request headers
$headers = [
'Accept: application/vnd.api+json',
'Content-Type: application/json',
'Content-Length: ' . mb_strlen($dataString),
sprintf('X-Joomla-Token: %s', trim($token)),
];
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/content/articles/%d',$url,$articleId),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => 'utf-8',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2TLS,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_POSTFIELDS => $dataString,
CURLOPT_HTTPHEADER => $headers,
]
);
$response = curl_exec($curl);
curl_close($curl);
echo $response;
DELETE - Удаление материала Joomla по id с помощью cURL (REST API)
<?php
// Before passing the HTTP METHOD to CURL
$curl = curl_init();
$url = 'https://example.org/api/index.php/v1';
$token = '';
$articleId = 1; // The Article ID of a specific Article
// HTTP request headers
$headers = [
'Accept: application/vnd.api+json',
'Content-Type: application/json',
sprintf('X-Joomla-Token: %s', trim($token)),
];
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/content/articles/%d',$url,$articleId),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => 'utf-8',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2TLS,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => $headers,
]
);
$response = curl_exec($curl);
curl_close($curl);
echo $response;