paysera/util-raml-code-generator

从 RAML 规范生成代码

11.8.0 2024-05-29 06:45 UTC

README

Latest Version Build Status

util-raml-code-generator 可以根据指定的 RAML 定义生成代码包。目前此实用工具可以

  • 自动生成和发布
    • PHP REST API 客户端
    • JavaScript REST API 客户端
  • 生成 Symfony Api Bundle

要求

  • PHP 7.2
  • Composer <= 2.2.9,更高版本尚未支持,正在开发中

安装

  • 克隆仓库并运行 composer install

目录表

  1. RAML 结构
    1. api.raml
    2. types/category.raml
    3. types/category-result.raml
    4. traits/category-filter.raml
  2. 自定义注解
    1. (generator_method_name_override)
  3. 生成和发布客户端
    1. config.json 格式
  4. 生成 JavaScript REST 客户端
    1. 文件树
    2. src/entity/Category.js
    3. src/entity/CategoryFilter.js
    4. src/entity/CategoryResult.js
    5. src/service/createClient.js
    6. src/service/CategoryClient.js
  5. 生成 PHP REST 客户端
    1. 文件树
    2. src/CategoryClient.php
    3. src/ClientFactory.php
    4. src/Entity/Category.php
    5. src/Entity/CategoryFilter.php
    6. src/Entity/CategoryResult.php
  6. 生成 Symfony Bundle

RAML 结构

让我们有以下 RAML 结构

raml
└───category
    |   api.raml
    |
    ├───traits
    |       category-filter.raml
    |
    ├───types
    |       category.raml
    |       category-result.raml

以下内容

raml/category/api.raml

#%RAML 1.0
title: Category API
version: v1
baseUri: https://example.com/category/rest/v1
mediaType: application/json
protocols: [ HTTPS ]

uses:
  Paysera: https://raw.githubusercontent.com/paysera/lib-raml-common/master/rest.raml

types:
  Category: !include types/category.raml
  CategoryResult: !include types/category-result.raml

traits:
  CategoryFilter: !include traits/category-filter.raml

/categories:
  get:
    description: Get categories list
    is:
      - CategoryFilter
  post:
    description: Create category
    body:
      application/json:
        type: Category
    responses:
      200:
        body:
          application/json:
            type: CategoryResult
  /{id}:
    uriParameters:
      id:
        type: string
    put:
      description: Update category
      body:
        application/json:
          type: Category
      responses:
        200:
          body:
            application/json:
              type: Category
    delete:
      description: Delete category
    /enable:
      put:
        description: Enable category
        responses:
          200:
            body:
              application/json:
                type: Category
    /disable:
      put:
        description: Disable category
        responses:
          200:
            body:
              application/json:
                type: Category

raml/category/types/category.raml

#%RAML 1.0 DataType
type: object
displayName: Category object
properties:
  id:
    type: string
    required: false
    description: Object id
  parent_id:
    type: string
    required: false
    description: Category parent id
  name:
    type: string
    required: true
    description: Category title
  status:
    type: string
    enum: ["active", "inactive"]
    required: false
    description: Category status

raml/category/traits/category-filter.raml

#%RAML 1.0 Trait
usage: Used where Category filtering is provided
description: Category Result filtering
is:
  - Paysera.Filter
queryParameters:
  parent_id:
    displayName: parent_id
    type: string
    minLength: 1
    example: 123456
    required: false

raml/category/types/category-result.raml

#%RAML 1.0 DataType
type: Paysera.Result
displayName: Category Result object
properties:
  items:
    required: true
    type: array
    items:
      type: Category

自定义注解

(generator_method_name_override)

用于覆盖方法名(适用于任何生成的客户端或包)。

例如

#%RAML 1.0
title: Custom
version: 1.0
baseUri: https://my-api.example.com/rest/v1

annotationTypes:
    generator_method_name_override: string

/something:
  get:
    (generator_method_name_override): customNameForMethod
    responses:
      204: ~

生成和发布客户端

  1. 运行 bin/console release:clients {path_to_config}
    • path_to_configconfig.json 文件的路径。

此命令将

  1. 克隆仓库
  2. raml 生成客户端
  3. 更新变更日志
  4. 显示当前文件与生成文件的差异
  5. 为 JavaScript 生成 dist 文件
  6. 创建提交
  7. 推送提交
  8. 创建标签

config.json 格式

