Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Documentation/Cors/Index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ We are not going here to explain what the CORS is. There is plenty of websites e

CORS configuration in t3api is based and (almost) fully compatible with well known Symfony bundle `nelmio/cors-bundle <https://github.com/nelmio/NelmioCorsBundle>`__.

In code below there is a list of all supported configuration options and their default values. If you would like to change these values to custom ones, you should use ``ext_localconf.php`` file of your extension (``typo3conf/ext/my_custom_ext/ext_localconf.php``).
In code below there is a list of all supported configuration options and their default values. If you would like to change these values to custom ones, you should use ``ext_localconf.php`` file of your extension (``EXT:my_custom_ext/ext_localconf.php``).

.. code-block:: php

Expand Down
2 changes: 1 addition & 1 deletion Documentation/Customization/ApiResourcePath/Index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ and registered in :file:`ext_localconf.php` like this:
\SourceBroker\T3api\Provider\ApiResourcePath\LoadedExtensionsDomainModelApiResourcePathProvider::class,
];

The same way you can add your own providers for additional patches.
The same way you can add your own providers (implementing ``\SourceBroker\T3api\Provider\ApiResourcePath\ApiResourcePathProvider``) for additional paths.
134 changes: 133 additions & 1 deletion Documentation/Customization/CollectionResponseSchema/Index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,136 @@
Collection response schema
===========================

@todo - write docs
By default collection ``GET`` operations return response in `Hydra <https://www.hydra-cg.com/spec/latest/core/>`__ format, built by ``\SourceBroker\T3api\Response\HydraCollectionResponse``:

.. code-block:: json

{
"hydra:member": [ ... ],
"hydra:totalItems": 6,
"hydra:view": {
"hydra:first": "/_api/news/news?page=1",
"hydra:last": "/_api/news/news?page=1",
"hydra:pages": ["/_api/news/news?page=1"],
"hydra:page": 1
},
"hydra:search": {
"hydra:template": "/_api/news/news{?order[uid],order[title],order[datetime],istopnews,uid,datetime,pid,search}",
"hydra:mapping": [
{"variable": "order[uid]", "property": "uid"},
...
]
}
}

- ``hydra:member`` - items of the current page.
- ``hydra:totalItems`` - number of all items matching the filters (not only on current page).
- ``hydra:view`` - pagination links, see :ref:`pagination`.
- ``hydra:search`` - URI template with all filters available for the endpoint.

Class used to build collection responses is configured globally:

.. code-block:: php

$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['collectionResponseClass'] = \SourceBroker\T3api\Response\HydraCollectionResponse::class;

Custom response class
======================

To change the schema (e.g. to match what your frontend expects) create a class which extends ``\SourceBroker\T3api\Response\AbstractCollectionResponse`` and register it in ``ext_localconf.php``. Abstract class gives you ``getMembers()`` (already paginated) and ``getTotalItems()``. The only method you have to implement is ``getOpenApiSchema()`` which describes the response in the generated OpenAPI documentation.

.. code-block:: php

<?php

namespace Vendor\Extension\Response;

use SourceBroker\T3api\OpenApi\Objects\Schema;
use SourceBroker\T3api\Response\AbstractCollectionResponse;

class SimpleCollectionResponse extends AbstractCollectionResponse
{
public static function getOpenApiSchema(string $membersReference): Schema
{
return Schema::object()
->properties(
Schema::array('items')->items(Schema::ref($membersReference)),
Schema::integer('total')->minimum(0),
Schema::integer('page')->minimum(1)
);
}

public function getPage(): int
{
return $this->operation->getPagination()->setParametersFromRequest($this->request)->getPage();
}
}

.. code-block:: php

// ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['collectionResponseClass'] = \Vendor\Extension\Response\SimpleCollectionResponse::class;

Response object is serialized with JMS serializer like any other object, so it is needed to tell the serializer which methods should be exposed. Do it with :ref:`YAML metadata <serialization_yaml-metadata>` (file ``Vendor.Extension.Response.SimpleCollectionResponse.yml`` in one of the ``serializerMetadataDirs``). Internal properties of ``AbstractCollectionResponse`` (``operation``, ``query``, ``request`` etc.) are already excluded by t3api.

.. code-block:: yaml

Vendor\Extension\Response\SimpleCollectionResponse:
virtual_properties:
getMembers:
serialized_name: items
type: array
groups: ['__simple_collection_response']
getTotalItems:
serialized_name: total
type: int
groups: ['__simple_collection_response']
getPage:
serialized_name: page
type: int
groups: ['__simple_collection_response']

