From 6666a2af4b437ebd1d5e0ca211fc4e84491ed458 Mon Sep 17 00:00:00 2001 From: Krystian Szymukowicz Date: Sat, 3 Oct 2026 18:09:31 +0200 Subject: [PATCH] [TASK] Update docs. --- Documentation/Cors/Index.rst | 2 +- .../Customization/ApiResourcePath/Index.rst | 2 +- .../CollectionResponseSchema/Index.rst | 134 ++++++- .../ExpressionLanguage/Index.rst | 8 +- Documentation/Events/Index.rst | 16 +- .../BuiltinFilters/BooleanFilter/Index.rst | 10 +- .../BuiltinFilters/ContainFilter/Index.rst | 44 ++- .../BuiltinFilters/DistanceFilter/Index.rst | 12 +- .../BuiltinFilters/NumericFilter/Index.rst | 27 +- .../BuiltinFilters/OrderFilter/Index.rst | 27 +- .../BuiltinFilters/RangeFilter/Index.rst | 26 +- .../BuiltinFilters/SearchFilter/Index.rst | 13 +- .../BuiltinFilters/UidFilter/Index.rst | 12 +- .../Filtering/CustomFilters/Index.rst | 4 +- .../Filtering/SqlInOperator/Index.rst | 11 +- .../Filtering/SqlOrOperator/Index.rst | 11 + Documentation/GettingStarted/Index.rst | 43 ++- .../HandlingCascadePersistence/Index.rst | 10 +- Documentation/HandlingFileUpload/Index.rst | 131 +++---- Documentation/Index.rst | 6 +- Documentation/Integration/Index.rst | 72 +++- .../Miscellaneous/Changelog/Index.rst | 68 +++- .../Development/CommandsList/Index.rst | 21 +- .../Development/Introduction/Index.rst | 6 +- .../TypicalUseCases/BugsFixing/Index.rst | 14 +- Documentation/Multilanguage/Index.rst | 57 +-- Documentation/Operations/Index.rst | 330 ++++++------------ Documentation/Pagination/ClientSide/Index.rst | 43 ++- Documentation/Pagination/Index.rst | 107 +++++- Documentation/Pagination/ServerSide/Index.rst | 53 ++- Documentation/ResponseCache/Index.rst | 26 +- Documentation/Security/Index.rst | 173 ++++++++- .../Serialization/ContextGroups/Index.rst | 156 ++++++++- .../Serialization/Customization/Index.rst | 133 ++++++- .../Serialization/Exceptions/Index.rst | 38 +- .../Serialization/Handlers/Index.rst | 256 +++++++++++++- Documentation/Serialization/Index.rst | 51 ++- .../Serialization/Subscribers/Index.rst | 73 ++-- .../Serialization/YamlMetadata/Index.rst | 72 +++- .../UseCases/CurrentUserAssignment/Index.rst | 5 +- .../UseCases/CurrentUserEndpoint/Index.rst | 14 +- README.md | 53 +++ README.rst | 54 --- 43 files changed, 1870 insertions(+), 554 deletions(-) create mode 100644 README.md delete mode 100644 README.rst diff --git a/Documentation/Cors/Index.rst b/Documentation/Cors/Index.rst index e38c46c5..4a3b02cb 100644 --- a/Documentation/Cors/Index.rst +++ b/Documentation/Cors/Index.rst @@ -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 `__. -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 diff --git a/Documentation/Customization/ApiResourcePath/Index.rst b/Documentation/Customization/ApiResourcePath/Index.rst index 12519e0f..a0216e9b 100644 --- a/Documentation/Customization/ApiResourcePath/Index.rst +++ b/Documentation/Customization/ApiResourcePath/Index.rst @@ -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. diff --git a/Documentation/Customization/CollectionResponseSchema/Index.rst b/Documentation/Customization/CollectionResponseSchema/Index.rst index 9bf8f9b6..1a1805d0 100644 --- a/Documentation/Customization/CollectionResponseSchema/Index.rst +++ b/Documentation/Customization/CollectionResponseSchema/Index.rst @@ -3,4 +3,136 @@ Collection response schema =========================== -@todo - write docs +By default collection ``GET`` operations return response in `Hydra `__ 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 + + 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 ` (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 + + 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 `__ + | diff --git a/Documentation/Customization/ExpressionLanguage/Index.rst b/Documentation/Customization/ExpressionLanguage/Index.rst index d177f2bd..a186caf4 100644 --- a/Documentation/Customization/ExpressionLanguage/Index.rst +++ b/Documentation/Customization/ExpressionLanguage/Index.rst @@ -3,15 +3,15 @@ Expression Language ===================== -`Symfony expression language `__ is `widely used in TYPO3 core `__. T3api also uses it in two places: +`Symfony expression language `__ is `widely used in TYPO3 core `__. T3api also uses it in two places: - :ref:`Serialization (and deserialization) ` - :ref:`Security (checking access to operations and filters) ` -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 `__. 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 `__. 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' => [ @@ -19,4 +19,4 @@ T3api utilizes TYPO3 core expression language feature. So, if you would like to ], ]; -After registration of custom provider follow TYPO3 documentation how to `add additional variables `__ and `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 `__ and `additional functions `__ into expression language. Now you can use your custom variables and functions inside t3api security check or serialization and deserialization expressions. diff --git a/Documentation/Events/Index.rst b/Documentation/Events/Index.rst index 1cb7692d..571ea65b 100644 --- a/Documentation/Events/Index.rst +++ b/Documentation/Events/Index.rst @@ -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`: @@ -39,7 +39,7 @@ Example implementation: public function __invoke(AfterDeserializeOperationEvent $event): void { $operation = $event->getOperation(); - $object => $event->getObject(); + $object = $event->getObject(); ... $event->setObject($object); } @@ -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`: @@ -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 `__ + Before using these events consider if `build-in TYPO3 mechanism to extend expression language `__ 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). @@ -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 ` 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`. diff --git a/Documentation/Filtering/BuiltinFilters/BooleanFilter/Index.rst b/Documentation/Filtering/BuiltinFilters/BooleanFilter/Index.rst index 45d95b1d..2d133e3f 100644 --- a/Documentation/Filtering/BuiltinFilters/BooleanFilter/Index.rst +++ b/Documentation/Filtering/BuiltinFilters/BooleanFilter/Index.rst @@ -7,10 +7,12 @@ Should be used to filter items by boolean fields. Syntax: ``?property=`` +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 ( @@ -31,10 +33,10 @@ Syntax: ``?property=`` { } -.. 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 `__ | * | 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://14.t3api.ddev.site/_api/news/news?istopnews=true&order[title]=asc `__ diff --git a/Documentation/Filtering/BuiltinFilters/ContainFilter/Index.rst b/Documentation/Filtering/BuiltinFilters/ContainFilter/Index.rst index 4fc099d4..efe0f1df 100644 --- a/Documentation/Filtering/BuiltinFilters/ContainFilter/Index.rst +++ b/Documentation/Filtering/BuiltinFilters/ContainFilter/Index.rst @@ -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=`` or ``?property[]=&property[]=``. + +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 ` or :ref:`NumericFilter ` + on a nested property, e.g. ``categories.uid``. diff --git a/Documentation/Filtering/BuiltinFilters/DistanceFilter/Index.rst b/Documentation/Filtering/BuiltinFilters/DistanceFilter/Index.rst index e97e198d..1c2503f1 100644 --- a/Documentation/Filtering/BuiltinFilters/DistanceFilter/Index.rst +++ b/Documentation/Filtering/BuiltinFilters/DistanceFilter/Index.rst @@ -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: @@ -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; /** @@ -33,3 +34,12 @@ Configuration for distance filter looks a little bit different than for other bu { } +Syntax: ``?[lat]=&[lng]=`` and optionally ``&[radius]=``. + +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 `. diff --git a/Documentation/Filtering/BuiltinFilters/NumericFilter/Index.rst b/Documentation/Filtering/BuiltinFilters/NumericFilter/Index.rst index 5dd51709..97a7c61c 100644 --- a/Documentation/Filtering/BuiltinFilters/NumericFilter/Index.rst +++ b/Documentation/Filtering/BuiltinFilters/NumericFilter/Index.rst @@ -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=`` or ``?property[]=&property[]=``. +Syntax: ``?property=`` or ``?property[]=&property[]=``. + +.. note:: + + Values are casted to integer, so ``?height=1.85`` is the same as ``?height=1``. To filter decimal fields use + :ref:`RangeFilter ` or a :ref:`custom filter `. .. code-block:: php @@ -22,9 +27,25 @@ Syntax: ``?property=`` or ``?property[]=&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 ` 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 `__ + | + * | Get list of news stored on page 3 or 6 (see :ref:`SQL "IN" operator `): + | `https://14.t3api.ddev.site/_api/news/news?pid[]=3&pid[]=6 `__ + | diff --git a/Documentation/Filtering/BuiltinFilters/OrderFilter/Index.rst b/Documentation/Filtering/BuiltinFilters/OrderFilter/Index.rst index c57d1299..555a1e9b 100644 --- a/Documentation/Filtering/BuiltinFilters/OrderFilter/Index.rst +++ b/Documentation/Filtering/BuiltinFilters/OrderFilter/Index.rst @@ -7,8 +7,23 @@ Allows to change default ordering of collection responses. Syntax: ``?order[property]=`` +Direction is case insensitive. Any other value results in an error. + It is possible to order by single field (query string :uri:`?order[title]=asc`) or by multiple of them (query string :uri:`?order[title]=asc&order[datetime]=desc`). +Default direction can be set as the strategy of the property. It is used when the parameter is sent without value (``?order[datetime]``). Without default direction such parameter is ignored. + +.. code-block:: php + + /** + * @T3api\ApiFilter( + * OrderFilter::class, + * properties={"title": "asc", "datetime": "desc"} + * ) + */ + +When the client does not send any ``order`` parameter, the collection is ordered with default ordering of the repository. + It may happen that conflict of names will occur if ``order`` is also the name of property with enabled another filter. Solution in such cases would be a change of parameter name used by ``OrderFilter``. It can be done using argument ``orderParameterName``, as on example below: .. code-block:: php @@ -27,19 +42,19 @@ It may happen that conflict of names will occur if ``order`` is also the name of * * @T3api\ApiFilter( * OrderFilter::class, - * properties={"title","datetime"} + * properties={"title","datetime"}, * arguments={"orderParameterName": "myOrderParameterName"}, * ) */ - class News extends \GeorgRinger\News\Domain\Model\News + class News extends \GeorgRinger\News\Domain\Model\News { } -.. 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 sorted by titles ascending: - | `https://13.t3api.ddev.site/_api/news/news?order[title]=asc `__ + * | Get list of news sorted by title ascending: + | `https://14.t3api.ddev.site/_api/news/news?order[title]=asc `__ | * | Get list of news sorted by date descending and then by title ascending: - | `https://13.t3api.ddev.site/_api/news/news?order[datetime]=desc&order[title]=asc `__ + | `https://14.t3api.ddev.site/_api/news/news?order[datetime]=desc&order[title]=asc `__ diff --git a/Documentation/Filtering/BuiltinFilters/RangeFilter/Index.rst b/Documentation/Filtering/BuiltinFilters/RangeFilter/Index.rst index d6b3ed53..7337ce6b 100644 --- a/Documentation/Filtering/BuiltinFilters/RangeFilter/Index.rst +++ b/Documentation/Filtering/BuiltinFilters/RangeFilter/Index.rst @@ -7,10 +7,20 @@ Allows to filter by a value lower than (or equal), greater than (or equal) and b Syntax: ``?property[]=value`` +- ``lt`` - lower than, +- ``lte`` - lower than or equal, +- ``gt`` - greater than, +- ``gte`` - greater than or equal, +- ``between`` - between two values (inclusive) separated with ``..``, e.g. ``?uid[between]=5..100``. + ``RangeFilter`` supports two different strategies: - ``int`` (alternatively ``number`` or ``integer``) - default strategy if not specified. Values passed in filter is casted to integer. -- ``datetime`` - allows to filter results by date time range (value passed in filter is casted to DateTime object before passed to Extbase query). +- ``datetime`` - allows to filter results by date time range (value passed in filter is casted to DateTime object before passed to Extbase query). Any format accepted by PHP's ``new \DateTime()`` can be used, e.g. ``2020-05-28``, ``2020-05-28T21:35:55`` or ``2020-05-28T21:35:55+02:00``. + +.. important:: + + When more than one operator is sent for the same property, conditions are joined with ``OR``, not ``AND``. It means that ``?datetime[gte]=2020-05-01&datetime[lte]=2020-05-31`` returns **all** news (every date is later than first of May **or** earlier than end of May). To get items from a range use ``between``: ``?datetime[between]=2020-05-01..2020-05-31``. .. code-block:: php @@ -34,18 +44,18 @@ Syntax: ``?property[]=value`` * }, * ) */ - class News extends \GeorgRinger\News\Domain\Model\News + class News extends \GeorgRinger\News\Domain\Model\News { } -.. 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 news from between two dates: - | `https://13.t3api.ddev.site/_api/news/news?datetime[between]=2020-05-28T21:35:55.000..2020-05-29T21:20:00.000 `__ + | `https://14.t3api.ddev.site/_api/news/news?datetime[between]=2020-05-28T21:35:55.000..2020-05-29T21:20:00.000 `__ | - * | Get news that are older than: - | `https://13.t3api.ddev.site/_api/news/news?datetime[gt]=2020-05-28T21:35:55.000 `__ + * | Get news that are newer than given date: + | `https://14.t3api.ddev.site/_api/news/news?datetime[gt]=2020-05-28T21:35:55.000 `__ | - * | Get news with uid between 5 and 100 (Note! It will return newses from all languages. Use UidFilter instead if you want to get language dependent newses): - | `https://13.t3api.ddev.site/_api/news/news?uid[between]=5..100 `__ + * | Get news from 28th May 2020 (whole day): + | `https://14.t3api.ddev.site/_api/news/news?datetime[between]=2020-05-28T00:00:00..2020-05-28T23:59:59 `__ | diff --git a/Documentation/Filtering/BuiltinFilters/SearchFilter/Index.rst b/Documentation/Filtering/BuiltinFilters/SearchFilter/Index.rst index 0db1d636..18a83993 100644 --- a/Documentation/Filtering/BuiltinFilters/SearchFilter/Index.rst +++ b/Documentation/Filtering/BuiltinFilters/SearchFilter/Index.rst @@ -3,7 +3,9 @@ SearchFilter ============ -Should be used to filter items by text fields. ``SearchFilter`` supports 3 strategies: +Should be used to filter items by text fields. Syntax: ``?property=`` or ``?property[]=&property[]=`` (items matching any of the values are returned). + +``SearchFilter`` supports 3 strategies: - ``exact`` - Filters items which matches **exactly** search term (``WHERE property = "value"`` in MySQL language). This is strategy used by default if no other is configured. @@ -60,7 +62,7 @@ Should be used to filter items by text fields. ``SearchFilter`` supports 3 strat { } -- ``matchAgainst`` - Filters items which matches search term using **full text search** (``MATCH(property) AGAINST ("value" IN NATURAL LANGUAGE MODE)`` in MySQL language). In it possible to extend query with ``WITH QUERY EXPANSION`` by adding ``withQueryExpansion`` in arguments (`Read more about query expansion `__) +- ``matchAgainst`` - Filters items which matches search term using **full text search** (``MATCH(property) AGAINST ("value" IN NATURAL LANGUAGE MODE)`` in MySQL language). It is possible to extend query with ``WITH QUERY EXPANSION`` by adding ``withQueryExpansion`` in arguments (`Read more about query expansion `__) .. code-block:: php @@ -93,9 +95,12 @@ Should be used to filter items by text fields. ``SearchFilter`` supports 3 strat { } +.. important:: + + ``matchAgainst`` strategy requires a ``FULLTEXT`` index on the searched column(s) (add it in ``ext_tables.sql``), otherwise MySQL throws an error. It is supported only by MySQL and MariaDB. -.. 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 filtered by search word "minima" and sort by datetime. - | `https://13.t3api.ddev.site/_api/news/news?search=minima&order[datetime]=asc `__ + | `https://14.t3api.ddev.site/_api/news/news?search=minima&order[datetime]=asc `__ | diff --git a/Documentation/Filtering/BuiltinFilters/UidFilter/Index.rst b/Documentation/Filtering/BuiltinFilters/UidFilter/Index.rst index d078c130..d17a603b 100644 --- a/Documentation/Filtering/BuiltinFilters/UidFilter/Index.rst +++ b/Documentation/Filtering/BuiltinFilters/UidFilter/Index.rst @@ -32,12 +32,12 @@ Syntax: ``?property=`` or ``?property[]=&property[]=``. * properties={"uid"}, * ) */ - class News extends \GeorgRinger\News\Domain\Model\News - { - } + class News extends \GeorgRinger\News\Domain\Model\News + { + } -.. 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 translated newses with uid 5,6,7 - | `https://13.t3api.ddev.site/_api/news/news?uid[]=5&uid[]=6&uid[]=7 `_ + * | Get news with uid 1, 2, 3 in German - translated records are found by uid of default language record (DE translations have uids 8 and 10): + | `https://14.t3api.ddev.site/de/_api/news/news?uid[]=1&uid[]=2&uid[]=3 `__ diff --git a/Documentation/Filtering/CustomFilters/Index.rst b/Documentation/Filtering/CustomFilters/Index.rst index 7437e0fb..026d674a 100644 --- a/Documentation/Filtering/CustomFilters/Index.rst +++ b/Documentation/Filtering/CustomFilters/Index.rst @@ -5,7 +5,7 @@ Custom filters ============== It is super easy to create custom filters which will match your specific requirements. -Custom filters has to implement interface ``\SourceBroker\T3api\Filter\FilterInterface`` and contain one public method ``filterProperty``. +Custom filters have to implement interface ``\SourceBroker\T3api\Filter\FilterInterface`` and contain one public method ``filterProperty``. Method ``filterProperty`` accepts 4 arguments: - $property (``string``) - Name of the property to filter by. @@ -64,7 +64,7 @@ It may be useful, but not required, to extend class ``\SourceBroker\T3api\Filter } .. note:: - Instance of your custom filter will be created using Extbase's ObjectManager, so you can inject into it any other services if you need them. + Instance of your custom filter is created with ``GeneralUtility::makeInstance()``, so constructor injection works if the filter is a public service (``public: true`` in ``Configuration/Services.yaml``). Filters are shared instances - do not keep request specific state in them, or reset it after use. .. _filtering_custom-filters_query-modifiers: diff --git a/Documentation/Filtering/SqlInOperator/Index.rst b/Documentation/Filtering/SqlInOperator/Index.rst index 4ecbb903..c3f2c029 100644 --- a/Documentation/Filtering/SqlInOperator/Index.rst +++ b/Documentation/Filtering/SqlInOperator/Index.rst @@ -3,4 +3,13 @@ SQL "IN" operator ================== -When using query params ``?property=`` only items which match exactly such condition are returned. But it is possible to pass multiple values. If you would like to receive all items which ``property`` matches ``value1`` **or** ``value2`` then you can send ``property`` as an array in query string: ``?property[]=&property[]=``. From build-in filters ``NumericFilter`` and ``SearchFilter`` are the filters which support ``IN`` operator. +When using query params ``?property=`` only items which match exactly such condition are returned. But it is possible to pass multiple values. If you would like to receive all items which ``property`` matches ``value1`` **or** ``value2`` then you can send ``property`` as an array in query string: ``?property[]=&property[]=``. From build-in filters ``NumericFilter``, ``UidFilter``, ``SearchFilter`` and ``ContainFilter`` are the filters which support ``IN`` operator. + +.. admonition:: Real examples. Run "ddev restart && ddev ci 14" and try those links below. + + * | Get news stored on page 3 or 6 (``NumericFilter``): + | `https://14.t3api.ddev.site/_api/news/news?pid[]=3&pid[]=6 `__ + | + * | Get news with uid 1 or 5 (``UidFilter``): + | `https://14.t3api.ddev.site/_api/news/news?uid[]=1&uid[]=5 `__ + | diff --git a/Documentation/Filtering/SqlOrOperator/Index.rst b/Documentation/Filtering/SqlOrOperator/Index.rst index 81238299..82bfd183 100644 --- a/Documentation/Filtering/SqlOrOperator/Index.rst +++ b/Documentation/Filtering/SqlOrOperator/Index.rst @@ -35,3 +35,14 @@ By default ``AND`` conjunction is used between all applied filters but there is class User extends \TYPO3\CMS\Extbase\Domain\Model\FrontendUser { } + +.. admonition:: Real examples. Run "ddev restart && ddev ci 14" and try those links below. + + News resource of testing instance has ``SearchFilter`` configured for ``title``, ``alternativeTitle``, ``bodytext`` and ``tags.title`` with common ``parameterName`` set to ``search``. + + * | Get list of news where title, alternative title, bodytext **or** title of any related tag contains "minima": + | `https://14.t3api.ddev.site/_api/news/news?search=minima `__ + | + * | Get list of news where any of the fields contains "folder B": + | `https://14.t3api.ddev.site/_api/news/news?search=folder%20B `__ + | diff --git a/Documentation/GettingStarted/Index.rst b/Documentation/GettingStarted/Index.rst index b8c0060b..2b2982d9 100644 --- a/Documentation/GettingStarted/Index.rst +++ b/Documentation/GettingStarted/Index.rst @@ -20,9 +20,9 @@ which allows to build smart frontend applications with auto-discoverability capa Installation ============ -Run +T3api supports TYPO3 12.4, 13 and 14 (PHP 8.1 or higher). Run -.. code-block:: yaml +.. code-block:: bash composer require sourcebroker/t3api @@ -33,7 +33,7 @@ Configuration Route enhancer ++++++++++++++ -Import route enhancer by adding following line on bottom of your site ``config.yaml`` . +Import route enhancer by adding following lines at the bottom of your site ``config.yaml``: .. code-block:: yaml @@ -69,22 +69,53 @@ Creating API resource Next step is to make an API resource from our entity. To map Extbase model to API resource it is just needed to add ``@SourceBroker\T3api\Annotation\ApiResource`` annotation -to our model class. +to our model class and define at least one :ref:`operation ` (endpoint): .. code-block:: php use SourceBroker\T3api\Annotation\ApiResource; /** - * @ApiResource() + * @ApiResource( + * collectionOperations={ + * "get"={ + * "path"="/items", + * }, + * }, + * itemOperations={ + * "get"={ + * "path"="/items/{id}", + * } + * }, + * ) */ class Item extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity { } +After flushing caches the items are available under ``https://example.com/_api/items`` and single item under ``https://example.com/_api/items/1``. + +.. note:: + By default all properties of the entity are returned. Use :ref:`serialization context groups ` + to control which properties are exposed. + .. note:: API resource can be created only from class which: - Extends ``\TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject``. - - Is kept in path ``EXT:{extkey}/Classes/Domain/Model``. + - Is kept in path ``EXT:{extkey}/Classes/Domain/Model`` (can be changed, see :ref:`customization_api-resource-path`). - Exists in enabled extension. + +.. admonition:: Real examples. Run "ddev restart && ddev ci 14" and try those links below. + + Testing instance uses ``EXT:t3apinews`` which exposes ``EXT:news`` records as API resources. + + * | Main endpoint with the list of all resources: + | `https://14.t3api.ddev.site/_api/ `__ + | + * | Collection of news: + | `https://14.t3api.ddev.site/_api/news/news `__ + | + * | Single news: + | `https://14.t3api.ddev.site/_api/news/news/1 `__ + | diff --git a/Documentation/HandlingCascadePersistence/Index.rst b/Documentation/HandlingCascadePersistence/Index.rst index ed640ee6..df26a1bb 100644 --- a/Documentation/HandlingCascadePersistence/Index.rst +++ b/Documentation/HandlingCascadePersistence/Index.rst @@ -1,13 +1,13 @@ .. _handling_cascade_persistence: -======================= +============================ Handling Cascade Persistence -======================= +============================ Create relation to existing entity =================================== -It is super easy to create relation to already existing entity. To do so you just need to pass ``uid`` of the related object as a value of property. It works in exactly same way for `One-To-One` and `Many-To-One` relations for whom target entity contains information about one related entity. As on example below - we want to assign ``User`` to ``Department`` assuming that ``User`` can belongs to only one ``Department``. +It is super easy to create relation to already existing entity. To do so you just need to pass ``uid`` of the related object as a value of property. It works in exactly same way for `One-To-One` and `Many-To-One` relations for whom target entity contains information about one related entity. As on example below - we want to assign ``User`` to ``Department`` assuming that ``User`` can belong to only one ``Department``. .. code-block:: json @@ -66,7 +66,7 @@ After enabling cascade persistence for ``Order.items`` property it is possible t "article": 14, "quantity": 4 } - ], + ] } Cascade persistence - updating related entity in single request @@ -105,5 +105,5 @@ Example payload below is the second request after first from previous example, w "article": 15, "quantity": 9 } - ], + ] } diff --git a/Documentation/HandlingFileUpload/Index.rst b/Documentation/HandlingFileUpload/Index.rst index 87f9a192..4a945d71 100644 --- a/Documentation/HandlingFileUpload/Index.rst +++ b/Documentation/HandlingFileUpload/Index.rst @@ -35,11 +35,11 @@ There is plenty configuration options which allows you to customize upload endpo - ``folder`` - destination folder (default: ``1:/user_upload/`` which means files will be uploaded into ``user_upload`` directory of file storage ID ``1``). -- ``allowedFileExtensions`` - Array of allowed file extensions (default:``$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']``). +- ``allowedFileExtensions`` - Array of allowed file extensions (default: ``$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']``). Files with PHP extensions are always rejected. -- ``conflictMode`` - Value of enumeration ``\TYPO3\CMS\Core\Resource\DuplicationBehavior`` (default: ``\TYPO3\CMS\Core\Resource\DuplicationBehavior::RENAME`` which means that new file name will be changed if same file already exists). +- ``conflictMode`` - What to do if file with the same name already exists: ``rename`` (default - new file name gets a suffix, e.g. ``image_01.jpg``), ``replace`` or ``cancel`` (values of ``\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior``). -- ``filenameMask`` - Allows to change the name of the uploaded file (default: ``[filename]``; see :ref:`how to customize name of uploaded file `). +- ``filenameMask`` - Allows to change the name of the uploaded file (default: ``[filename][extensionWithDot]``; see :ref:`how to customize name of uploaded file `). - ``filenameHashAlgorithm`` - (default: ``md5``; see :ref:`how to customize name of uploaded file `). @@ -64,7 +64,7 @@ There is plenty configuration options which allows you to customize upload endpo * "upload"={ * "folder"="1:/user_upload/", * "allowedFileExtensions"={"jpg", "jpeg", "png"}, - * "conflictMode"=DuplicationBehavior::RENAME, + * "conflictMode"="rename", * } * } * ) @@ -76,82 +76,65 @@ There is plenty configuration options which allows you to customize upload endpo Configuring TCA ================ -It may be needed to adjust ``TCA`` configuration to correctly fill ``sys_file_reference`` columns. Correct ``TCA`` configuration contains at least 3 elements inside ``foreign_match_fields`` array - ``fieldname``, ``tablenames`` and ``table_local`` but extension builder by default creates only ``fieldname`` (at least in current version). +Field which keeps file references has to be configured as TCA field of ``type`` ``file``. TYPO3 fills +``fieldname`` and ``tablenames`` columns of ``sys_file_reference`` automatically for such fields. .. code-block:: php - $GLOBALS['TCA']['tx_users_domain_model_user']['columns']['photo']['config']['foreign_match_fields']['fieldname'] = 'photo'; - $GLOBALS['TCA']['tx_users_domain_model_user']['columns']['photo']['config']['foreign_match_fields']['tablenames'] = 'tx_users_domain_model_user'; - $GLOBALS['TCA']['tx_users_domain_model_user']['columns']['photo']['config']['foreign_match_fields']['table_local'] = 'sys_file'; - -Appropriate ``TCA`` configuration for uploadable field may look like code below. Mind that -``\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig`` adds element ``fieldname`` by default -so it is needed only to take care of ``tablenames`` and ``table_local``. - -.. code-block:: php - - 'photo' => [ - 'exclude' => true, - 'label' => 'LLL:EXT:users/Resources/Private/Language/locallang_db.xlf:tx_users_domain_model_user.photo', - 'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig( - 'photo', - [ - 'foreign_match_fields' => [ - 'tablenames' => 'tx_users_domain_model_user', - 'table_local' => 'sys_file', - ], - 'appearance' => [ - 'createNewRelationLinkTitle' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:images.addFileReference' - ], - 'foreign_types' => [ - '0' => [ - 'showitem' => ' - --palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette' - ], - \TYPO3\CMS\Core\Resource\File::FILETYPE_TEXT => [ - 'showitem' => ' - --palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette' - ], - \TYPO3\CMS\Core\Resource\File::FILETYPE_IMAGE => [ - 'showitem' => ' - --palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette' - ], - \TYPO3\CMS\Core\Resource\File::FILETYPE_AUDIO => [ - 'showitem' => ' - --palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette' - ], - \TYPO3\CMS\Core\Resource\File::FILETYPE_VIDEO => [ - 'showitem' => ' - --palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette' - ], - \TYPO3\CMS\Core\Resource\File::FILETYPE_APPLICATION => [ - 'showitem' => ' - --palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette' - ] - ], - 'maxitems' => 1 - ], - $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] - ), - ], + 'photo' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:users/Resources/Private/Language/locallang_db.xlf:tx_users_domain_model_user.photo', + 'config' => [ + 'type' => 'file', + 'allowed' => 'common-image-types', + 'maxitems' => 1, + ], + ], File upload request ==================== -@todo - write docs +File is sent as ``multipart/form-data`` ``POST`` request to the upload endpoint. Name of the form field has to be ``originalResource``. Only one file can be uploaded in a single request - to attach multiple files upload them one by one and then :ref:`save references ` to all of them in a single request. + +.. code-block:: bash -@todo - write docs: request with multiple files (ObjectStorage with FileReference) + curl -X POST -F "originalResource=@/path/to/photo.jpg" https://example.com/_api/files File upload response ===================== -@todo - write docs +On success the response has status ``201`` and contains created ``sys_file`` record. The most important property is ``uid`` - it is needed to create a file reference in the next request. + +.. code-block:: json + + { + "publicUrl": "fileadmin/user_upload/media-export-excluded/test1_01.jpg", + "absolutePublicUrl": "https://14.t3api.ddev.site/fileadmin/user_upload/media-export-excluded/test1_01.jpg", + "properties": { + "size": 42520, + "mime_type": "image/jpeg", + "name": "test1_01.jpg", + "extension": "jpg", + "width": 720, + "height": 449, + "uid": 3 + }, + "name": "test1_01.jpg", + "uid": 3, + "identifier": "/user_upload/media-export-excluded/test1_01.jpg" + } + +(``properties`` shortened.) + +.. admonition:: Real examples. Run "ddev restart && ddev ci 14" and try those links below. + + Testing instance has upload endpoint ``/_api/news/files`` (``EXT:t3apinews`` ``File`` resource) which accepts ``jpg``, ``jpeg`` and ``png`` files and stores them in ``1:/user_upload/media-export-excluded/``: + + .. code-block:: bash + + curl -k -X POST -F "originalResource=@.test/14/public/fileadmin/user_upload/test1.jpg" https://14.t3api.ddev.site/_api/news/files + +.. _handling_file_upload_save_reference: Save reference to new file =========================== @@ -164,11 +147,11 @@ Save reference to new file { "photo": { - "uidLocal": 15, + "uidLocal": 15 } } -If you would like to save any other data inside file reference it is needed to extend ``TYPO3\CMS\Extbase\Domain\Model\FileReference`` class. +``uidLocal`` is the ``uid`` of the file returned by upload endpoint. If you would like to save any other data inside file reference (e.g. ``showinpreview`` in ``EXT:news``) it is needed to use a class which extends ``TYPO3\CMS\Extbase\Domain\Model\FileReference`` and contains such properties. .. code-block:: json @@ -185,9 +168,7 @@ If you would like to save any other data inside file reference it is needed to e ] } -@todo - write docs information about handling custom class of file reference (which extends standard extbase FileReference) - -@todo - write docs +Custom file reference class is handled in the same way as the default one - t3api supports every class which extends ``TYPO3\CMS\Extbase\Domain\Model\FileReference``. Make sure that the property type points to your class (in annotation or :ref:`YAML metadata `), e.g. ``ObjectStorage`` for ``falMedia`` in testing instance. Removing single file reference =============================== @@ -333,7 +314,7 @@ Keeping name of the file uploaded by client sometimes may not be wanted - as dev * "upload"={ * "folder"="1:/user_upload/", * "allowedFileExtensions"={"jpg", "jpeg", "png"}, - * "conflictMode"=DuplicationBehavior::RENAME, + * "conflictMode"="rename", * "filenameMask"="static-prefix-[filenameHash][extensionWithDot]", * } * } @@ -372,7 +353,7 @@ It is possible to customize hash algorithm used to generate ``contentHash`` and * "upload"={ * "folder"="1:/user_upload/", * "allowedFileExtensions"={"jpg", "jpeg", "png"}, - * "conflictMode"=DuplicationBehavior::RENAME, + * "conflictMode"="rename", * "filenameMask"="static-prefix-[filenameHash]-[contentHash][extensionWithDot]", * "contentHashAlgorithm"="sha1", * "filenameHashAlgorithm"="sha1", diff --git a/Documentation/Index.rst b/Documentation/Index.rst index 8c44f75d..f46830aa 100644 --- a/Documentation/Index.rst +++ b/Documentation/Index.rst @@ -40,9 +40,9 @@ If you find an error or something is missing, please: `Report a Problem **Table of Contents** .. toctree:: - :maxdepth: 5 - :titlesonly: - :glob: + :maxdepth: 5 + :titlesonly: + :glob: GettingStarted/Index Operations/Index diff --git a/Documentation/Integration/Index.rst b/Documentation/Integration/Index.rst index e6ddbb4f..496643d8 100644 --- a/Documentation/Integration/Index.rst +++ b/Documentation/Integration/Index.rst @@ -4,34 +4,80 @@ Integration ============ -@todo - write docs - Integration with other extensions ==================================== -@todo - write docs -@todo - write docs: configure serializer for classes which can not be override :ref:`serialization_yaml_metadata` +Models of other extensions (e.g. ``EXT:news``) can be exposed in the API without changing their code: + +#. Create a model in your extension which extends the model of the other extension and add ``@T3api\ApiResource`` annotation to it. +#. Map your model to the table of the original model with Extbase persistence configuration (``Configuration/Extbase/Persistence/Classes.php``). If your class should also be returned by the original repositories and relations, register it as ``subclasses`` / ``recordType`` as described in TYPO3 documentation. +#. Configure serialization of properties of the original class with :ref:`YAML metadata ` - annotations can not be added to classes you do not own. News extension - Example integration ====================================== -@todo - write docs t3apinews +Testing instance of t3api contains complete integration with ``EXT:news`` - extension ``t3apinews`` (see ``.ddev/test/files/src/t3apinews`` in t3api repository). It is a good starting point for your own integration: + +- ``Classes/Domain/Model/News.php``, ``Category.php``, ``Tag.php`` - API resources extending ``EXT:news`` models, with operations and :ref:`filters `. +- ``Classes/Domain/Model/File.php`` - resource used for :ref:`file upload `. +- ``Configuration/Extbase/Persistence/Classes.php`` - mapping of the models to ``EXT:news`` tables. +- ``Resources/Private/Serializer/GeorgRinger.News/*.yml`` - :ref:`YAML metadata ` with serialization groups for ``EXT:news`` models, registered in ``ext_localconf.php``. + +.. code-block:: php + :caption: EXT:t3apinews/Configuration/Extbase/Persistence/Classes.php + + use SourceBroker\T3apinews\Domain\Model\Category; + use SourceBroker\T3apinews\Domain\Model\News; + use SourceBroker\T3apinews\Domain\Model\Tag; + + return [ + News::class => [ + 'tableName' => 'tx_news_domain_model_news', + ], + Tag::class => [ + 'tableName' => 'tx_news_domain_model_tag', + ], + Category::class => [ + 'tableName' => 'sys_category', + ], + ]; + +.. admonition:: Real examples. Run "ddev restart && ddev ci 14" and try those links below. + + * | News, categories and tags of ``EXT:news`` exposed by ``t3apinews``: + | `https://14.t3api.ddev.site/_api/news/news `__ + | `https://14.t3api.ddev.site/_api/news/categories `__ + | `https://14.t3api.ddev.site/_api/news/tags `__ + | Inline output ====================================== -@todo - write docs: If you would like to include your JSON directly in TYPO3 HTML output (e.g. to omit waiting for initial request -to API) you can use xxxViewHelper as follows: +If you would like to include API response directly in TYPO3 HTML output (e.g. to avoid waiting for initial request +to the API in your frontend application) you can use ``t3api:inline`` ViewHelper. It processes the request internally, +without HTTP request, and returns the same JSON which would be returned by the endpoint. + +Arguments: + +- ``route`` (``string``, required) - path of the endpoint, without API base path, e.g. ``news/news`` or ``news/news/1``. +- ``params`` (``array``) - query params, e.g. filters: ``{search: 'minima', order: {title: 'asc'}}``. +- ``itemsPerPage`` (``int``) - number of items per page (sent as ``itemsPerPage`` param). +- ``page`` (``int``) - page number (sent as ``page`` param). .. code-block:: html - + - + +.. important:: + + Output of the ViewHelper is HTML-escaped by Fluid, so wrap it in ```` when it is placed inside ``