{
  "SomeApiName": {
    "raml_file": "path/to/api.raml",
    "clients": {
      "javascript": {
        "repository": "ssh://some.hostname.com/source/js-some-monorepo.git/packages/some-api-client",
        "library_name": "@vendor/some-api-client",
        "client_name": "SomeApiClient"
      },
      "php": {
          "repository": "ssh://some.hostname.com/source/some-api-client-dedicated-repo.git",
          "library_name": "vendor/lib-some-api-client",
          "namespace": "Vendor\\Client\\SomeApiClient"
      }
    }
  }
}

生成 JavaScript REST 客户端

  1. 运行 bin/console js-generator:package {path_to_raml} {output_dir} {client_name} --library_name={library_name}
    • path_to_ramlraml 文件的路径。
    • output_dir 是放置生成文件的目录。
    • client_name 是您主要 JavaScript 客户端的名字。
    • library_name 是生成库的可选名字,例如 @paysera/some-api-client
  2. 检查 {output_dir} 目录中的输出。

JavaScript 客户端文件树

output_dir 中,您应该期望生成以下文件

|   .babelrc.js
|   .eslintrc.js
|   .gitignore
|   package.json
|   README.md
|   webpack.config.js
|
└───src
    |   index.js
    |   angular.module.js
    |
    ├───entity
    |       Category.js
    |       CategoryFilter.js
    |       CategoryResult.js
    |
    ├───service
    |       CategoryClient.js
    |       createClient.js

包含以下内容和服务的 src 目录

src/entity/Category.js

import { Entity } from '@paysera/http-client-common';

class Category extends Entity {
    constructor(data = {}) {
        super(data);
    }

    /**
     * @return {string}|null
     */
    getId() {
        return this.get('id');
    }

    /**
     * @param {string} id
     */
    setId(id) {
        this.set('id', id);
    }

    /**
     * @return {string}|null
     */
    getParentId() {
        return this.get('parent_id');
    }

    /**
     * @param {string} parentId
     */
    setParentId(parentId) {
        this.set('parent_id', parentId);
    }

    /**
     * @return {string}
     */
    getName() {
        return this.get('name');
    }

    /**
     * @param {string} name
     */
    setName(name) {
        this.set('name', name);
    }

    /**
     * @return {string}|null
     */
    getStatus() {
        return this.get('status');
    }

    /**
     * @param {string} status
     */
    setStatus(status) {
        this.set('status', status);
    }
}

export default Category;

src/entity/CategoryFilter.js

import { Filter } from '@paysera/http-client-common';

class CategoryFilter extends Filter {
    /**
     * @return {string}|null
     */
    getParentId() {
        return this.get('parent_id');
    }

    /**
     * @param {string} parentId
     */
    setParentId(parentId) {
        this.set('parent_id', parentId);
    }
}

export default CategoryFilter;

src/entity/CategoryResult.js

import Category from './Category';
import { Result } from '@paysera/http-client-common';

/* eslint class-methods-use-this: ["error", { "exceptMethods": ["createItem"] }] */
class CategoryResult extends Result {
    /**
     * @param {Array} data
     * @returns {Category}
     */
    createItem(data) {
        return new Category(data);
    }
}

export default CategoryResult;

src/service/createClient.js

import { createClient } from '@paysera/http-client-common';
import CategoryClient from './CategoryClient';

/**
 * @param {string} baseURL
 * @param {[]|null} middleware
 * @param {object} options
 *
 * @returns {CategoryClient}
 */
/* eslint import/prefer-default-export: ["off"] */
export const createAClient = ({
    baseURL = 'https://example.com/category/rest/v1/',
    middleware = null,
    options = {}
}) => {
    const defaultUrlParameters = {};
    
    if (Object.prototype.hasOwnProperty.call(options, 'urlParameters')) {
        const { urlParameters } = options;
        for (let [key, value] of Object.entries(defaultUrlParameters)) {
            if (!Object.prototype.hasOwnProperty.call(urlParameters, key)) {
                urlParameters[key] = value;
            }
        }
        options.urlParameters = urlParameters;
    }

    return new CategoryClient(
        createClient({
            baseURL,
            middleware,
            options
        })
    )
};

src/service/CategoryClient.js

import { createRequest, ClientWrapper } from '@paysera/http-client-common';

import Category from '../entity/Category';
import CategoryFilter from '../entity/CategoryFilter';
import CategoryResult from '../entity/CategoryResult';

