J4.x

Difference between revisions of "Adding an API to a Joomla Component"

From Joomla! Documentation

(Extra routing info and making difference between HTML and JSON views explicit.)
(13 intermediate revisions by 5 users not shown)
Line 1: Line 1:
 
<noinclude><languages /></noinclude>
 
<noinclude><languages /></noinclude>
 
<noinclude>{{Joomla version|version=4.0}}</noinclude>
 
<noinclude>{{Joomla version|version=4.0}}</noinclude>
<translate><!--T:1--> This page is intended to document how to integrate the API layer introduced in {{jver|4.0}} into your existing Joomla component. This assumes that you are using the default Joomla MVC layer</translate>
+
<translate><!--T:1--> This page is intended to document how to integrate the Web Services layer introduced in {{jver|4.0}} into an existing Joomla component. This assumes that you are using the default Joomla MVC layer.</translate>
  
  
<translate><!--T:2--> ''' API integration for weblinks extension as an example '''</translate>
+
<translate><!--T:2--> ''' Web Services integration for weblinks extension as an example '''</translate>
  
 
<translate><!--T:3--> Repository extension:</translate> https://github.com/joomla-extensions/weblinks
 
<translate><!--T:3--> Repository extension:</translate> https://github.com/joomla-extensions/weblinks
  
Pull request: https://github.com/joomla-extensions/weblinks/pull/407
+
<translate>Pull request:</translate> https://github.com/joomla-extensions/weblinks/pull/407
  
<translate>== First step == <!--T:4--></translate>
+
<translate><!--T:14--> ==Plugin Code==</translate>
  
1. <translate><!--T:5--> Create a folder</translate> '''''src/api'''''
+
<translate>
 +
The point of entry to your component from an API call is a plugin.
 +
 
 +
The plugin re-routes the API call into the API code that services the request.
 +
 
 +
In this example an API call to the weblinks component might be
 +
</translate>
 +
<source>
 +
(<translate>yourSite</translate>)/api/index.php/v1/weblinks
 +
</source>
 +
 
 +
<translate>
 +
This means your installation file is best represented as a package, pkg_weblinks in this case, so that it can contain not only your original component, but also the required plugin.
 +
</translate>
 +
 
 +
1. <translate><!--T:15--> Create the plugin folder</translate> '''''plugins/webservices/weblinks'''''.
 +
 
 +
<translate>
 +
Your plugin code goes in a subdirectory of the webservices directory under the plugins directory, in this example</translate> '''''plugins/webservices/weblinks'''''.
 +
[[File:Struct2.png.png|500px|thumb|center|File system structure]]
 +
 
 +
2. <translate><!--T:16--> In</translate> '''''weblinks.php'''''<translate>, create the class </translate> '''''PlgWebservicesWeblinks'''''.
 +
 
 +
<source lang="php">
 +
use Joomla\CMS\Plugin\CMSPlugin;
 +
use Joomla\CMS\Router\ApiRouter;
 +
 
 +
class PlgWebservicesWeblinks extends CMSPlugin
 +
{
 +
    public function onBeforeApiRoute(&$router)
 +
    {
 +
        $router->createCRUDRoutes('v1/weblinks', 'weblinks', ['component' => 'com_weblinks']);
 +
    }
 +
}
 +
</source>
 +
 
 +
<translate><!--T:17--> In the '''''onBeforeApiRoute''''' method, register all the routes needed for the webservice.
 +
 
 +
When Joomla receives an API call it loads the API router which then collects a list of endpoints it should be aware of by running the "onBeforeApiRoute" method in all enabled plugin classes that contain it. Then it can locate the API component relevant to the API endpoint that has been called.
 +
</translate>
 +
 
 +
<translate>The createCRUDRoutes method creates five routes; two GET routes for list and item data, and one POST, one PATCH, and one DELETE route. If you don't require this, simply create the routes directly here.</translate>
 +
 
 +
<translate><!--T:4--> == The API code == </translate>
 +
 
 +
<translate>
 +
The router specifies the path for the relevant API code.
 +
 
 +
This API code is in a folder named "api" off the site root. It is exactly analagous to the "admin" and "site" sections.
 +
 
 +