Collection operations are usually serialized with ``normalizationContext`` groups (see :ref:`serialization_context-groups`), so the group used above has to be added to the serialization context. ``HydraCollectionResponse`` does the same with group ``__hydra_collection_response`` (see ``AddHydraCollectionResponseSerializationGroupEventListener``). Use ``AfterCreateContextForOperationEvent`` (see :ref:`events`):

.. code-block:: php

<?php

namespace Vendor\Extension\EventListener;

use SourceBroker\T3api\Domain\Model\CollectionOperation;
use SourceBroker\T3api\Event\AfterCreateContextForOperationEvent;

class AddSimpleCollectionResponseGroupEventListener
{
public function __invoke(AfterCreateContextForOperationEvent $event): void
{
$context = $event->getContext();

if (
$event->getOperation() instanceof CollectionOperation
&& $event->getOperation()->isMethodGet()
&& $context->hasAttribute('groups')
) {
$context->setGroups(array_merge(
$context->getAttribute('groups'),
['__simple_collection_response']
));
}
}
}

.. code-block:: yaml

# Configuration/Services.yaml
services:
Vendor\Extension\EventListener\AddSimpleCollectionResponseGroupEventListener:
tags:
- name: event.listener
event: SourceBroker\T3api\Event\AfterCreateContextForOperationEvent

.. admonition:: Real examples. Run "ddev restart && ddev ci 14" and try those links below.

* | Default Hydra collection response with pagination (``hydra:view``) and available filters (``hydra:search``):
| `https://14.t3api.ddev.site/_api/news/news?itemsPerPage=2 <https://14.t3api.ddev.site/_api/news/news?itemsPerPage=2>`__
|
8 changes: 4 additions & 4 deletions Documentation/Customization/ExpressionLanguage/Index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
Expression Language
=====================

`Symfony expression language <https://github.com/symfony/expression-language>`__ is `widely used in TYPO3 core <https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/ApiOverview/SymfonyExpressionLanguage/Index.html>`__. T3api also uses it in two places:
`Symfony expression language <https://github.com/symfony/expression-language>`__ is `widely used in TYPO3 core <https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/SymfonyExpressionLanguage/Index.html>`__. T3api also uses it in two places:

- :ref:`Serialization (and deserialization) <serialization>`
- :ref:`Security (checking access to operations and filters) <security>`

T3api utilizes TYPO3 core expression language feature. So, if you would like to extend expression language used in t3api core, you need to register new provider in `the same way as you do it e.g. for TS <https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/ApiOverview/SymfonyExpressionLanguage/Index.html#registering-new-provider-within-an-extension>`__. Just notice that appropriate context has to be specified as array key (in this case it's ``t3api``). Code below shows how to register ``MyCustomProvider`` in ``t3api`` context.
T3api utilizes TYPO3 core expression language feature. So, if you would like to extend expression language used in t3api core, you need to register new provider in `the same way as you do it e.g. for TS <https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/SymfonyExpressionLanguage/Index.html#registering-new-provider-within-an-extension>`__. Just notice that appropriate context has to be specified as array key (in this case it's ``t3api``). Code below shows how to register ``MyCustomProvider`` in ``t3api`` context.

.. code-block:: php
:caption: typo3conf/ext/my_ext/Configuration/ExpressionLanguage.php
:caption: EXT:my_ext/Configuration/ExpressionLanguage.php

return [
't3api' => [
\Vendor\MyExt\ExpressionLanguage\MyCustomProvider::class,
],
];

After registration of custom provider follow TYPO3 documentation how to `add additional variables <https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/ApiOverview/SymfonyExpressionLanguage/Index.html#additional-variables>`__ and `additional functions <https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/ApiOverview/SymfonyExpressionLanguage/Index.html#additional-functions>`__ into expression language. Now you can use your custom variables and functions inside t3api security check or serialization and deserialization expressions.
After registration of custom provider follow TYPO3 documentation how to `add additional variables <https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/SymfonyExpressionLanguage/Index.html#additional-variables>`__ and `additional functions <https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/SymfonyExpressionLanguage/Index.html#additional-functions>`__ into expression language. Now you can use your custom variables and functions inside t3api security check or serialization and deserialization expressions.
16 changes: 12 additions & 4 deletions Documentation/Events/Index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ An event that is dispatched after request payload is deserialized to objects.
May be useful when it is needed e.g. to change datetime property to current or
assign current TYPO3 user as an author. This event gives access to following data:

- ``operation`` - instance of ``\SourceBroker\T3api\HydraCollectionResponseomain\Model\OperationInterface``
- ``operation`` - instance of ``\SourceBroker\T3api\Domain\Model\OperationInterface``
- ``object`` - object deserialized from payload

Example event registration in :file:`Services.yaml`:
Expand All @@ -39,7 +39,7 @@ Example implementation:
public function __invoke(AfterDeserializeOperationEvent $event): void
{
$operation = $event->getOperation();
$object => $event->getObject();
$object = $event->getObject();
...
$event->setObject($object);
}
Expand Down Expand Up @@ -97,7 +97,7 @@ properties in API response). This event gives access to following data:

- ``operation`` - instance of ``\SourceBroker\T3api\Domain\Model\OperationInterface``
- ``request`` - instance of ``Symfony\Component\HttpFoundation\Request``
- ``context`` - instance of ``JMS\Serializer\SerializationContext``
- ``context`` - instance of ``JMS\Serializer\SerializationContext`` (or ``JMS\Serializer\DeserializationContext`` when context for deserialization of request payload is created)

Example event registration in :file:`Services.yaml`:

Expand Down Expand Up @@ -140,7 +140,7 @@ There are 3 events dispatched before evaluation of security expressions.

.. note::

Before using these events consider if `build-in TYPO3 mechanism to extend expression language <https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/ApiOverview/SymfonyExpressionLanguage/Index.html>`__
Before using these events consider if `build-in TYPO3 mechanism to extend expression language <https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/SymfonyExpressionLanguage/Index.html>`__
won't be a better solution. Keep in mind that registering variables within these
events will make it available only in t3api context while using TYPO3 mechanism makes
it possible to be used also in other places (e.g. TypoScript conditions).
Expand Down Expand Up @@ -254,3 +254,11 @@ Example implementation:
...
}
}