class CategoryClient {
    /**
     * @param {ClientWrapper} client
     */
    constructor(client) {
        this.client = client;
    }

    /**
     * Enable category
     * PUT /categories/{id}/enable
     *
     * @param {string} id
     * @return {Promise.<Category>}
     */
    enableCategory(id) {
        const request = createRequest(
            'PUT',
            `categories/${encodeURIComponent(id)}/enable`,
            null,
        );

        return this.client
            .performRequest(request)
            .then(data => new Category(data));
    }

    /**
     * Disable category
     * PUT /categories/{id}/disable
     *
     * @param {string} id
     * @return {Promise.<Category>}
     */
    disableCategory(id) {
        const request = createRequest(
            'PUT',
            `categories/${encodeURIComponent(id)}/disable`,
            null,
        );

        return this.client
            .performRequest(request)
            .then(data => new Category(data));
    }

    /**
     * Update category
     * PUT /categories/{id}
     *
     * @param {string} id
     * @param {Category} category
     * @return {Promise.<Category>}
     */
    updateCategory(id, category) {
        const request = createRequest(
            'PUT',
            `categories/${encodeURIComponent(id)}`,
            category,
        );

        return this.client
            .performRequest(request)
            .then(data => new Category(data));
    }
    /**
     * Delete category
     * DELETE /categories/{id}
     *
     * @param {string} id
     * @return {Promise.<null>}
     */
    deleteCategory(id) {
        const request = createRequest(
            'DELETE',
            `categories/${encodeURIComponent(id)}`,
            null,
        );

        return this.client
            .performRequest(request)
            .then(data => null);
    }

    /**
     * Standard SQL-style Result filtering
     * GET /categories
     *
     * @param {CategoryFilter} categoryFilter
     * @return {Promise.<null>}
     */
    getCategories(categoryFilter) {
        const request = createRequest(
            'GET',
            `categories`,
            categoryFilter,
        );

        return this.client
            .performRequest(request)
            .then(data => null);
    }
    /**
     * Create category
     * POST /categories
     *
     * @param {Category} category
     * @return {Promise.<CategoryResult>}
     */
    createCategory(category) {
        const request = createRequest(
            'POST',
            `categories`,
            category,
        );

        return this.client
            .performRequest(request)
            .then(data => new CategoryResult(data, 'items'));
    }

}

export default AClient;

生成 PHP REST 客户端

  1. 运行 bin/console php-generator:rest-client {path_to_raml} {output_dir} {namespace} --library_name={library_name}
    • path_to_ramlraml 文件的路径。
    • output_dir 是放置生成文件的目录。
    • namespace 是客户端的命名空间。
    • library_name 是生成库的可选名字,例如 paysera/lib-some-api-client
  2. 检查 {output_dir} 目录中的输出。

PHP 客户端文件树

output_dir 中,您应该期望生成以下文件

|   composer.json
|   README.md
|
└───src
    |   CategoryClient.php
    |   ClientFactory.php
    |
    ├───Entity
    |       Category.php
    |       CategoryFilter.php
    |       CategoryResult.php

包含以下内容的 src 目录

src/CategoryClient.php

<?php

namespace Paysera\Test\CategoryClient;

use Paysera\Test\CategoryClient\Entity as Entities;
use Fig\Http\Message\RequestMethodInterface;
use Paysera\Component\RestClientCommon\Client\ApiClient;

class CategoryClient
{
    private $apiClient;

    public function __construct(ApiClient $apiClient)
    {
        $this->apiClient = $apiClient;
    }

    public function withOptions(array $options)
    {
        return new CategoryClient($this->apiClient->withOptions($options));
    }

    /**
     * Enable category
     * PUT /categories/{id}/enable
     *
     * @param string $id
     * @return Entities\Category
     */
    public function enableCategory($id)
    {
        $request = $this->apiClient->createRequest(
            RequestMethodInterface::METHOD_PUT,
            sprintf('categories/%s/enable', urlencode($id)),
            null
        );
        $data = $this->apiClient->makeRequest($request);

        return new Entities\Category($data);
    }

    /**
     * Disable category
     * PUT /categories/{id}/disable
     *
     * @param string $id
     * @return Entities\Category
     */
    public function disableCategory($id)
    {
        $request = $this->apiClient->createRequest(
            RequestMethodInterface::METHOD_PUT,
            sprintf('categories/%s/disable', urlencode($id)),
            null
        );
        $data = $this->apiClient->makeRequest($request);

        return new Entities\Category($data);
    }

