yapro/apiration-bundle

There is no license information available for the latest version (v1.0.6) of this package.

Api tool

v1.0.6 2023-04-27 08:26 UTC

This package is not auto-updated.

Last update: 2024-04-24 09:53:53 UTC


README

The library casts a json-request to your object and casts your object to a json-response.

lib tests

How to use

  1. Make an ApiRationObject, example SimpleModel
<?php

declare(strict_types=1);

namespace App;

use YaPro\ApiRationBundle\Marker\ApiRationObjectInterface;

class SimpleModel implements ApiRationObjectInterface
{
    private string $varString;
    private bool $varBoolean;

    public function __construct(string $varString, bool $varBoolean) {
        $this->varString = $varString;
        $this->varBoolean = $varBoolean;
    }

    public function getVarString(): string
    {
        return $this->varString;
    }

    public function isVarBoolean(): bool
    {
        return $this->varBoolean;
    }
}
  1. Use the SimpleModel in controller action (specify the namespace completely)
<?php

declare(strict_types=1);

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class AppController extends AbstractController
{
    /**
     * @Route("/api-json-test/simple-model")
     *
     * @param \App\SimpleModel $model
     *
     * @return SimpleModel
     */
    public function getSimpleModel(SimpleModel $model, Request $request): SimpleModel
    {
        return $model;
    }
}
  1. Make the curl request
curl -X GET "localhost/api-json-test/simple-model" -H 'Content-Type: application/json' -d'
{
  "varString": "string",
  "varBoolean": "true"
}
'
  1. Get the answer
{"varString":"string","varBoolean":true}

As you can see, any object which implements the ApiRationObjectInterface is automatically converted to json.

More examples and tests

JsonRequest - the simple way to work with Request

<?php

declare(strict_types=1);

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use YaPro\ApiRationBundle\Request\JsonRequest;

class AppController extends AbstractController
{
    /**
     * @Route("/search", methods={"POST"})
     *
     * @param JsonRequest           $request
     * @param ArticleRepository $articleRepository
     *
     * @return JsonResponse
     */
    public function search(JsonRequest $request): JsonResponse
    {
        $userAddresses = $request->getArray(); // request: ["foo@go.com", "bar@go.com"]
        // OR:
        $myFieldValue = $request->getObject()->myField; // request: {"myField": "my value"}

        return $this->json([]);
    }

If you need to create a JsonLd response for the creation operation, try:

        return new ResourceCreatedJsonLdResponse(
            $article->getId(),
            [
                'title' => $article->getTitle(),
                'text' => $article->getText(),
            ]
        );

If you need to create a JsonLd response for an update operation, try ResourceUpdatedJsonLdResponse.

How to make JsonLd Response (hydra:Collection)

CollectionJsonLdResponse is automatically support pagination:

<?php

declare(strict_types=1);

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use YaPro\ApiRationBundle\Response\JsonLd\CollectionJsonLdResponse;

class AppController extends AbstractController
{
    /**
     * @Route("/list", methods={"GET"})
     */
    public function list(Request $request, ArticleRepository $articleRepository, SerializerInterface $serializer)
    {
        $collection = $articleRepository->getList();
        /*
            function getList(): array {
                return $this->createQueryBuilder('t')
                    ->select('t')
                    ->where('t.difficulty > 0')
                    ->getQuery()
                    ->getResult();
            }
        */
        $items = $serializer->normalize($collection, null, ['groups' => 'apiRead']);
        return (new CollectionJsonLdResponse($request))->initData($items);
    }
    
    /**
     * @Route("/search", methods={"GET"})
     *
     * @param Request           $request
     * @param ArticleRepository $articleRepository
     *
     * @return CollectionJsonLdResponse
     */
    public function search(Request $request, ArticleRepository $articleRepository): CollectionJsonLdResponse
    {
        $response = new CollectionJsonLdResponse($request);
        $searchValue = $request->query->get('searchValue', '');
        if (empty($searchValue)) {
            return $response;
        }

        $items = $this->getEntityManager()->getConnection()->fetchAll("
            SELECT 
                id,
                title
            FROM Article
            WHERE title LIKE :searchValue
            ORDER BY createdAt DESC
            LIMIT " . $response->getOffset() . ", " . $response->getLimit() . "
        ", [
             'searchValue' => $searchValue,
         ]);

        return $response->initData(
            $items,
            $this->getTotalItems()
        );
    }
}

Notice: symfony 6.3 is supports similar features, but the bundle supports more functionality, for example, responding to an invalid request by throwing a BadRequestException:

$message = 'Validation errors';
$errors = [
    'field_name' => 'The name cannot contain a number',
    'field_lastname' => [
        'The name cannot contain a number',
        'Name must be at least 2 characters long',
    ],
];
throw new BadRequestException($message, $errors);

and the client will receive the response with the status 400:

{
    "message": "Validation errors",
    "errors": [
        {
            "fieldName": "field_name",
            "messages": [
                "The name cannot contain a number"
            ]
        },
        {
            "fieldName": "field_lastname",
            "messages": [
                "The name cannot contain a number",
                "Name must be at least 2 characters long"
            ]
        }
    ]
}

More examples.

Installation on PHP 7

Add as a requirement in your composer.json file or run for prod:

composer require yapro/apiration-bundle laminas/laminas-code:3.4.1

Installation on PHP 8

Add as a requirement in your composer.json file or run for prod:

composer require yapro/apiration-bundle

As dev:

composer require yapro/apiration-bundle dev-master

Dev

docker build -t yapro/apiration-bundle:latest -f ./Dockerfile ./
docker run --rm --user=$(id -u):$(id -g) --add-host=host.docker.internal:host-gateway -it --rm -v $(pwd):/app -w /app yapro/apiration-bundle:latest bash
cp -f composer.lock.php7 composer.lock
composer install -o

Debug tests:

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" \
vendor/bin/simple-phpunit --cache-result-file=/tmp/phpunit.cache -v --stderr --stop-on-incomplete --stop-on-defect \
--stop-on-failure --stop-on-warning --fail-on-warning --stop-on-risky --fail-on-risky --testsuite=Unit,Functional

If you need php8:

docker build -t yapro/apiration-bundle:latest --build-arg "PHP_VERSION=8" -f ./Dockerfile ./
cp -f composer.lock.php8 composer.lock

Cs-Fixer:

wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v3.8.0/php-cs-fixer.phar && chmod +x ./php-cs-fixer.phar
./php-cs-fixer.phar fix --config=.php-cs-fixer.dist.php -v --using-cache=no --allow-risky=yes

Update phpmd rules:

wget https://github.com/phpmd/phpmd/releases/download/2.12.0/phpmd.phar && chmod +x ./phpmd.phar
./phpmd.phar . text phpmd.xml --exclude .github/workflows,vendor --strict --generate-baseline

CORS (Optional functionality)

    YaPro\ApiRationBundle\Cors\CorsResolver:
        tags:
            - { name: kernel.event_subscriber }

If the library doesn't work, try to add the following lines to services.yml:

    Symfony\Component\Serializer\Encoder\JsonDecode: ~
    Symfony\Component\Serializer\Encoder\JsonEncode: ~