Webservices - Unterstützen der Joomla-API
In diesem Teil werfen wir einen Blick auf die Joomla 4-API und den Zugriff auf Joomla 4-Inhalte. Eine Programmierschnittstelle - kurz API (von englisch application programming interface) - ist ein Programmteil, der von einem Softwaresystem anderen Programmen zur Anbindung an das System zur Verfügung gestellt wird. Heutzutage bieten viele Online-Dienste APIs; diese heißen dann Webservice. Das Vorhandensein einer dokumentierten Programmierschnittstelle (API) für eine Joomla-Komponente macht es möglich, mit anderen zusammenzuarbeiten. Entweder über zusätzliche Software, welche die API per Erweiterung nutzt oder Daten werden über die API in anderen Anwendungen nutzbar.
Für Ungeduldige: Sieh dir den geänderten Programmcode in der Diff-Ansicht[^codeberg.org/astrid/j4examplecode/compare/t29…t30] an und übernimm diese Änderungen in deine Entwicklungsversion.
Schritt für Schritt
Neue Dateien
Komponente
api/components/com_foos/src/Controller/FooController.php
Erstelle den Controller FooController
der von ApiController
erbt. In der Klasse ApiController
ist alles Notwendige implementiert. Wenn du keine abweichenden Anforderungen hast, dann ist das Rad erfunden. Überschreibe lediglich die folgenden Felder für deine Komponente:
protected $contentType = 'foos';
und protected $default_view = 'foos';
.
$contentType
- wird standardmäßig für$modelName
verwendet.$default_view
- ist Standard für$viewName
.
//
<?php
namespace FooNamespace\Component\Foos\Api\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\Controller\ApiController;
use Joomla\Component\Fields\Administrator\Helper\FieldsHelper;
class FooController extends ApiController
{
protected $contentType = 'foos';
protected $default_view = 'foos';
protected function save($recordKey = null)
{
$data = (array) json_decode($this->input->json->getRaw(), true);
foreach (FieldsHelper::getFields('com_foos.foo') as $field)
{
if (isset($data[$field->name]))
{
!isset($data['com_fields']) && $data['com_fields'] = [];
$data['com_fields'][$field->name] = $data[$field->name];
unset($data[$field->name]);
}
}
$this->input->set('data', $data);
return parent::save($recordKey);
}
}
api/components/com_foos/src/View/Foos/JsonapiView.php
Erstelle die Schnittstelle JsonapiView
die von BaseApiView
erbt. Greife wieder auf fertigen Joomla Code zu. Überschreibe die folgenden Felder für deine Komponente:
protected $fieldsToRenderItem = ['id', 'alias', 'name', 'catid'];
und protected $fieldsToRenderList = ['id', 'alias', 'name', 'catid'];
.
$fieldsToRenderItem
- Array mit Informationen zum Anzeigen eines einzelnen Objekts.$fieldsToRenderList
- Array mit Inhalten zum Auflisten von Objekten.
<?php
namespace FooNamespace\Component\Foos\Api\View\Foos;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\JsonApiView as BaseApiView;
use Joomla\Component\Fields\Administrator\Helper\FieldsHelper;
class JsonapiView extends BaseApiView
{
protected $fieldsToRenderItem = ['id', 'alias', 'name', 'catid'];
protected $fieldsToRenderList = ['id', 'alias', 'name', 'catid'];
public function displayList(array $items = null)
{
foreach (FieldsHelper::getFields('com_foos.foo') as $field)
{
$this->fieldsToRenderList[] = $field->id;
}
return parent::displayList();
}
public function displayItem($item = null)
{
foreach (FieldsHelper::getFields('com_foos.foo') as $field)
{
$this->fieldsToRenderItem[] = $field->name;
}
return parent::displayItem();
}
protected function prepareItem($item)
{
foreach (FieldsHelper::getFields('com_foos.foo', $item, true) as $field)
{
$item->{$field->name} = isset($field->apivalue) ? $field->apivalue : $field->rawvalue;
}
return parent::prepareItem($item);
}
}
Plugin
plugins/webservices/foos/foos.php
In der Plugin-Datei erstellen wir die Klasse PlgWebservicesFoos
und registrieren in der onBeforeApiRoute
-Methode alle Routen, die wir für den Webservice benötigen.
<?php
defined('_JEXEC') or die;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Router\ApiRouter;
class PlgWebservicesFoos extends CMSPlugin
{
protected $autoloadLanguage = true;
public function onBeforeApiRoute(&$router)
{
$router->createCRUDRoutes(
'v1/foos',
'foo',
['component' => 'com_foos']
);
$router->createCRUDRoutes(
'v1/foos/categories',
'categories',
['component' => 'com_categories', 'extension' => 'com_foos']
);
}
}
plugins/webservices/foos/foos.xml
Um das Plugin zu installieren, ist eine Installationsdatei notwendig. Die kennst du von der Komponente.
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="webservices" method="upgrade">
<name>plg_webservices_foos</name>
<creationDate>[DATE]</creationDate>
<author>[AUTHOR]</author>
<authorEmail>[AUTHOR_EMAIL]</authorEmail>
<authorUrl>[AUTHOR_URL]</authorUrl>
<copyright>[COPYRIGHT]</copyright>
<license>GNU General Public License version 2 or later;</license>
<version>__BUMP_VERSION__</version>
<description>PLG_WEBSERVICES_FOOS_XML_DESCRIPTION</description>
<files>
<filename plugin="foos">foos.php</filename>
<folder>language</folder>
</files>
</extension>
plugins/webservices/foos/language/en-GB/plg_webservices_foos.ini
Die Sprach-Dateien füge ich der Vollständigkeit halber bei.
PLG_WEBSERVICES_FOOS="Web Services - Foos"
PLG_WEBSERVICES_FOOS_XML_DESCRIPTION="Used to add foos routes to the API for your website."
plugins/webservices/foos/language/en-GB/plg_webservices_foos.sys.ini
Auch die Sprach-Datei, welche in Hautpsache der für die Installation und die Erstellung des Menüs im Dashboard zuständig ist, füge ich bei.
PLG_WEBSERVICES_FOOS="Web Services - Foos"
PLG_WEBSERVICES_FOOS_XML_DESCRIPTION="Used to add foos routes to the API for your website."
Geänderte Dateien
Komponente
administrator/components/com_foos/foos.xml
In der Installationsdatei ist wichtig, den Ordner api
aufzunehmen. Sonst werden die Dateien im Unterordner api
bei einer Installation nicht in das richtige Verzeichnis kopiert.
<folder>tmpl</folder>
</files>
</administration>
+ <api>
+ <files folder="api/components/com_foos">
+ <folder>src</folder>
+ </files>
+ </api>
<changelogurl>https://codeberg.org/astrid/j4examplecode/raw/branch/tutorial/changelog.xml</changelogurl>
<updateservers>
<server type="extension" name="Foo Updates">https://codeberg.org/astrid/j4examplecode/raw/branch/tutorial/foo_update.xml</server>
Verschiedenes
Öffentliche Api
Routen können durch Setzen eines Flags als öffentlich deklariert werden. Allerdings ist dies gefährlich und kann sensible Informationen preisgeben. Du kannst den öffentlichen Zugang bei der Registrierung der Route festlegen. Wenn Du Joomla\CMS\Router\ApiRouter::createCRUDRoutes()
verwendest, übergebe das vierte Argument mit true, um public GETs
zu aktivieren.
...
$router->createCRUDRoutes(
'v1/foos',
'foos',
['component' => 'com_foos'],
true
);
...
Oder wenn du die Route manuell instanziierst verwende Code analog zum nachfolgenden Beispiel.
...
$route = new Joomla\Router\Route(['GET'], 'v1/foos', 'foos.displayList', [], ['component' => 'com_foos', 'public' => true]);
$router->addRoute($route);
...
Weitere Informationen findest du im PR 27021[^github.com/joomla/joomla-cms/pull/27021] oder unter joomla.stackexchange.com[^joomla.stackexchange.com/questions/32320/joomla-api-and-credentials].
Joomla API und benutzerdefinierte URL
API Urls enthalten das Wort /api
. Zum Beispiel http://localhost/joomla-cms4/api/index.php/v1/foos
. Wenn du eine andere URL brauchst, ist es möglich, über die Datei .htaccess
umzuleiten.
Redirect 301 /yourCustomFolder /api/v1/foos/index.php/yourCustomFolder
Teste deine Joomla-Komponente
-
Führe eine neue Installation durch. Deinstalliere hierzu deine bisherige Installation und kopiere alle Dateien erneut. Kopiere die Dateien im
administrator
Ordner in denadministrator
Ordner deiner Joomla 4 Installation. Kopiere die Dateien imapi
Ordner in denapi
Ordner deiner Joomla 4 Installation. Kopiere die Dateien implugin
Ordner in denplugin
Ordner deiner Joomla 4 Installation. Installiere deine Komponente und das Plugin wie in Teil eins beschrieben, nachdem du alle Dateien kopiert hast. -
Aktiviere das Plugin
- Aktive das Plugin Basic Auth
- Die Schnittstelle bietet dir nun folgende Abfragemöglichkeiten:
Eine Liste von Foos: curl -X GET /api/index.php/v1/foos
Ein einzelnes Foo-Element: curl -X GET /api/index.php/v1/foos/{foo_id}
Lösche ein Foo Element: curl -X DELETE /api/index.php/v1/foos/{foo_id}
Bei den nachfolgenden Beispielen gehe ich davon aus, dass deine Installation unter http://localhost/joomla-cms4
befindet und dein Benutzer sowie dein Passwort adminadminadmin (Base64: YWRtaW5hZG1pbmFkbWluOmFkbWluYWRtaW5hZG1pbg==)
, lauten. Ändere diese Angaben gegebenenfalls.
curl.haxx.de
Für Curl ist es erforderlich, dass du das Passwort in Base64 umwandelst. Eine Website, die dir dies abnimmt, ist base64encode.org.
Nutzt du Curl[^curl.haxx.se]? Die folgende Abfrage listet dir alle Elemente auf:
curl --location --request GET 'http://localhost/joomla-cms4/api/index.php/v1/foos' \
--header 'Accept: application/vnd.api+json' \
--header 'Authorization: Basic YWRtaW5hZG1pbmFkbWluOmFkbWluYWRtaW5hZG1pbg=='
Das Ausgabeformat ist JSON und sieht beispielsweise wie folgt aus:
{
"links": {
"self": "http://localhost/joomla-cms4/api/index.php/v1/foos"
},
"data": [
{
"type": "foos",
"id": "2",
"attributes": {
"id": 2,
"name": "Astrid",
"catid": 8
}
},
{
"type": "foos",
"id": "3",
"attributes": {
"id": 3,
"name": "Elmar",
"catid": 0
}
},
{
"type": "foos",
"id": "1",
"attributes": {
"id": 1,
"name": "Nina",
"catid": 0
}
}
],
"meta": {
"total-pages": 1
}
}
Das Mitgeben der Anmeldeinformationen ist zwingend. Zusammen sieht der Aufruf in einer Konsole wie folgt aus:
$ curl -X GET -H 'Authorization: Basic YWRtaW5hZG1pbmFkbWluOmFkbWluYWRtaW5hZG1pbg==' -i 'http://localhost/joomla-cms4/api/index.php/v1/foos'
HTTP/1.1 200 OK
Date: Tue, 16 May 2023 20:34:34 GMT
Server: Apache/2.4.54 (Unix) OpenSSL/1.1.1o
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
cross-origin-opener-policy: same-origin
X-Powered-By: JoomlaAPI/1.0
Expires: Wed, 17 Aug 2005 00:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Upgrade: h2,h2c
Connection: Upgrade
Last-Modified: Tue, 16 May 2023 20:34:34 GMT
Transfer-Encoding: chunked
Content-Type: application/vnd.api+json; charset=utf-8
{"links":{"self":"http:\/\/localhost\/joomla-cms\/api\/index.php\/v1\/foos"},"data":[{"type":"foos","id":"2","attributes":{"id":2,"name":"Astrid","catid":8}},{"type":"foos","id":"3","attributes":{"id":3,"name":"Elmar","catid":0}},{"type":"foos","id":"1","attributes":{"id":1,"name":"Nina","catid":0}}],"meta":{"total-pages":1}}
postman.com
Nutzt du postman.com? Dann ist meine Kollektion[^github.com/astridx/boilerplate/blob/tutorial/tutorial/component/30/Content%20und%20Foos.postman_collection.json] unter Umständen hilfreich für dich. In ihr sind zusätzlich Abfragen für com_content
enthalten.
Sonstiges
Firefox Addon
Ich nutze gerne dieses Addon[^addons.mozilla.org/de/firefox/addon/restclient] in Firefox.
Links
Joomla API-Spezifikation[^docs.joomla.org/J4.x
]Integration in Weblinks[^github.com/joomla-extensions/weblinks/pull/407]