Record updated / deleted
========================

``\SourceBroker\T3api\Event\RecordUpdatedEvent`` and ``\SourceBroker\T3api\Event\RecordDeletedEvent`` are used
to invalidate the :ref:`response cache <response-cache>` when records are changed. You can also dispatch them yourself,
e.g. after changing records with plain SQL, to flush cached API responses. See :ref:`response-cache-invalidation`.
10 changes: 6 additions & 4 deletions Documentation/Filtering/BuiltinFilters/BooleanFilter/Index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ Should be used to filter items by boolean fields.

Syntax: ``?property=<true|false|1|0>``

If the parameter is passed as an array (``?property[]=true``) only the first value is used.

.. code-block:: php

use SourceBroker\T3api\Annotation as T3api;
use SourceBroker\T3api\Filter\SearchFilter;
use SourceBroker\T3api\Filter\BooleanFilter;

/**
* @T3api\ApiResource (
Expand All @@ -31,10 +33,10 @@ Syntax: ``?property=<true|false|1|0>``
{
}

.. admonition:: Real examples. Run "ddev restart && ddev ci 13" and try those links below.
.. admonition:: Real examples. Run "ddev restart && ddev ci 14" and try those links below.

* | Get list of news which are "Top News":
| https://13.t3api.ddev.site/_api/news/news?istopnews=true
| `https://14.t3api.ddev.site/_api/news/news?istopnews=true <https://14.t3api.ddev.site/_api/news/news?istopnews=true>`__
|
* | Get list of news which are "Top News" and sort by title
| `https://13.t3api.ddev.site/_api/news/news?istopnews=true&order[title]=asc <https://13.t3api.ddev.site/_api/news/news?istopnews=true&order[title]=asc>`__
| `https://14.t3api.ddev.site/_api/news/news?istopnews=true&order[title]=asc <https://14.t3api.ddev.site/_api/news/news?istopnews=true&order[title]=asc>`__
44 changes: 43 additions & 1 deletion Documentation/Filtering/BuiltinFilters/ContainFilter/Index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,46 @@
ContainFilter
==============

@todo - write docs
Should be used to filter items by fields which keep a **comma separated list of values** in a single database column (e.g. ``fe_users.usergroup``, ``pages.fe_group`` or any custom ``select`` field with ``renderType=selectCheckBox`` / ``selectMultipleSideBySide`` which is stored without MM table).

Under the hood ``ContainFilter`` uses MySQL's ``FIND_IN_SET()`` function, so request ``?usergroup=2`` matches records where ``usergroup`` column is e.g. ``2``, ``1,2`` or ``2,5,7`` but **not** ``12`` or ``20`` (as it would happen with ``LIKE "%2%"``).

Syntax: ``?property=<value>`` or ``?property[]=<value1>&property[]=<value2>``.

When multiple values are passed then items which contain **any** of the values are returned (``FIND_IN_SET("value1", property) > 0 OR FIND_IN_SET("value2", property) > 0``).

.. code-block:: php

use SourceBroker\T3api\Annotation as T3api;
use SourceBroker\T3api\Filter\ContainFilter;

/**
* @T3api\ApiResource (
* collectionOperations={
* "get"={
* "path"="/users",
* },
* },
* )
*
* @T3api\ApiFilter(
* ContainFilter::class,
* properties={"usergroup"}
* )
*/
class User extends \TYPO3\CMS\Extbase\Domain\Model\FrontendUser
{
}

