Giter VIP home page Giter VIP logo

api-platform-understanding's Introduction

api-platform-understanding

Цель проекта:

  • разобраться в ApiPlatform с помощью тестов
  • запускать данные тесты в своем CI для гарантии, что в обновленной версии ApiPlatform не поломали базовый функционал

Как изучать:

Важные моменты:

  • если запустить тесты, то можно увидеть, что тесты пройдены, но печатаются какие-то ошибки, это следствие того, что используется Symfony KernelBrowser, подробности в OneItemTest::testDeleteNonExistentBook()
  • нельзя доверять валидацию объектов симфони-валидатору, т.к. он не применяется если объекты наполняются внутри приложения

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

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

lib tests

Настройки

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

api_platform.eager_loading = true

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

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

Стало:

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

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

MaxDepth

Если все-таки потребуется получать сразу коллекцию, то укажем отдельную группу "Company:apiRead" для свойства $products, на необходимую глубину отношений с помощью Symfony\Component\Serializer\Annotation\MaxDepth + enable_max_depth:

/**
 * @ApiPlatform(normalizationContext={"groups"={"read"}, "enable_max_depth"=true})
 */
class Book {
    /**
     * @ORM\ManyToOne(targetEntity="User", inversedBy="books")
     * @Groups({"read"})
     * @MaxDepth(1)
     */
    private $author;
}

Еще есть интересная аннотация: @ApiSubresource(maxDepth=1)

skip_null_values (default: true)

А если указать "skip_null_values": false в:

  • denormalizationContext - то в реквесте все свойства объекта должны быть указаны, даже nullable
  • normalizationContext - то в респонсе все свойства объекта будут присутствовать, даже со значением null

Где указывается:

 * @ApiResource(
 *     normalizationContext={
 *         "groups": {"apiRead"},
 *         "skip_null_values": false
 *     },
 *     denormalizationContext={
 *         "groups": {"apiWrite"},
 *         "skip_null_values": false
 *     }
 * )

Запретить доступ к чтению и записи в поле, но дать доступ на чтение IRI можно так:

use ApiPlatform\Core\Annotation\ApiProperty;
 /**
 * @Groups({"apiRead", "apiWrite"})
 * @ApiProperty(readableLink=false, writableLink=false)
 */
private Book $parent;

Таким образом можно ограничить рекурсию - например ссылка на другую сущность или на запись родителя.

Разрешить искать и сортировать записи можно так:

@ApiFilter(SearchFilter::class, strategy="ipartial")
@ApiFilter(OrderFilter::class, properties={"id", "author", "publicationDate"})

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

@ApiProperty(attributes={"fetchEager": false, "fetchable": false})
@SerializedName("public_field_name")

Автоматические группы apiRead/apiWrite

Следующий пример демонстрирует, как можно автоматически (при необходимости динамично) выставлять группы apiRead/apiWrite всем (или некоторым) атрибутам сущностей:

    YaPro\ApiPlatformUnderstanding\Wrapper\ClassMetadataFactoryWrapper:
        # overrides the "decorates" service, but that service is still available as ".inner"
        decorates: 'api_platform.serializer.mapping.class_metadata_factory'
        arguments: [ '@.inner' ]
        decoration_priority: -20
    # чтобы ApiPlatform не кэшировал значение возвращаемое методом ClassMetadataFactoryWrapper::getMetadataFor заменяем:
    'api_platform.cache.metadata.property':
      class: Symfony\Component\Cache\Adapter\ArrayAdapter

См. src/Wrapper/ClassMetadataFactoryWrapper.php

Дополнительные группы сериализации

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

 *         "put_links_box_reuses": {
 *             "method": "PUT",
 *             "path": "/book/{id}/reviews",
 *             "controller": PlaceholderAction::class,
 *             "openapi_context": {"summary": "UPSERT коллекции для указанной сущности"},
 *             "requirements": {"id": "\d+"},
 *             "denormalization_context": {"groups": {"reviews-write"}}
 *         },

Именно так делать не нужно, потому что ApiPlatform из коробки дает возможность делать такой Upsert, но как пример эта ситуация очень хороша, кстати вот полный пример:

