Skip to content

Latest commit

 

History

History
118 lines (99 loc) · 5.01 KB

File metadata and controls

118 lines (99 loc) · 5.01 KB
title Extending Endpoints
description Add custom REST API endpoints to the Studio Backend.

Extending Endpoints

Add custom API endpoints by extending AbstractApiController and annotating with OpenAPI attributes. All endpoints must include OpenAPI documentation so they appear in the Swagger UI.

Implementation Steps

  1. Extend AbstractApiController - provides the base API_PATH and JSON serialization via the Symfony serializer.

  2. Define the route using Symfony's #[Route] attribute.

  3. Add security with #[IsGranted] - the UserPermissionVoter checks the current user's permissions and returns a 403 response if denied.

  4. Add OpenAPI attributes - use the matching HTTP method attribute (e.g. #[Get], #[Post]). Include at least one response annotation. Use #[DefaultResponses] for standard error responses. For query parameters, use #[MapQueryString]; for request bodies, use #[MapRequestPayload].

  5. Add custom OpenAPI schemas if needed. See Extending OpenAPI for creating reusable attribute classes.

We try to leverage symfony functionality as much as possible for parameters with #[MapQueryString] or #[MapRequestPayload] attributes.

Example

<?php
declare(strict_types=1);
namespace App\Note\Controller;

use OpenApi\Attributes\Get;
use Pimcore\Bundle\StudioBackendBundle\Controller\AbstractApiController;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidFilterException;
use Pimcore\Bundle\StudioBackendBundle\Note\Attribute\Parameter\Query\NoteSortByParameter;
use Pimcore\Bundle\StudioBackendBundle\Note\MappedParameter\NoteElementParameters;
use Pimcore\Bundle\StudioBackendBundle\Note\MappedParameter\NoteParameters;
use Pimcore\Bundle\StudioBackendBundle\Note\Schema\Note;
use Pimcore\Bundle\StudioBackendBundle\Note\Service\NoteServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Query\FieldFilterParameter;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Query\FilterParameter;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Query\PageParameter;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Query\PageSizeParameter;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Query\SortOrderParameter;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Property\GenericCollection;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\Content\CollectionJson;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\SuccessResponse;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions;
use Pimcore\Bundle\StudioBackendBundle\Util\Trait\PaginatedResponseTrait;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Attribute\MapQueryString;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Serializer\SerializerInterface;


final class CollectionController extends AbstractApiController
{
    use PaginatedResponseTrait;

    public function __construct(
        SerializerInterface $serializer,
        private readonly NoteServiceInterface $noteService
    ) {
        parent::__construct($serializer);
    }

    /**
     * @throws InvalidFilterException
     */
    #[Route('/notes', name: 'pimcore_studio_api_get_notes', methods: ['GET'])]
    #[IsGranted(UserPermissions::NOTES_EVENTS->value)]
    #[Get(
        path: self::PREFIX . '/notes',
        operationId: 'note_get_collection',
        description: 'note_get_collection_description',
        summary: 'note_get_collection_summary',
        tags: [Tags::Notes->name]
    )]
    #[PageParameter]
    #[PageSizeParameter(50)]
    #[NoteSortByParameter]
    #[SortOrderParameter]
    #[FilterParameter('notes')]
    #[FieldFilterParameter]
    #[SuccessResponse(
        description: 'note_get_collection_success_response',
        content: new CollectionJson(new GenericCollection(Note::class))
    )]
    #[DefaultResponses([
        HttpResponseCodes::UNAUTHORIZED,
    ])]
    public function getNotes(
        #[MapQueryString] NoteParameters $parameters = new NoteParameters()
    ): JsonResponse {
        $collection = $this->noteService->listNotes(new NoteElementParameters(), $parameters);

        return $this->getPaginatedCollection(
            $this->serializer,
            $collection->getItems(),
            $collection->getTotalItems()
        );
    }
}

Available classes for simplification

  • Collection - Predefined response object for collections with the total item count and items.
  • HttpResponseCodes - Predefined HTTP response codes.