    /**
     * Update category
     * PUT /categories/{id}
     *
     * @param string $id
     * @param Entities\Category $category
     * @return Entities\Category
     */
    public function updateCategory($id, Entities\Category $category)
    {
        $request = $this->apiClient->createRequest(
            RequestMethodInterface::METHOD_PUT,
            sprintf('categories/%s', urlencode($id)),
            $category
        );
        $data = $this->apiClient->makeRequest($request);

        return new Entities\Category($data);
    }

    /**
     * Delete category
     * DELETE /categories/{id}
     *
     * @param string $id
     * @return null
     */
    public function deleteCategory($id)
    {
        $request = $this->apiClient->createRequest(
            RequestMethodInterface::METHOD_DELETE,
            sprintf('categories/%s', urlencode($id)),
            null
        );
        $data = $this->apiClient->makeRequest($request);

        return null;
    }

    /**
     * Standard SQL-style Result filtering
     * GET /categories
     *
     * @param Entities\CategoryFilter $categoryFilter
     * @return null
     */
    public function getCategories(Entities\CategoryFilter $categoryFilter)
    {
        $request = $this->apiClient->createRequest(
            RequestMethodInterface::METHOD_GET,
            'categories',
            $categoryFilter
        );
        $data = $this->apiClient->makeRequest($request);

        return null;
    }

    /**
     * Create category
     * POST /categories
     *
     * @param Entities\Category $category
     * @return Entities\CategoryResult
     */
    public function createCategory(Entities\Category $category)
    {
        $request = $this->apiClient->createRequest(
            RequestMethodInterface::METHOD_POST,
            'categories',
            $category
        );
        $data = $this->apiClient->makeRequest($request);

        return new Entities\CategoryResult($data, 'items');
    }
}

src/ClientFactory.php

<?php

namespace Paysera\Test\CategoryClient;

use Paysera\Component\RestClientCommon\Util\ClientFactoryAbstract;
use Paysera\Component\RestClientCommon\Client\ApiClient;

class ClientFactory extends ClientFactoryAbstract
{
    const DEFAULT_BASE_URL = 'https://example.com/category/rest/v1/';

    private $apiClient;

    public function __construct($options)
    {
        if ($options instanceof ApiClient) {
            $this->apiClient = $options;
            return;
        }

        $defaultUrlParameters = [];
        
        $options['url_parameters'] = $this->resolveDefaultUrlParameters($defaultUrlParameters, $options);
        $this->apiClient = $this->createApiClient($options);
    }

    public function getAClient()
    {
        return new CategoryClient($this->apiClient);
    }

    private function resolveDefaultUrlParameters(array $defaults, array $options)
    {
        $params = [];
        if (isset($options['url_parameters'])) {
            $params = $options['url_parameters'];
        }

        return $params + $defaults;
    }
}

src/Entity/Category.php

<?php

namespace Paysera\Test\CategoryClient\Entity;

use Paysera\Component\RestClientCommon\Entity\Entity;

class Category extends Entity
{
    const STATUS_ACTIVE = 'active';
    const STATUS_INACTIVE = 'inactive';

    public function __construct(array $data = [])
    {
        parent::__construct($data);
    }

    /**
     * @return string|null
     */
    public function getId()
    {
        return $this->get('id');
    }
    /**
     * @param string $id
     * @return $this
     */
    public function setId($id)
    {
        $this->set('id', $id);
        return $this;
    }
    /**
     * @return string|null
     */
    public function getParentId()
    {
        return $this->get('parent_id');
    }
    /**
     * @param string $parentId
     * @return $this
     */
    public function setParentId($parentId)
    {
        $this->set('parent_id', $parentId);
        return $this;
    }
    /**
     * @return string
     */
    public function getName()
    {
        return $this->get('name');
    }
    /**
     * @param string $name
     * @return $this
     */
    public function setName($name)
    {
        $this->set('name', $name);
        return $this;
    }
    /**
     * @return string|null
     */
    public function getStatus()
    {
        return $this->get('status');
    }
    /**
     * @param string $status
     * @return $this
     */
    public function setStatus($status)
    {
        $this->set('status', $status);
        return $this;
    }
}

src/Entity/CategoryFilter.php

<?php

namespace Paysera\Test\CategoryClient\Entity;

use Paysera\Component\RestClientCommon\Entity\Filter;

