API Platform

На данной странице представлен список интересных возможностей по примерам.

Фильтрация через связанные сущности

Если объявить аннотацию фильтрации:

то получаем возможность фильтровать данные через связанные сущности (любой вложенности по связям):

Нюанс 1

можно не указывать id в конце свойства для фильтрации:

 * @ApiFilter(SearchFilter::class, properties={
 *     "products.offerProducts.offer": "exact"
 * })

но тогда в запросе тоже писать id не нужно (правильный пример):

curl --insecure -X GET "https://localhost/api/categories?products.offerProducts.offer=22&page=1&itemsPerPage=30" -H  "accept: application/json"

важно: имейте ввиду, что если в анотации не пишите id, а в запросе пишите, то фильтрация не сработает и вернутся неотфильтрованные данные.

Нюанс 2

возвращается всегда уникальный список категорий (неважно сколько продуктов участвует в связях products.offerProducts )

Нюанс 3

когда используют SearchFilter без указания полей, включается фильтрация по всем полям сущности, но если указать какое-либо поле (как в примере выше), то фильтрация по всем полям выключается (доступна только фильтрация по указанным полям). 

Сортировка

Если объявить аннотацию сортировки:

то получим возможность сортировать данные:

результат сортировки соответствует ожиданию (обратите внимание на поле parentId в результате).

Нюанс

порядок указания полей сортировки влияет на результат (сначала сортируем по parentId, потом по id):

curl --insecure -X GET "https://localhost/api/categories?order%5BparentId%5D=desc&order%5Bid%5D=desc&page=1&itemsPerPage=30" -H  "accept: application/json"

таким образом будет выполнен SQL: ORDER BY parentId DESC, id DESC

Получение значений только указанных полей

use ApiPlatform\Core\Serializer\Filter\PropertyFilter;
/**
 * @ApiResource(
 * @ApiFilter(PropertyFilter::class)
 */
class CheeseListing

запрос:

?properties[]=title&properties[]=shortDescription

Детали: 1 - 2

Create multiple entries (records)

Рассмотрим несколько примеров взаимодействия разных сущностей.

Примеры на основе двух сущностей SiteGroup и Site

Создание группы и сайта одновременно:

curl -X POST "http://any.local:8888/api/site_groups" -H "accept: application/ld+json" -H "Content-Type: application/ld+json" \
-d "{\"name\":\"group 0\",\"sites\":[{\"name\":\"site 0\"}]}"
201 Response body
{
  "@context": "/api/contexts/SiteGroup",
  "@id": "/api/site_groups/27",
  "@type": "SiteGroup",
  "name": "group 0",
  "sites": [
    {
      "@id": "/api/sites/23",
      "@type": "Site",
      "name": "site 0",
      "id": 23,
      "siteGroupId": 27,
      "siteGroupName": "group 0"
    }
  ],
  "id": 27
}

Создание группы и сайта одновременно (повторно):

curl -X POST "http://any.local:8888/api/site_groups" -H "accept: application/ld+json" -H "Content-Type: application/ld+json" \
-d '{"name":"group 0","sites":[{"name":"site 0", "domain": "site.ru"}]}'
409 Response body
{
  "title": "The object already exist"
}

Создание группы с ранее созданным сайтом:

curl -X POST "http://any.local:8888/api/site_groups" -H "accept: application/ld+json" -H "Content-Type: application/ld+json" \
-d '{"name":"group 1","sites":["/api/sites/2"]}'
201 Response body
{
  "@context": "/api/contexts/SiteGroup",
  "@id": "/api/site_groups/27",
  "@type": "SiteGroup",
  "name": "group 1",
  "sites": [
    {
      "@id": "/api/sites/23",
      "@type": "Site",
      "name": "site 0",
      "id": 23,
      "siteGroupId": 27,
      "siteGroupName": "group 0"
    }
  ],
  "id": 27
}

Изменить имя группы + список сайтов входящих в данную группу:

curl -X PUT "http://any.local:8888/api/site_groups/1" -H "accept: application/ld+json" -H "Content-Type: application/ld+json" \
-d "{\"name\":\"new group name\",\"sites\":[\"/api/sites/1\"]}"

сразу 2 сайта в группе:

curl -X PUT "http://any.local:8888/api/site_groups/1" -H "accept: application/ld+json" -H "Content-Type: application/ld+json" \
-d "{\"name\":\"new group name\",\"sites\":[\"/api/sites/1\",\"/api/sites/2\"]}"

Изменить имя группы + удалить все сайты из группы:

curl -X PUT "http://any.local:8888/api/site_groups/1" -H "accept: application/ld+json" -H "Content-Type: application/ld+json" \
-d "{\"name\":\"string\",\"sites\":[]}"

Примеры на основе двух сущностей PopupWindow и ActionConfiguration

Создать Попап+добавить к нему группу конфигурации:

