Как создать RESTful API на Symfony 5 + API Platform для проекта на MODX

в 20:44, , рубрики: api-platform, modx, modx revolution, symfony

Как создать RESTful API на Symfony 5 + API Platform для проекта на MODX - 1
Из этого урока Вы узнаете как можно быстро создать RESTful API для любого сайта на любой CMS, MODX — это только пример.

Для создания API я буду использовать:

Результат здесь:
https://github.com/andchir/modx2-api

Всё описанное я проделывал на Linux, но я думаю, что на Windows разница в командах будет не большая. Предварительно я установил Composer и утилиту Symfony.

Создание проекта и классов пользователей

Создаю проект:

composer create-project symfony/skeleton modx2-api

Устанавливаю необходимые пакеты для создания API:

cd modx2-api
composer req api
composer req migrations
composer req form
composer req maker --dev

Запускаю локальный сервер.
Если установлена утилита Symfony:

symfony server:start

или так:

php -S 127.0.0.1:8000 -t public

Открываю в браузере адрес для проверки, что всё работает:
http://127.0.0.1:8000/

Открываю файл .env и редактирую строку подключения к базе данных проекта на MODX:

DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name

Теперь нужно создать пользователя и настроить систему авторизации по логину и паролю.

Устанавливаю дополнительные пакеты:

composer req jwt-auth
composer req orm-fixtures --dev
composer req profiler --dev

В файле .env появились новые параметры пакета "jwt-authentication-bundle".

Создаю классы сущности и репозитория пользователя:

php bin/console make:user

Появились два файла:

src/Entity/User.php
src/Repository/UserRepository.php

Создаю таблицу "user" в базе данных:

bin/console doctrine:schema:create

Настраиваю авторизацию пользователей в соответствии с инструкциями:

Код контроллера AppControllerSecurityController

<?php

namespace AppController;

use AppEntityUser;
use AppFormTypeLoginType;
use AppFormTypeUpdateProfileType;
use DoctrineORMEntityManagerInterface;
use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentRoutingAnnotationRoute;
use SymfonyComponentSecurityCoreEncoderUserPasswordEncoderInterface;
use SymfonyComponentSecurityHttpAuthenticationAuthenticationUtils;
use SymfonyComponentValidatorValidatorValidatorInterface;

class SecurityController extends AbstractController
{
    /**
     * @Route("/login", name="app_login")
     * @param AuthenticationUtils $authenticationUtils
     * @return Response
     */
    public function loginAction(AuthenticationUtils $authenticationUtils): Response
    {
        if ($this->getUser()) {
            return $this->redirectToRoute('api_entrypoint');
        }
        $form = $this->createForm(LoginType::class);
        $error = $authenticationUtils->getLastAuthenticationError();
        $lastUsername = $authenticationUtils->getLastUsername();
        return $this->render('security/login.html.twig', [
            'form' => $form->createView(),
            'last_username' => $lastUsername,
            'error' => $error
        ]);
    }
}

Генерирую класс для создания пользователей:

php bin/console make:fixtures

Подробнее здесь: https://symfony.com/doc/current/security.html#a-create-your-user-class

Код класса AppDataFixturesUserFixtures

<?php

namespace AppDataFixtures;

use AppEntityUser;
use DoctrineBundleFixturesBundleFixture;
use DoctrineCommonPersistenceObjectManager;
use SymfonyComponentSecurityCoreEncoderUserPasswordEncoderInterface;

class UserFixtures extends Fixture
{
    private $passwordEncoder;

    public function __construct(UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->passwordEncoder = $passwordEncoder;
    }

    public function load(ObjectManager $manager)
    {
        $user = new User();
        $user
            ->setEmail('admin@admin.com')
            ->setRoles(['ROLE_USER', 'ROLE_ADMIN']);

        $user->setPassword($this->passwordEncoder->encodePassword(
            $user,
            'admin'// пароль
        ));

        $manager->persist($user);
        $manager->flush();
    }
}

Создаю администратора с адресом почты "admin@admin.com" и паролем "admin":

php bin/console doctrine:fixtures:load --append --group=UserFixtures

Позже эти данные можно будет изменить.

