J4.x

Adding an API to a Joomla Component

From Joomla! Documentation

Revision as of 18:20, 24 August 2019 by Samoylov (talk | contribs) (→‎Fields)

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


API 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

First step[edit]

1. Create a folder src/api

File system structure

2. Create a class WeblinksController

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 when outputting response as type object
$default_view - will be used as default for $viewName

3. Create a class JsonApiView.php

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

Second step[edit]

1. Create a folder plugins/webservices/weblinks

File system structure

2. In the 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 that we need for webservice.

3. Create 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 files en-GB/en-GB.plg_webservices_weblinks.ini, en-GB/en-GB.plg_webservices_weblinks.sys.ini with next content:

; 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 API for your website."

Third step[edit]

1. In the file src/administrator/components/com_weblinks/weblinks.xml add a description for the API files

<api>
    <files folder="api/components/com_weblinks">
       ##API_COMPONENT_FILES##
    </files>
</api>

2. In the file src/administrator/manifests/packages/pkg_weblinks.xml add a description for webservice plugin

<files>
    ...
    <file type="plugin" id="weblinks" group="webservices">plg_webservices_weblinks.zip</file>
</files>

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, just need to pass the parameter 'extension' => 'com_weblinks'

Fields[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 API component, otherwise we will take

$field->rawvalue

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.