
Из этого урока Вы узнаете как можно быстро создать 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".
Создаю классы сущности и репозитория (Doctrine ORM) пользователя:
php bin/console make:user
Появились два файла:
src/Entity/User.php src/Repository/UserRepository.php
Создаю таблицу "user" в базе данных:
bin/console doctrine:schema:create
Настраиваю авторизацию пользователей в соответствии с инструкциями:
- https://symfony.com/doc/current/security/form_login_setup.html
- https://api-platform.com/docs/core/jwt/#configuring-the-symfony-securitybundle
<?php namespace App\Controller; use App\Entity\User; use App\Form\Type\LoginType; use App\Form\Type\UpdateProfileType; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; use Symfony\Component\Validator\Validator\ValidatorInterface; 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
<?php namespace App\DataFixtures; use App\Entity\User; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; 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 "App\Entity" annotation --path=src/Entity

Генерирую геттеры и сеттеры для классов:
php bin/console make:entity --regenerate App
Открываю код одного класса, например src/Entity/ModxSiteContent.php. Добавляю аннотацию @ApiResource:

API для ModxSiteContent готово.
Открываю страницу http://localhost:8000/api

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

Нажимаю кнопку "Try it out" и затем кнопку "Execute". Получаю такой результат:

Связи таблиц
Я не буду описывать как создаются фильтры, чтобы не дублировать документацию, но приведу пример как можно создать связи для таблиц, т.к. это немного сложнее.
В случае MODX данные пользователей хранятся в отдельной таблице "user_attributes". Например, мне нужно в выборку пользователей по GET запросу добавить их email, имя, телефон и т.д. Открываю код класса App\Entity\ModxUsers, добавляю приватное свойство $attributes и описываю связь с классом App\Entity\ModxUserAttributes в аннотоции "@ORM\OneToOne":
/** * @var ModxUserAttributes * * @ORM\OneToOne(targetEntity="ModxUserAttributes", mappedBy="user") * @Groups({"read", "attributes"}) */ private $attributes;
Снова добавляю недостающие геттеры и сеттеры:
php bin/console make:entity --regenerate App\\Entity\\ModxUsers
Обратите внимание, что я добавил группу "attributes" в аннотацию @ApiResource

Открываю код класса App\Entity\ModxUserAttributes, добавляю аннотацию @ApiResource и связь с классом App\Entity\ModxUsers:
/** * @var ModxUsers * * @ORM\OneToOne(targetEntity="ModxUsers", mappedBy="attributes") * @ORM\JoinColumn(name="internalKey", referencedColumnName="id") */ private $user;
Всем свойствам, которые нужно добавить в выборку, вставляю аннотацию @Groups({"attributes"}).

Результат:

В итоге для авторизации в вашем приложении вам нужно сначала отправить логин (email) и пароль на URL "/authentication_token", получить токен и потом этот токен отправлять в каждом запросе в заголовке "Authorization":

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