Как запустить тесты или поправить их

Предисловие: в репозитории имеется файл composer.lock.dist, необходимый, чтобы понимать, когда и при каких версиях зависимостей текущие тесты успешно проходят, но Вы можете запускать их на основании своего composer.lock файла, это позволит выявлять расхождения в версиях библиотеки ApiPlatform.

Build

docker build -t yapro/api-platform-understanding:latest -f ./Dockerfile ./

Tests

docker run --rm --user=1000:1000 -v $(pwd):/app yapro/api-platform-understanding:latest bash -c "cd /app && \
  bin/console doctrine:schema:drop --full-database --force -v && \
  bin/console doctrine:schema:update --force -v && \
  bin/phpunit tests/Functional"

Если тесты падают, попробуйте выполнить: ln -sf composer.lock.dist composer.lock

Запуск bin/diff-openapi.sh чтобы найти изменения в OAS-контракте

docker run --rm --user=1000:1000 -v $(pwd):/app yapro/api-platform-understanding:latest bash -c "cd /app && \
  COMPOSER_MEMORY_LIMIT=-1 composer install --optimize-autoloader --no-scripts --no-interaction && \
  bin/console doctrine:schema:drop --full-database --force -v && \
  bin/console doctrine:schema:update --force -v && \
  bin/diff-openapi.sh"

Текущая схема находится по адресу public/oas/api-platform.yaml и её можно открыть в редакторе https://editor.swagger.io/

Перегенерировать схему можно так:

docker run --rm --user=1000:1000 -v $(pwd):/app yapro/api-platform-understanding:latest bash -c "cd /app && \
  COMPOSER_MEMORY_LIMIT=-1 composer install --optimize-autoloader --no-scripts --no-interaction && \
  bin/console doctrine:schema:drop --full-database --force -v && \
  bin/console doctrine:schema:update --force -v && \
  bin/console api:openapi:export --yaml --env=test --output=public/oas/api-platform.yaml"

Dev

А еще можно выполнить команду ниже и смотреть по адресу: http://127.0.0.1:8000/api

docker run -it --rm --user=$(id -u):$(id -g) --net=host -v $(pwd):/app -w /app yapro/api-platform-understanding:latest bash
COMPOSER_MEMORY_LIMIT=-1 composer install -o && \
bin/console doctrine:schema:drop --full-database --force -v && \
bin/console doctrine:schema:update --force -v && \
php -S 127.0.0.1:8000 -t public

Можно заходить по адресу: http://127.0.0.1:8000/api

Debug PHP:

В phpunit.xml.dist поменяйте APP_ENV на test и запустите:

docker run --rm --user=$(id -u):$(id -g) -v $(pwd):/app --add-host=host.docker.internal:host-gateway yapro/api-platform-understanding:latest bash -c "cd /app && \
  COMPOSER_MEMORY_LIMIT=-1 composer install --optimize-autoloader --no-scripts --no-interaction && \
  bin/console doctrine:schema:drop --full-database --force -v && \
  bin/console doctrine:schema:update --force -v && \
  PHP_IDE_CONFIG=\"serverName=common\" \
  XDEBUG_SESSION=common \
  XDEBUG_MODE=debug \
  XDEBUG_CONFIG=\"max_nesting_level=200 client_port=9003 client_host=host.docker.internal\" \
  bin/phpunit --cache-result-file=/tmp/phpunit.cache tests/Functional"

Если с xdebug что-то не получается, напишите: php -dxdebug.log='/tmp/xdebug.log' и смотрите в лог.

Cs-Fixer: fix code

docker run --user=1000:1000 --rm -v $(pwd):/app -w /app yapro/api-platform-understanding:latest ./php-cs-fixer.phar fix --config=.php-cs-fixer.dist.php -v --using-cache=no --allow-risky=yes

PhpMd: update rules

docker run --user=1000:1000 --rm -v $(pwd):/app -w /app yapro/api-platform-understanding:latest ./phpmd.phar . text phpmd.xml --exclude .github/workflows,vendor,var,public/bundles --strict --generate-baseline

api-platform-understanding's People

Contributors

yapro avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.