class CategoryFilter extends Filter
{
    /**
     * @return string|null
     */
    public function getParentId()
    {
        return $this->get('parent_id');
    }
    /**
     * @param string $parentId
     * @return $this
     */
    public function setParentId($parentId)
    {
        $this->set('parent_id', $parentId);
        return $this;
    }
}

src/Entity/CategoryResult.php

<?php

namespace Paysera\Test\CategoryClient\Entity;

use Paysera\Component\RestClientCommon\Entity\Result;

class CategoryResult extends Result
{
    protected function createItem(array $data)
    {
        return new Category($data);
    }
}

生成 Symfony Bundle

  1. 运行 bin/console php-generator:symfony-bundle {path_to_raml} {output_dir} {namespace}
    • path_to_ramlraml 文件的路径。
    • output_dir 是放置生成文件的目录。
    • namespace 是您的 Symfony Bundle 的命名空间。
  2. 检查 {output_dir} 目录中的输出。

output_dir 中,您应该期望生成以下文件

|   CategoryPermissions.php
|   VendorCategoryApiBundle.php
|
└───Controller
    |   CategoryApiController.php
    |
    DependencyInjection
    |   Configuration.php
    |   VendorCategoryApiExtension.php
    |
    Entity
    |   Category.php
    |   CategoryFilter.php
    |
    Normalizer
    |   CategoryNormalizer.php
    |   CategoryFilterNormalizer.php
    |
    Repository
    |   CategoryRepository.php
    |
    Service
    |   CategoryManager.php
    |
    Voter
    |   CategoryScopeVoter.php
    |
    Resources
    |
    ├───config
    |       routing.xml
    |       services.xml
    |       
    ├───────services
    |           api.xml
    |           controllers.xml
    |           normalizers.xml
    |           repositories.xml
    |           services.xml
    |
    ├───────routing
    |           rest_v1.xml
    |
    ├───────doctrine
    |           Category.orm.xml

以下内容

CategoryPermissions.php

<?php

namespace Vendor\Test\CategoryApiBundle;

final class CategoryPermissions
{
    const GET_CATEGORIES = 'get_categories';
    const CREATE_CATEGORY = 'create_category';
    const UPDATE_CATEGORY = 'update_category';
    const DELETE_CATEGORY = 'delete_category';
    const ENABLE_CATEGORY = 'enable_category';
    const DISABLE_CATEGORY = 'disable_category';
    
}

VendorCategoryApiBundle.php

<?php

namespace Vendor\Test\CategoryApiBundle;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class VendorCategoryApiBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
    }
}

Controller/CategoryApiController.php

<?php

namespace Vendor\Test\CategoryApiBundle\Controller;

use Vendor\Test\CategoryApiBundle\Entity as Entities;
use Paysera\Component\Serializer\Entity\Result;
use Vendor\Test\CategoryApiBundle\Service\CategoryManager;
use Vendor\Test\CategoryApiBundle\CategoryPermissions;
use Paysera\Bundle\SecurityBundle\Service\AuthorizationChecker;
use Doctrine\ORM\EntityManager;

class CategoryApiController
{
    private $authorizationChecker;
    private $entityManager;
    private $categoryManager;
    
    public function __construct(
        CategoryManager $categoryManager,
        AuthorizationChecker $authorizationChecker,
        EntityManager $entityManager
    ) {
        $this->categoryManager = $categoryManager;
        $this->authorizationChecker = $authorizationChecker;
        $this->entityManager = $entityManager;
    }