With configuration above:

- ``/users?usergroup=2`` - returns users which belong to user group with uid ``2``.
- ``/users?usergroup[]=2&usergroup[]=5`` - returns users which belong to user group ``2`` **or** ``5``.

Nested properties (e.g. ``address.tags``) are supported as well - required joins are added automatically.

.. note::

``ContainFilter`` is not meant for relations stored in MM tables (e.g. news categories). For such relations use
:ref:`SearchFilter <filtering_filters_search-filter>` or :ref:`NumericFilter <filtering_filters_numeric-filter>`
on a nested property, e.g. ``categories.uid``.
12 changes: 11 additions & 1 deletion Documentation/Filtering/BuiltinFilters/DistanceFilter/Index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
DistanceFilter
===============

Distance filter allows to filter map points points by radius. Map points kept in the database needs to contain latitude and longitude to use this filter.
Distance filter allows to filter map points by radius. Map points kept in the database needs to contain latitude and longitude to use this filter.

Configuration for distance filter looks a little bit different than for other build-in filter. Because distance filter is not based on single field it should not contain ``properties`` definition. Instead of that it is needed to specify which model properties contain latitude and longitude in ``arguments``. Moreover, as ``properties`` is not defined, ``parameterName`` is required. Beside default values in ``arguments``, distance filter accepts also:

Expand All @@ -15,6 +15,7 @@ Configuration for distance filter looks a little bit different than for other bu

.. code-block:: php

use SourceBroker\T3api\Annotation as T3api;
use SourceBroker\T3api\Filter\DistanceFilter;

/**
Expand All @@ -33,3 +34,12 @@ Configuration for distance filter looks a little bit different than for other bu
{
}

Syntax: ``?<parameterName>[lat]=<float>&<parameterName>[lng]=<float>`` and optionally ``&<parameterName>[radius]=<float>``.

With configuration above request ``/items?position[lat]=52.2297&position[lng]=21.0122`` returns items which are not further than 100 km from given point. Both ``lat`` and ``lng`` are required - if one of them is missing an error is returned. ``position[radius]`` is taken into account only if ``allowClientRadius`` is set to ``true``, otherwise it is ignored and ``radius`` from ``arguments`` is used.

Latitude and longitude properties can also be nested (e.g. ``"latProperty"="address.latitude"``).

.. note::

Distance filter only filters items - it does not order them by distance. If you need ordering by distance use a :ref:`custom filter with query modifier <filtering_custom-filters_query-modifiers>`.
27 changes: 24 additions & 3 deletions Documentation/Filtering/BuiltinFilters/NumericFilter/Index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
NumericFilter
==============

Should be used to filter items by numeric fields.
Should be used to filter items by integer fields (e.g. ``pid``, relation uids, counters).

Syntax: ``?property=<int|decimal...>`` or ``?property[]=<int|decimal...>&property[]=<int|decimal...>``.
Syntax: ``?property=<int>`` or ``?property[]=<int>&property[]=<int>``.

.. note::

Values are casted to integer, so ``?height=1.85`` is the same as ``?height=1``. To filter decimal fields use
:ref:`RangeFilter <filtering_filters_range-filter>` or a :ref:`custom filter <filtering_custom-filters>`.

.. code-block:: php

Expand All @@ -22,9 +27,25 @@ Syntax: ``?property=<int|decimal...>`` or ``?property[]=<int|decimal...>&propert
* )
* @T3api\ApiFilter(
* NumericFilter::class,
* properties={"address.number", "height"},
* properties={"address.number", "age"},
* )
*/
class User extends \TYPO3\CMS\Extbase\Domain\Model\FrontendUser
{
}

.. important::

If you want to filter by ``uid`` on a site with more than one language use
:ref:`UidFilter <filtering_filters_uid-filter>` instead of ``NumericFilter``.

.. admonition:: Real examples. Run "ddev restart && ddev ci 14" and try those links below.

News resource of testing instance has ``NumericFilter`` configured for ``pid`` property: ``@T3api\ApiFilter(NumericFilter::class, properties={"pid"})``.

* | Get list of news stored on page (folder) with uid 6:
| `https://14.t3api.ddev.site/_api/news/news?pid=6 <https://14.t3api.ddev.site/_api/news/news?pid=6>`__
|
* | Get list of news stored on page 3 or 6 (see :ref:`SQL "IN" operator <filtering_sql-in-operator>`):
| `https://14.t3api.ddev.site/_api/news/news?pid[]=3&pid[]=6 <https://14.t3api.ddev.site/_api/news/news?pid[]=3&pid[]=6>`__
|
Loading
Loading