curl -X POST "http://any.local:8888/api/popup_windows" -H "accept: application/ld+json" -H "Content-Type: application/ld+json" \
-d "{\"name\":\"string\",\"html\":\"\",\"javascript\":\"string\",\"settings\":\"string\",\"actionConfigurations\":[\"/api/action_configurations/1\"]}"

curl -X POST "http://any.local:8888/api/popup_windows" -H "accept: application/ld+json" -H "Content-Type: application/ld+json" \
-d '{"name":"group 1","sites":["/api/sites/2"]}'

Редактирование существующего события:

curl -i 'http://any.local:8888/api/action_configurations/2' -X PUT -H 'accept: application/json, text/plain, */*' -H 'content-type: application/json' \
--data-binary '{"name":"Yapro.ru"}' -H 'X-ECOM-DEBUG: true'

Удаление popup_window:

curl -X DELETE "http://any.local:8888/api/popup_windows/11" -H "accept: */*"
204 Response

Пагинация от клиента:

curl 'http://localhost:8888/api/popup_windows?page=1&pagination=true&itemsPerPage=20'

Работа с коллекциями

  1. Чтобы работал метод PUT и PATCH для свойства, которое является коллекцией сущностей, в сущность должны быть добавлены 2 метода: addPropertyName и removePropertyName (PropertyName это имя свойства класса сущности).
  2. Чтобы работала очистка, через PUT и PATCH в аннотации к свойству нужно добавить orphanRemoval=true.

Настройки

api_platform.eager_loading = true заставляет \ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\EagerLoadingExtension использовать sql-join'ы для получения данных всех связанных сущностей, при этом вложенность никак ограничить нельзя (все что связано, все будет вытащено из бд).

Уменьшить вложенность данных в ответе можно только с помощью аннотаций групп симфони-сериализатора. У себя в проекте мы решили не указывать группу нормализации для свойств-отношений (свойств коллекций в сущности) которые имеют очень большую вложенность, т.е. меняем это:

/**
 * @Groups({"apiRead", "apiWrite"})
 */
private Collection $mainProducts;

на это:

/**
 * @Groups({"apiWrite"})
 */
private Collection $mainProducts;

Т.е. отношения запрашивать будем из самой коллекции, например: "/api/bundle_main_products" c фильтром по свойству bundle.

Но, если потребуется все-таки получать сразу коллекцию, то укажем группу "Bundle:apiRead" для свойств, на необходимую глубину отношений.

Переопределение роутов

Если вдруг (непонятно зачем) появится такая необходимость, то сделать это можно так:

/**
 * @Route(path="/admin/api/v1")
 */
class TemplatesController extends AbstractController
{
    /**
     * @Route(
     *     name="get_template",
     *     path="/templates/{id}",
     *     methods={"GET"},
     *     defaults={
     *         "_api_resource_class": Template::class,
     *         "_api_item_operation_name": "get_template"
     *     },
     *     requirements={"id": "\d+"}
     * )
     *
     * @return JsonResponse
     */
    public function getTemplateAction(Template $data)
    {
        return new JsonResponse($this->templateTransformer->transformFull($data));
    }

Как сделать запрос с частичной выборкой данных

Например:

  • хотите получить указанные поля объекта
  • хотите получить объект со всеми связанными объектами за исключением связи с объектом класса Х

К сожалению, из коробки такого функционала нет, но его можно реализовать с помощью дата-провайдера.

Интересные аннотации

@SerializedName("site_group_id")
@ApiSubresource(maxDepth=1)
@ApiProperty(attributes={"fetchEager": false, "fetchable": false})

CORS battle

Сравним два API-подхода выполнения запросов из браузера.

fetch("http://95.165.102.48:8888/login",
  {
    "credentials": "include",
    "headers": {
      "accept":"application/json, text/plain, */*",
      "content-type": "application/json"
    },
    "referrer": "http://localhost:4200/login",
    "referrerPolicy": "no-referrer-when-downgrade",
    "body": "{\"email\":\"igor@fox.pro\",\"password\":\"igor@fox.pro\"}",
    "method": "POST",
    "mode": "cors"
  }
);

с помощью JavaScript (вариант 1):

var xmlhttp = new XMLHttpRequest();
xmlhttp.withCredentials = true; 
xmlhttp.open("POST", "http://localhost:4200/login");
xmlhttp.setRequestHeader("Accept", "application/json");
xmlhttp.setRequestHeader("Content-Type", "application/json");
xmlhttp.send(JSON.stringify({email:"igor@fox.pro", password:"igor@fox.pro"}))

с помощью JavaScript (вариант 2):

var jbody = {
  "name": "Просто значение",
  "visitorType": 0
};
var xmlhttp = new XMLHttpRequest();
xmlhttp.withCredentials = true;
xmlhttp.open("POST", "http://any.local:8888/api/action_configurations");
xmlhttp.setRequestHeader("Content-Type", "application/json");
xmlhttp.send(JSON.stringify(jbody));

На этом все, всем удачки.

p.s. create multiple resources


15.01.2011 10:06

Комментарии

Комментариев пока нет.


Copyrights © 2007 - 2026 YaPro.Ru