    /**
     * Enable category
     * PUT /categories/{id}/enable
     *
     * @param Entities\Category $category
     * @return Entities\Category
     */
    public function enableCategory(Entities\Category $category)
    {
        $this->authorizationChecker->check(CategoryPermissions::ENABLE_CATEGORY);
        $result = $this->categoryManager->enableCategory($category);
        $this->entityManager->flush();
        return $result;
    }
    /**
     * Disable category
     * PUT /categories/{id}/disable
     *
     * @param Entities\Category $category
     * @return Entities\Category
     */
    public function disableCategory(Entities\Category $category)
    {
        $this->authorizationChecker->check(CategoryPermissions::DISABLE_CATEGORY);
        $result = $this->categoryManager->disableCategory($category);
        $this->entityManager->flush();
        return $result;
    }
    /**
     * Update category
     * PUT /categories/{id}
     *
     * @param Entities\Category $originalCategory
     * @param Entities\Category $updatedCategory
     * @return Entities\Category
     */
    public function updateCategory(Entities\Category $originalCategory, Entities\Category $updatedCategory)
    {
        $this->authorizationChecker->check(CategoryPermissions::UPDATE_CATEGORY);
        $result = $this->categoryManager->updateCategory($originalCategory, $updatedCategory);
        $this->entityManager->flush();
        return $result;
    }
    /**
     * Delete category
     * DELETE /categories/{id}
     *
     * @param Entities\Category $category
     * @return null
     */
    public function deleteCategory(Entities\Category $category)
    {
        $this->authorizationChecker->check(CategoryPermissions::DELETE_CATEGORY);
        $this->categoryManager->deleteCategory($category);
        $this->entityManager->flush();
        return null;
    }
    /**
     * Standard SQL-style Result filtering
     * GET /categories
     *
     * @param Entities\CategoryFilter $categoryFilter
     * @return null
     */
    public function getCategories(Entities\CategoryFilter $categoryFilter)
    {
        $this->authorizationChecker->check(CategoryPermissions::GET_CATEGORIES);
        return $this->categoryManager->getCategories($categoryFilter);
    }
    /**
     * Create category
     * POST /categories
     *
     * @param Entities\Category $category
     * @return Result|Entities\Category[]
     */
    public function createCategory(Entities\Category $category)
    {
        $this->authorizationChecker->check(CategoryPermissions::CREATE_CATEGORY);
        $result = $this->categoryManager->createCategory($category);
        $this->entityManager->flush();
        return $result;
    }
}

DependencyInjection/Configuration.php

<?php

namespace Vendor\Test\CategoryApiBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

class Configuration implements ConfigurationInterface
{
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('vendor_category_api');

        return $treeBuilder;
    }
}

DependencyInjection/VendorCategoryApiExtension.php

<?php

namespace Vendor\Test\CategoryApiBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;

class VendorCategoryApiExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.xml');
    }
}

Entity/Category.php

<?php

namespace Vendor\Test\CategoryApiBundle\Entity;

class Category
{
    const STATUS_ACTIVE = 'active';
    const STATUS_INACTIVE = 'inactive';

    private $id;
    private $parentId;
    private $name;
    private $status;

    public function __construct()
    {
                    
    }

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }
    /**
     * @return string|null
     */
    public function getParentId()
    {
        return $this->parentId;
    }
    /**
     * @param string $parentId
     * @return $this
     */
    public function setParentId($parentId)
    {
        $this->parentId = $parentId;
        return $this;
    }
    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }
    /**
     * @param string $name
     * @return $this
     */
    public function setName($name)
    {
        $this->name = $name;
        return $this;
    }
    /**
     * @return string|null
     */
    public function getStatus()
    {
        return $this->status;
    }
    /**
     * @param string $status
     * @return $this
     */
    public function setStatus($status)
    {
        $this->status = $status;
        return $this;
    }

}

Entity/CategoryFilter.php

<?php

namespace Vendor\Test\CategoryApiBundle\Entity;

use Paysera\Component\Serializer\Entity\Filter;

class CategoryFilter extends Filter
{
    private $parentId;

    /**
     * @return string|null
     */
    public function getParentId()
    {
        return $this->parentId;
    }
    /**
     * @param string $parentId
     * @return $this
     */
    public function setParentId($parentId)
    {
        $this->parentId = $parentId;
        return $this;
    }
}

Normalizer/CategoryNormalizer.php

<?php

namespace Vendor\Test\CategoryApiBundle\Normalizer;

use Paysera\Component\Serializer\Normalizer\DenormalizerInterface;
use Paysera\Component\Serializer\Normalizer\NormalizerInterface;
use Vendor\Test\CategoryApiBundle\Entity\Category;

class CategoryNormalizer implements NormalizerInterface, DenormalizerInterface
{
    
    /**
     * @param array $data
     *
     * @return Category
     */
    public function mapToEntity($data)
    {
        $entity = new Category();

        if (isset($data['parent_id'])) {
            $entity->setParentId($data['parent_id']);
        }
        if (isset($data['name'])) {
            $entity->setName($data['name']);
        }
        if (isset($data['status'])) {
            $entity->setStatus($data['status']);
        }
        
        return $entity;
    }