In your installation package this code should be included in your component installer because the Joomla installation process automatically creates a section for each installed component there, whether it has any API code or not.
 +
 
 +
The structure of this section has the same directory pattern as the other extension sections (ie components, modules, plugins).
 +
</translate>
 +
 
 +
1. <translate><!--T:5--> Create the API code folder</translate> '''''/api'''''.
  
 
[[File:Struct1.png|500px|thumb|center|File system structure]]
 
[[File:Struct1.png|500px|thumb|center|File system structure]]
  
2. <translate><!--T:6--> Create a class</translate> '''''WeblinksController'''''
+
2. <translate><!--T:6--> Create the controller class</translate> '''''WeblinksController'''''.
 +
 
 +
<translate>
 +
The controller is named in the second parameter of the "createCRUDRoutes" method in the plugin.
 +
 
 +
You can expose more than one output structure by registering more than one route, specifiying a different controller for each structure.
 +
</translate>
 +
 
 
<source lang="php">
 
<source lang="php">
 
use Joomla\CMS\MVC\Controller\ApiController;
 
use Joomla\CMS\MVC\Controller\ApiController;
  
class WeblinksController extends ApiController  
+
class WeblinksController extends ApiController
 
{
 
{
 
     protected $contentType = 'weblinks';
 
     protected $contentType = 'weblinks';
Line 30: Line 92:
 
<translate><!--T:7--> Override the following fields:</translate>
 
<translate><!--T:7--> Override the following fields:</translate>
  
  $contentType - <translate><!--T:8--> will be used as default for $modelName as well when outputting response as type object</translate>
+
  $contentType - <translate><!--T:8--> will be used as default for </translate>$modelName<translate> as well as when outputting response as type object</translate>
  $default_view - <translate><!--T:9--> will be used as default for $viewName</translate>
+
  $default_view - <translate><!--T:9--> will be used as default for </translate>$viewName
 +
 
 +
3. <translate><!--T:10--> The display class</translate> '''''JsonapiView.php'''''
 +
 
 +
<translate>
 +
Extending JsonApiView gives you the same kinds of features that HtmlView does in a usual component view, slightly changed to be appropriate for outputting Json.
  
3. <translate><!--T:10--> Create a class</translate> '''''JsonApiView.php'''''
+
NB Although the core class is JsonApiView (uppercase A for Api), your override must have a lowercase a.
 +
 
 +
The item and list fields to render, as shown below, specify which fields to include in the respective outputs.
 +
</translate>
  
 
<source lang="php">
 
<source lang="php">
 
use Joomla\CMS\MVC\View\JsonApiView as BaseApiView;
 
use Joomla\CMS\MVC\View\JsonApiView as BaseApiView;
  
class JsonApiView extends BaseApiView
+
class JsonapiView extends BaseApiView
 
{
 
{
 
     protected $fieldsToRenderItem = [
 
     protected $fieldsToRenderItem = [
Line 63: Line 133:
 
  $fieldsToRenderList - <translate><!--T:13--> array of fields for listing objects</translate>
 
  $fieldsToRenderList - <translate><!--T:13--> array of fields for listing objects</translate>
  
<translate>== Second step == <!--T:14--></translate>
+
<translate>Notice that this indicates a significant difference between JSON API views and HTML views. WIth HTML views you are used to having separate files and directories for displaying a list of items and displaying individual items. There isn't the same "display" difference for JSON, so one JsonapiView handles both types. That's why both fields lists are both shown in this one view file.</translate>
  
1. <translate><!--T:15--> Create a folder</translate> '''''plugins/webservices/weblinks'''''
+
3. <translate><!--T:18--> Create the manifest file, </translate> '''''weblinks.xml'''''
 
 
[[File:Struct2.png.png|500px|thumb|center|File system structure]]
 
 
 
2. <translate><!--T:16--> In the '''''weblinks.php''''', create the class</translate> '''''PlgWebservicesWeblinks'''''
 
<source lang="php">
 
use Joomla\CMS\Plugin\CMSPlugin;
 
use Joomla\CMS\Router\ApiRouter;
 
 
 
class PlgWebservicesWeblinks extends CMSPlugin
 
{
 
    public function onBeforeApiRoute(&$router)
 
    {
 
        $router->createCRUDRoutes('v1/weblinks', 'weblinks', ['component' => 'com_weblinks']);
 
    }
 
}
 
</source>
 
 
 
<translate><!--T:17--> In the '''''onBeforeApiRoute''''' method, register all the routes that we need for webservice.</translate>
 
 
 
3. <translate><!--T:18--> Create</translate> '''''weblinks.xml'''''
 
 
<source lang="xml">
 
<source lang="xml">
 
<?xml version="1.0" encoding="utf-8"?>
 
<?xml version="1.0" encoding="utf-8"?>
 
<extension version="3.1" type="plugin" group="webservices" method="upgrade">
 
<extension version="3.1" type="plugin" group="webservices" method="upgrade">
     <name>plg_webservices_weblinks</name>
+
     <name>PLG_WEBSERVICES_WEBLINKS</name>
 
     <author>Joomla! Project</author>
 
     <author>Joomla! Project</author>
 
     <creationDate>August 2017</creationDate>
 
     <creationDate>August 2017</creationDate>
Line 107: Line 157:
 
</source>
 
</source>
  
4. <translate><!--T:19--> Create files</translate> '''''en-GB/en-GB.plg_webservices_weblinks.ini''''', '''''en-GB/en-GB.plg_webservices_weblinks.sys.ini''''' <translate><!--T:20--> with next content:</translate>
+
4. <translate><!--T:19--> Create language files</translate> <translate><!--T:20--> with content</translate>
 +
 
 +
<translate>As usual, language files can, and should, be supplied; </translate>'''''en-GB/plg_webservices_weblinks.ini''''', '''''en-GB/plg_webservices_weblinks.sys.ini'''''<translate>.  It may be unnecessary to provide the user language file (".ini"), but content for the system file (".sys.ini") is required, if only for the plugin administration list.</translate>
 +
 
 
<source lang="php">
 
<source lang="php">
 
; Joomla! Project
 
; Joomla! Project
Line 115: Line 168:
  
 
PLG_WEBSERVICES_WEBLINKS="Web Services - Weblinks"
 
PLG_WEBSERVICES_WEBLINKS="Web Services - Weblinks"
PLG_WEBSERVICES_WEBLINKS_XML_DESCRIPTION="Used to add weblinks routes to the API for your website."
+
PLG_WEBSERVICES_WEBLINKS_XML_DESCRIPTION="Used to add weblinks routes to the Web Services API for your website."
 
</source>
 
</source>
  
<translate>== Third step  == <!--T:21--></translate>
+
<translate><!--T:21--> == Packaging ==</translate>
1. <translate><!--T:22--> In the file</translate> '''''src/administrator/components/com_weblinks/weblinks.xml''''' <translate><!--T:23--> add a description for the API files</translate>
+
<translate>There are two considerations for packaging, the code in the api section and the plugin.</translate>
 +
<translate>=== Component Packaging ===</translate>
 +
<translate>As mentioned elsewhere, the api code is packaged with the component code. To include the code in the package, add the following section to your component manifest</translate>
 
<source lang="xml">
 
<source lang="xml">
 
<api>
 
<api>
     <files folder="api/components/com_weblinks">
+
     <files folder="api">
      ##API_COMPONENT_FILES##
+
        <folder>src</folder>
 
     </files>
 
     </files>
</api>
+
</api></source>
</source>
+
<translate>This will create a section for your api code in the correctly named (for your component) folder under the "api" folder.</translate>
 +
<translate>===Plugin Packaging ===</translate>
 +
<translate><!--T:24--> The manifest file for the plugin itself is shown above.  Since the plugin is an integral part of the component now, it is best to package the two together. Similar to a single extension installation file, a package installation file contains each extension in the package, already compressed, and a package manifest file.  In the case of this example, the manifest file would look something like this (some fields omitted): </translate>
 +
<source lang="xml">
 +
<?xml version="1.0" encoding="utf-8"?>
 +
<!--~
 +
  ~ @package  Weblinks
 +
    ....
 +
  -->
 +
 
 +
<extension version="4.0.0" type="package" method="upgrade">
 +
    <name>Weblinks</name>
 +
    <packagename>weblinks</packagename>
 +
    <version>x.y.z</version>
 +
    <!-- etc etc -->
 +
 
 +
    <!-- List of extensions to install -->
 +
    <files>
 +
        <!-- Component -->
 +
        <file type="component" id="com_weblinks">com_weblinks.zip</file>
  
2. <translate><!--T:24--> In the file</translate>  '''''src/administrator/manifests/packages/pkg_weblinks.xml''''' <translate><!--T:25--> add a description for webservice plugin</translate>
+
        <!-- Plugins: web services -->
<source lang="xml">
+
        <file type="plugin" id="weblinks" group="webservices">plg_webservices_weblinks.zip</file>
<files>
+
    </files>
    ...
+
</extension>
    <file type="plugin" id="weblinks" group="webservices">plg_webservices_weblinks.zip</file>
 
</files>
 
 
</source>
 
</source>
  
== Categories ==
+
<translate><!--T:51--> == Categories == </translate>
1. Add categories support for weblinks webservice. Edit the file '''''src/plugins/webservices/weblinks/weblinks.php'''''
+
1. <translate><!--T:52--> Add categories support for weblinks webservice. Edit the file '''''src/plugins/webservices/weblinks/weblinks.php'''''</translate>
 
<source lang="php">
 
<source lang="php">
 
class PlgWebservicesWeblinks extends CMSPlugin
 
class PlgWebservicesWeblinks extends CMSPlugin
Line 153: Line 225:
 
</source>
 
</source>
  
<translate><!--T:26--> We use the ready-made component '''''com_categories''''', just need to pass the parameter 'extension'</translate> => 'com_weblinks'
+
<translate><!--T:26--> We use the ready-made component '''''com_categories''''', and just need to pass the parameter</translate> 'extension' => 'com_weblinks'
  
<translate>== Fields == <!--T:27--></translate>
+
<translate><!--T:27--> == Fields and Groups == </translate>
 
1. <translate><!--T:28--> Add fields and fields groups support for weblinks webservice. Edit the file</translate> '''''src/plugins/webservices/weblinks/weblinks.php'''''
 
1. <translate><!--T:28--> Add fields and fields groups support for weblinks webservice. Edit the file</translate> '''''src/plugins/webservices/weblinks/weblinks.php'''''
 
<source lang="php">
 
<source lang="php">
Line 178: Line 250:
 
</source>
 
</source>
  
2. <translate><!--T:29--> Override the function</translate> '''''save''''' <translate><!--T:30--> in</translate> '''''WeblinksController'''''
+
2. <translate><!--T:29--> Override the function</translate> '''''save''''' <translate><!--T:30--> in</translate> '''''WeblinksController'''''.
 
<source lang="php">
 
<source lang="php">
 
class WeblinksController extends ApiController
 
class WeblinksController extends ApiController
Line 208: Line 280:
 
</source>
 
</source>
  
3. <translate><!--T:31--> Override the functions</translate> '''''displayList, displayItem, prepareItem''''' <translate><!--T:32--> in</translate> '''''Weblinks\JsonApiView'''''
+
3. <translate><!--T:31--> Override the functions</translate> '''''displayList, displayItem, prepareItem''''' <translate><!--T:32--> in</translate> '''''Weblinks\JsonApiView'''''.
  
 
<source lang="php">
 
<source lang="php">
Line 249: Line 321:
 
</source>
 
</source>
  
<translate><!--T:33--> Pay attention in function prepareItem to <source lang="php">$field->apivalue</source> If the type of the field is complex, we hope that it will return a value for output in the API component, otherwise we will take </translate>
+
<translate><!--T:33--> Pay attention in function prepareItem to <source lang="php">$field->apivalue</source>. If the type of the field is complex, we hope that it will return a value for output in the Web Services API component, otherwise we will take </translate>
 
<source lang="php">$field->rawvalue</source>
 
<source lang="php">$field->rawvalue</source>
 +
 +
<translate>== Data ==</translate>
 +
 +
<translate>The core functionality renders the data that you would expect from a simple component in the normal web interface.  For this, the API defaults to administration data models.  However, providing a "Model" section in your API code gives you back the flexibility to construct whatever data shape you want just for your API output. Provide a model for each view with the usual naming conventions.
 +
</translate>
 +
 +
<translate>
 +
The system JsonApiView class formats the Json output as three data sets; links, items, and metadata.  To change that, override the display class in your own JsonapiView and *don't* call the parent class.
 +
</translate>
  
 
<translate>== Integration Work Example == <!--T:34--></translate>
 
<translate>== Integration Work Example == <!--T:34--></translate>
Line 266: Line 347:
  
 
<translate>==== Create Weblink ==== <!--T:40--></translate>
 
<translate>==== Create Weblink ==== <!--T:40--></translate>
curl -X POST -H "Content-Type: application/json" /api/index.php/v1/weblinks -d
 
 
<source lang="javascript">
 
<source lang="javascript">
{
+
curl -X POST -H "Content-Type: application/json" /api/index.php/v1/weblinks -d
 +
'{
 
     "access": "1",
 
     "access": "1",
 
     "alias": "",
 
     "alias": "",
Line 300: Line 381:
 
     "url": "http://somelink.com/",
 
     "url": "http://somelink.com/",
 
     "xreference": "xreference"
 
     "xreference": "xreference"
}
+
}'</source>
</source>
 
  
 
<translate>==== Update Weblink ==== <!--T:41--></translate>
 
<translate>==== Update Weblink ==== <!--T:41--></translate>
curl -X PUT -H "Content-Type: application/json" /api/index.php/v1/weblinks/{weblink_id} -d
 
 
<source lang="javascript">
 
<source lang="javascript">
{
+
curl -X PUT -H "Content-Type: application/json" /api/index.php/v1/weblinks/{weblink_id} -d
 +
'{
 
     "catid": "8",
 
     "catid": "8",
 
     "description": "<p>some new text</p>",
 
     "description": "<p>some new text</p>",
Line 312: Line 392:
 
     "title": "new title",
 
     "title": "new title",
 
     "url": "http://newsomelink.com/"
 
     "url": "http://newsomelink.com/"
}
+
}'</source>
</source>
 
  
 
<translate>=== Categories === <!--T:42--></translate>
 
<translate>=== Categories === <!--T:42--></translate>
 
<translate><!--T:43--> Route Weblinks Categories is:</translate> "v1/weblinks/categories"
 
<translate><!--T:43--> Route Weblinks Categories is:</translate> "v1/weblinks/categories"
  
<translate><!--T:44--> Working with it is similar to</translate> [[J4.x:Joomla_Core_APIs#Categories|Banners Categories]].
+
<translate><!--T:44--> Working with it is similar to [[S:MyLanguage/J4.x:Joomla_Core_APIs#Categories|Banners Categories]].</translate>
  
 
<translate>=== Fields === <!--T:45--></translate>
 
<translate>=== Fields === <!--T:45--></translate>
Line 324: Line 403:
 
<translate><!--T:46--> Route Fields Weblinks is:</translate> "v1/fields/weblinks"
 
<translate><!--T:46--> Route Fields Weblinks is:</translate> "v1/fields/weblinks"
  
<translate><!--T:47--> Working with it is similar to</translate> [[J4.x:Joomla_Core_APIs#Fields_Contact|Fields Contact]].
+
<translate><!--T:47--> Working with it is similar to [[S:MyLanguage/J4.x:Joomla_Core_APIs#Fields_Contact|Fields Contact]].</translate>
  
 
<translate>=== Groups Fields === <!--T:48--></translate>
 
<translate>=== Groups Fields === <!--T:48--></translate>
Line 330: Line 409:
 
<translate><!--T:49--> Route Groups Fields Weblinks is:</translate> "v1/fields/groups/weblinks"
 
<translate><!--T:49--> Route Groups Fields Weblinks is:</translate> "v1/fields/groups/weblinks"
  
<translate><!--T:50--> Working with it is similar to</translate> [[J4.x:Joomla_Core_APIs#Groups_Fields_Contact|Groups Fields Contact]].
+
<translate><!--T:50--> Working with it is similar to [[S:MyLanguage/J4.x:Joomla_Core_APIs#Groups_Fields_Contact|Groups Fields Contact]].</translate>
 
 
  
 
<noinclude>
 
<noinclude>
 
[[Category:Development{{#translation:}}]]
 
[[Category:Development{{#translation:}}]]
 
[[Category:Component_Development{{#translation:}}]]
 
[[Category:Component_Development{{#translation:}}]]
[[Category:Joomla!_Api{{#translation:}}]]
+
[[Category:Joomla!_4.x{{#translation:}}]]
 
[[Category:Joomla! 4.x Web Services{{#translation:}}]]
 
[[Category:Joomla! 4.x Web Services{{#translation:}}]]
[[Category:Joomla!_4.x{{#translation:}}]]
 
 
</noinclude>
 
</noinclude>

Revision as of 04:00, 25 May 2022

Other languages:
Deutsch • ‎English • ‎français
Joomla! 
4.0

This page is intended to document how to integrate the Web Services layer introduced in Joomla 4.0 into an existing Joomla component. This assumes that you are using the default Joomla MVC layer.


Web Services integration for weblinks extension as an example

Repository extension: https://github.com/joomla-extensions/weblinks

Pull request: https://github.com/joomla-extensions/weblinks/pull/407

Plugin Code[edit]

The point of entry to your component from an API call is a plugin.

The plugin re-routes the API call into the API code that services the request.

In this example an API call to the weblinks component might be

(yourSite)/api/index.php/v1/weblinks

This means your installation file is best represented as a package, pkg_weblinks in this case, so that it can contain not only your original component, but also the required plugin.

1. Create the plugin folder plugins/webservices/weblinks.

Your plugin code goes in a subdirectory of the webservices directory under the plugins directory, in this example plugins/webservices/weblinks.

File system structure

2. In weblinks.php, create the class PlgWebservicesWeblinks.

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Router\ApiRouter;

class PlgWebservicesWeblinks extends CMSPlugin
{
    public function onBeforeApiRoute(&$router)
    {
        $router->createCRUDRoutes('v1/weblinks', 'weblinks', ['component' => 'com_weblinks']);
    }
}

In the onBeforeApiRoute method, register all the routes needed for the webservice.

When Joomla receives an API call it loads the API router which then collects a list of endpoints it should be aware of by running the "onBeforeApiRoute" method in all enabled plugin classes that contain it. Then it can locate the API component relevant to the API endpoint that has been called.

The createCRUDRoutes method creates five routes; two GET routes for list and item data, and one POST, one PATCH, and one DELETE route. If you don't require this, simply create the routes directly here.

The API code[edit]

The router specifies the path for the relevant API code.

This API code is in a folder named "api" off the site root. It is exactly analagous to the "admin" and "site" sections.

In your installation package this code should be included in your component installer because the Joomla installation process automatically creates a section for each installed component there, whether it has any API code or not.

The structure of this section has the same directory pattern as the other extension sections (ie components, modules, plugins).

1. Create the API code folder /api.

File system structure

2. Create the controller class WeblinksController.

The controller is named in the second parameter of the "createCRUDRoutes" method in the plugin.

You can expose more than one output structure by registering more than one route, specifiying a different controller for each structure.

use Joomla\CMS\MVC\Controller\ApiController;

class WeblinksController extends ApiController
{
    protected $contentType = 'weblinks';

    protected $default_view = 'weblinks';
}

Override the following fields:

$contentType - will be used as default for $modelName as well as when outputting response as type object
$default_view - will be used as default for $viewName

3. The display class JsonapiView.php

Extending JsonApiView gives you the same kinds of features that HtmlView does in a usual component view, slightly changed to be appropriate for outputting Json.

NB Although the core class is JsonApiView (uppercase A for Api), your override must have a lowercase a.

The item and list fields to render, as shown below, specify which fields to include in the respective outputs.

use Joomla\CMS\MVC\View\JsonApiView as BaseApiView;

class JsonapiView extends BaseApiView
{
    protected $fieldsToRenderItem = [
        'id',
        'catid',
        'title',
        'alias',
        'url',
        'xreference',
        'tags',
    ];

    protected $fieldsToRenderList = [
        'id',
        'title',
        'alias',
    ];
}

Override the following fields:

$fieldsToRenderItem - array of fields to display a single object
$fieldsToRenderList - array of fields for listing objects

Notice that this indicates a significant difference between JSON API views and HTML views. WIth HTML views you are used to having separate files and directories for displaying a list of items and displaying individual items. There isn't the same "display" difference for JSON, so one JsonapiView handles both types. That's why both fields lists are both shown in this one view file.

3. Create the manifest file, weblinks.xml

<?xml version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin" group="webservices" method="upgrade">
    <name>PLG_WEBSERVICES_WEBLINKS</name>
    <author>Joomla! Project</author>
    <creationDate>August 2017</creationDate>
    <copyright>(C) 2005 - 2019 Open Source Matters. All rights reserved.</copyright>
    <license>GNU General Public License version 2 or later; see LICENSE.txt</license>
    <authorEmail>admin@joomla.org</authorEmail>
    <authorUrl>www.joomla.org</authorUrl>
    <version>4.0.0</version>
    <description>PLG_WEBSERVICES_WEBLINKS_XML_DESCRIPTION</description>
    <files>
         ##FILES##
    </files>
    <languages folder="administrator/language">
         ##LANGUAGE_FILES##
    </languages>
</extension>

4. Create language files with content

As usual, language files can, and should, be supplied; en-GB/plg_webservices_weblinks.ini, en-GB/plg_webservices_weblinks.sys.ini. It may be unnecessary to provide the user language file (".ini"), but content for the system file (".sys.ini") is required, if only for the plugin administration list.

; Joomla! Project
; Copyright (C) 2005 - 2019 Open Source Matters. All rights reserved.
; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php
; Note : All ini files need to be saved as UTF-8

PLG_WEBSERVICES_WEBLINKS="Web Services - Weblinks"
PLG_WEBSERVICES_WEBLINKS_XML_DESCRIPTION="Used to add weblinks routes to the Web Services API for your website."

Packaging[edit]

There are two considerations for packaging, the code in the api section and the plugin.

Component Packaging[edit]

As mentioned elsewhere, the api code is packaged with the component code. To include the code in the package, add the following section to your component manifest

<api>
    <files folder="api">
        <folder>src</folder>
    </files>
</api>

This will create a section for your api code in the correctly named (for your component) folder under the "api" folder.

Plugin Packaging[edit]

The manifest file for the plugin itself is shown above. Since the plugin is an integral part of the component now, it is best to package the two together. Similar to a single extension installation file, a package installation file contains each extension in the package, already compressed, and a package manifest file. In the case of this example, the manifest file would look something like this (some fields omitted):

<?xml version="1.0" encoding="utf-8"?>
<!--~
  ~ @package   Weblinks
    ....
  -->

<extension version="4.0.0" type="package" method="upgrade">
    <name>Weblinks</name>
    <packagename>weblinks</packagename>
    <version>x.y.z</version>
    <!-- etc etc -->

    <!-- List of extensions to install -->
    <files>
        <!-- Component -->
        <file type="component" id="com_weblinks">com_weblinks.zip</file>

        <!-- Plugins: web services -->
        <file type="plugin" id="weblinks" group="webservices">plg_webservices_weblinks.zip</file>
    </files>
</extension>

Categories[edit]

1. Add categories support for weblinks webservice. Edit the file src/plugins/webservices/weblinks/weblinks.php

class PlgWebservicesWeblinks extends CMSPlugin
{
    public function onBeforeApiRoute(&$router)
    {
        ...
        $router->createCRUDRoutes(
            'v1/weblinks/categories',
            'categories',
            ['component' => 'com_categories', 'extension' => 'com_weblinks']
        );
    }
}

We use the ready-made component com_categories, and just need to pass the parameter 'extension' => 'com_weblinks'

Fields and Groups[edit]

1. Add fields and fields groups support for weblinks webservice. Edit the file src/plugins/webservices/weblinks/weblinks.php

class PlgWebservicesWeblinks extends CMSPlugin
{
    public function onBeforeApiRoute(&$router)
    {
        ...
        $router->createCRUDRoutes(
            'v1/fields/weblinks',
            'fields',
            ['component' => 'com_fields', 'context' => 'com_weblinks.weblink']
        );

        $router->createCRUDRoutes(
            'v1/fields/groups/weblinks',
            'groups',
            ['component' => 'com_fields', 'context' => 'com_weblinks.weblink']
        );
    }
}

2. Override the function save in WeblinksController.

class WeblinksController extends ApiController
{
    ...

    protected function save($recordKey = null)
    {
        $data = (array) json_decode($this->input->json->getRaw(), true);

        foreach (FieldsHelper::getFields('com_weblinks.weblink') 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);
    }

    ...
}

3. Override the functions displayList, displayItem, prepareItem in Weblinks\JsonApiView.

class JsonApiView extends BaseApiView
{
    ...

    public function displayList(array $items = null)
    {
        foreach (FieldsHelper::getFields('com_weblinks.weblink') as $field)
        {
            $this->fieldsToRenderList[] = $field->name;
        }

        return parent::displayList();
    }

    public function displayItem($item = null)
    {
        foreach (FieldsHelper::getFields('com_weblinks.weblink') as $field)
        {
            $this->fieldsToRenderItem[] = $field->name;
        }

        return parent::displayItem();
    }

    protected function prepareItem($item)
    {
        foreach (FieldsHelper::getFields('com_weblinks.weblink', $item, true) as $field)
        {
            $item->{$field->name} = isset($field->apivalue) ? $field->apivalue : $field->rawvalue;
        }

        return parent::prepareItem($item);
    }

    ...
}

Pay attention in function prepareItem to

$field->apivalue

. If the type of the field is complex, we hope that it will return a value for output in the Web Services API component, otherwise we will take

$field->rawvalue

Data[edit]

The core functionality renders the data that you would expect from a simple component in the normal web interface. For this, the API defaults to administration data models. However, providing a "Model" section in your API code gives you back the flexibility to construct whatever data shape you want just for your API output. Provide a model for each view with the usual naming conventions.

The system JsonApiView class formats the Json output as three data sets; links, items, and metadata. To change that, override the display class in your own JsonapiView and *don't* call the parent class.

Integration Work Example[edit]

NOTE: Remember to enable weblinks webservice plugin!

Weblinks[edit]

Get List of Weblinks[edit]

curl -X GET /api/index.php/v1/weblinks

Get Single Weblink[edit]

curl -X GET /api/index.php/v1/weblinks/{weblink_id}

Delete Weblink[edit]

curl -X DELETE /api/index.php/v1/weblinks/{weblink_id}

Create Weblink[edit]

curl -X POST -H "Content-Type: application/json" /api/index.php/v1/weblinks -d
'{
    "access": "1",
    "alias": "",
    "catid": "8",
    "description": "<p>text</p>",
    "images": {
        "float_first": "",
        "float_second": "",
        "image_first": "",
        "image_first_alt": "",
        "image_first_caption": "",
        "image_second": "",
        "image_second_alt": "",
        "image_second_caption": ""
    },
    "language": "*",
    "metadata": {
        "rights": "",
        "robots": ""
    },
    "metadesc": "",
    "metakey": "",
    "modified": "",
    "params": {
        "count_clicks": "",
        "height": "",
        "target": "",
        "width": ""
    },
    "title": "weblink title",
    "url": "http://somelink.com/",
    "xreference": "xreference"
}'

Update Weblink[edit]

curl -X PUT -H "Content-Type: application/json" /api/index.php/v1/weblinks/{weblink_id} -d
'{
    "catid": "8",
    "description": "<p>some new text</p>",
    "language": "*",
    "title": "new title",
    "url": "http://newsomelink.com/"
}'

Categories[edit]

Route Weblinks Categories is: "v1/weblinks/categories"

Working with it is similar to Banners Categories.

Fields[edit]

Route Fields Weblinks is: "v1/fields/weblinks"

Working with it is similar to Fields Contact.

Groups Fields[edit]

Route Groups Fields Weblinks is: "v1/fields/groups/weblinks"

Working with it is similar to Groups Fields Contact.