Генерирую ключи в папке config/jwt/:

jwt_passhrase=$(grep ''^JWT_PASSPHRASE='' .env | cut -f 2 -d ''='')
echo "$jwt_passhrase" | openssl genpkey -out config/jwt/private.pem -pass stdin -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096
echo "$jwt_passhrase" | openssl pkey -in config/jwt/private.pem -passin stdin -out config/jwt/public.pem -pubout
setfacl -R -m u:www-data:rX -m u:"$(whoami)":rwX config/jwt
setfacl -dR -m u:www-data:rX -m u:"$(whoami)":rwX config/jwt

Проверяю создание токена:

curl -X POST -H "Content-Type: application/json" http://localhost:8000/authentication_token -d '{"email":"admin@admin.com","password":"admin"}'

Создаю миграцию:

php bin/console make:migration

Теперь самое интересное.

Генерация API и документации

Генерирую классы сущностей для всех таблиц базы данных:

php bin/console doctrine:mapping:import "AppEntity" annotation --path=src/Entity

Как создать RESTful API на Symfony 5 + API Platform для проекта на MODX - 2

Генерирую геттеры и сеттеры для классов:

php bin/console make:entity --regenerate App

Открываю код одного класса, например src/Entity/ModxSiteContent.php. Добавляю аннотацию @ApiResource:
Как создать RESTful API на Symfony 5 + API Platform для проекта на MODX - 3

API для ModxSiteContent готово.

Открываю страницу http://localhost:8000/api

Как создать RESTful API на Symfony 5 + API Platform для проекта на MODX - 4

Беру токен, нажимаю кнопку "Authorize", вставляю строку с токеном:

Bearer MY_TOKEN

Как создать RESTful API на Symfony 5 + API Platform для проекта на MODX - 5

Нажимаю кнопку "Try it out" и затем кнопку "Execute". Получаю такой результат:
Как создать RESTful API на Symfony 5 + API Platform для проекта на MODX - 6

Связи таблиц

Я не буду описывать как создаются фильтры, чтобы не дублировать документацию, но приведу пример как можно создать связи для таблиц, т.к. это немного сложнее.

В случае MODX данные пользователей хранятся в отдельной таблице "user_attributes". Например, мне нужно в выборку пользователей по GET запросу добавить их email, имя, телефон и т.д. Открываю код класса AppEntityModxUsers, добавляю приватное свойство $attributes и описываю связь с классом AppEntityModxUserAttributes в аннотоции "@ORMOneToOne":

/**
 * @var ModxUserAttributes
 *
 * @ORMOneToOne(targetEntity="ModxUserAttributes", mappedBy="user")
 * @Groups({"read", "attributes"})
 */
private $attributes;

Снова добавляю недостающие геттеры и сеттеры:

php bin/console make:entity --regenerate App\Entity\ModxUsers

Обратите внимание, что я добавил группу "attributes" в аннотацию @ApiResource

Картинка

Как создать RESTful API на Symfony 5 + API Platform для проекта на MODX - 7

Открываю код класса AppEntityModxUserAttributes, добавляю аннотацию @ApiResource и связь с классом AppEntityModxUsers:

/**
 * @var ModxUsers
 *
 * @ORMOneToOne(targetEntity="ModxUsers", mappedBy="attributes")
 * @ORMJoinColumn(name="internalKey", referencedColumnName="id")
 */
private $user;

Всем свойствам, которые нужно добавить в выборку, вставляю аннотацию @Groups({"attributes"}).

Картинка

Как создать RESTful API на Symfony 5 + API Platform для проекта на MODX - 8

Результат:
Как создать RESTful API на Symfony 5 + API Platform для проекта на MODX - 9

В итоге для авторизации в вашем приложении вам нужно сначала отправить логин (email) и пароль на URL "/authentication_token", получить токен и потом этот токен отправлять в каждом запросе в заголовке "Authorization":
Как создать RESTful API на Symfony 5 + API Platform для проекта на MODX - 10

Если проект https://github.com/andchir/modx2-api будет интересен пользователям, он будет развиваться. Также жду PR от всех желающих помочь.

Автор: Андрей Ч.

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js