    /**
     * @param Category $entity
     *
     * @return array
     */
    public function mapFromEntity($entity)
    {
        return [
            'id' => $entity->getId(),
            'parent_id' => $entity->getParentId(),
            'name' => $entity->getName(),
            'status' => $entity->getStatus(),
            
        ];
    }
}

Normalizer/CategoryFilterNormalizer.php

<?php

namespace Vendor\Test\CategoryApiBundle\Normalizer;

use Paysera\Component\Serializer\Normalizer\FilterNormalizer;
use Vendor\Test\CategoryApiBundle\Entity\CategoryFilter;

class CategoryFilterNormalizer extends FilterNormalizer
{
    /**
     * @param array $data
     *
     * @return CategoryFilter
     */
    public function mapToEntity($data)
    {
        $entity = new CategoryFilter();
        $this->mapBaseKeys($data, $entity);

        if (isset($data['parent_id'])) {
            $entity->setParentId($data['parent_id']);
        }
        
        return $entity;
    }

    /**
     * @param CategoryFilter $entity
     *
     * @return array
     */
    public function mapFromEntity($entity)
    {
        $data = parent::mapFromEntity($entity);
        return array_merge(
            $data,
            [
                'parent_id' => $entity->getParentId(),
                
            ]
        );
        
    }
}

Repository/CategoryRepository.php

<?php

namespace Vendor\Test\CategoryApiBundle\Repository;

use Doctrine\ORM\EntityRepository;

class CategoryRepository extends EntityRepository
{
}

Service/CategoryManager.php

<?php

namespace Vendor\Test\CategoryApiBundle\Service;

use Paysera\Component\Serializer\Entity\Result;
use Vendor\Test\CategoryApiBundle\Entity as Entities;
use Vendor\Test\CategoryApiBundle\Repository\CategoryRepository;
use Doctrine\ORM\EntityManager;

class CategoryManager
{
    private $categoryRepository;
    private $entityManager;

    public function __construct(
        CategoryRepository $categoryRepository,
        EntityManager $entityManager
    ) {
        $this->categoryRepository = $categoryRepository;
        $this->entityManager = $entityManager;
    }

    /**
     * @param Entities\Category $category
     * @return Entities\Category
     */
    public function enableCategory(Entities\Category $category)
    {
        //TODO: generated_code
    }
    /**
     * @param Entities\Category $category
     * @return Entities\Category
     */
    public function disableCategory(Entities\Category $category)
    {
        //TODO: generated_code
    }
    /**
     * @param Entities\Category $category
     * @param Entities\Category $category
     * @return Entities\Category
     */
    public function updateCategory(Entities\Category $originalCategory, Entities\Category $updatedCategory)
    {
        //TODO: generated_code
    }
    /**
     * @param Entities\Category $category
     * @return null
     */
    public function deleteCategory(Entities\Category $category)
    {
        //TODO: generated_code
    }
    /**
     * @param Entities\CategoryFilter $categoryFilter
     * @return null
     */
    public function getCategories(Entities\CategoryFilter $categoryFilter)
    {
        //TODO: generated_code
    }
    /**
     * @param Entities\Category $category
     * @return Result|Entities\Category[]
     */
    public function createCategory(Entities\Category $category)
    {
        //TODO: generated_code
    }
}

Voter/CategoryScopeVoter.php

<?php

namespace Vendor\Test\CategoryApiBundle\Voter;

use Vendor\Test\CategoryApiBundle\CategoryPermissions;
use Paysera\Bundle\SecurityBundle\Security\ContextAwareScopeVoter;
use Paysera\Bundle\SecurityBundle\Entity\AccessedBy;

class CategoryScopeVoter extends ContextAwareScopeVoter
{
    public function getPermissionScopeMap()
    {
        return [
            CategoryPermissions::GET_CATEGORIES => [
                // TODO: generated_code
            ],
            CategoryPermissions::CREATE_CATEGORY => [
                // TODO: generated_code
            ],
            CategoryPermissions::UPDATE_CATEGORY => [
                // TODO: generated_code
            ],
            CategoryPermissions::DELETE_CATEGORY => [
                // TODO: generated_code
            ],
            CategoryPermissions::ENABLE_CATEGORY => [
                // TODO: generated_code
            ],
            CategoryPermissions::DISABLE_CATEGORY => [
                // TODO: generated_code
            ],
        ];
    }

    public function checkAccessRights(AccessedBy $accessedBy, $permission, $subject)
    {
        // TODO: generated_code
    }
}