Использование RESTful API, используя PHP и Yii2

С момента своего появления архитектура RESTful переопределила то, как мы думаем (и создаем) программные приложения, разбивая сложные экосистемы приложений на более мелкие, более целенаправленные приложения, взаимодействующие друг с другом посредством вызовов RESTful.

Архитектура клиент-сервер позволяет веб-клиентам и мобильным приложениям взаимодействовать с одной и той же инфраструктурой (например, с помощью API на стороне сервера), чтобы обеспечить бесперебойную работу пользователей.

В этом руководстве я покажу вам, как создать RESTful API с помощью Yii framework (версия 2) — высокопроизводительного PHP-фреймворка на основе компонентов.

Предпосылки

В этом руководстве вам поможет базовое понимание фреймворка Yii и PHP. Тем не менее, я буду предоставлять краткие пояснения и ссылки на соответствующие части официальной документации на протяжении всего руководства. Если вам неясна какая-либо концепция, вы можете просмотреть связанный материал, прежде чем продолжить.

Кроме того, в вашей системе должно быть установлено следующее:

  • PHP версии 7 или выше с включенным расширением PDO .
  • Композитор установлен глобально
  • Локальный сервер базы данных. Хотя в этом руководстве будет использоваться MySQL, вы можете выбрать предпочитаемого поставщика базы данных.
  • Postman или аналогичное приложение для проверки конечных точек. Вы также можете использовать cURL для проверки конечных точек.

Что мы построим

В этом руководстве мы создадим API для библиотечного приложения. Это приложение будет обрабатывать два основных ресурса: Members и Books . Приложение позволит выполнять базовые операции CRUD для обоих ресурсов. Кроме того, участники смогут брать книги. Если книга была взята напрокат, другой пользователь не сможет взять ту же книгу.

Начиная

Создание приложения

Для начала создайте новое приложение с именем library-apiи переключитесь в каталог проекта, используя следующие команды.

composer create-project --prefer-dist yiisoft/yii2-app-basic library-api
cd library-api

Убедитесь, что приложение было успешно создано с помощью следующей команды.

php yii serve

По умолчанию приложение будет обслуживаться по адресу http://localhost:8080/ . Перейдите по URL-адресу и убедитесь, что он показывает страницу приветствия, которую вы можете увидеть ниже.

Домашняя страница Yii2 по умолчанию

Вернитесь к терминалу и нажмите Control + C, чтобы выйти из сервера.

Настройка базы данных

Используя предпочитаемое вами приложение для управления базами данных, создайте новую базу данных с именем library-api. Затем обновите параметры подключения к базе данных, чтобы связать ваше приложение с вновь созданной базой данных. Для этого откройте config/db.phpи обновите возвращенный массив, чтобы он соответствовал показанному ниже, аккуратно заменив заполнители именем пользователя и паролем для вашей базы данных.

return [
    'class' => 'yii\\db\\Connection',
    'dsn' => 'mysql:host=localhost;dbname=library_api',
    'username' => '<USERNAME>'
    'password' => '<PASSWORD>',
    'charset' => 'utf8',
];

Создание миграций

Затем создайте миграции для таблиц базы данных. Чтобы поместить миграции в контекст, приложение будет иметь три объекта, а именно:

  1. Участник: этот объект будет представлять члена библиотеки. Для этой статьи таблица участников будет содержать имена участников вместе с датой начала членства.
  2. Книга: этот объект будет представлять книгу, доступную в библиотеке. Для этой статьи таблица book будет содержать название книги, автора, год выпуска и информацию о том, доступна ли книга во временное пользование или нет.
  3. Ссуда: эта сущность будет управлять данными, связанными с процессом «заимствования» книги. Для этой статьи он будет содержать одолженную книгу, члена, который ее одолжил, дату, когда она была одолжена, и ожидаемую дату возврата.

Чтобы создать миграцию, используйте команду yii, указав migrate/createимя создаваемой миграции. При запросе подтверждения введите yesдля создания миграции. В этой статье используйте приведенные ниже команды для создания необходимых миграций.

php yii migrate/create create_member_table
php yii migrate/create create_book_table
php yii migrate/create create_loan_table

По умолчанию файлы миграции находятся в каталоге миграции . Имена файлов миграции начинаются с буквы « m » и даты и времени в формате UTC , когда они были созданы. Например , m210408_150000_create_member_table.php .

Откройте migrations/m<ГГММДД_ЧЧММСС>_create_member_table.php и измените safeUpфункцию, чтобы она соответствовала следующему коду.

public function safeUp()
    {
        $this->createTable('member', [
            'id' => $this->primaryKey(),
            'name' => $this->string()->notNull(),
            'started_on' => $this->dateTime()->defaultValue(date('Y-m-d H:i:s'))
        ]);
    }

Отредактируйте safeUpфункцию migrations/m<ГГММДД_ЧЧММСС>_create_book_table.php , чтобы она соответствовала следующему коду.

public function safeUp()
    {
        $this->createTable('book', [
            'id' => $this->primaryKey(),
            'name' => $this->string(),
            'author' => $this->string(),
            'release_year' => $this->smallInteger(),
            'is_available_for_loan' => $this->boolean()->defaultValue(true)
        ]);
    }

Отредактируйте функции safeUpи safeDownдля migrations/m<ГГММДД_ЧЧММСС>_create_loan_table.php , чтобы они соответствовали следующему коду.

    public function safeUp()
    {
        $this->createTable('loan', [
            'id' => $this->primaryKey(),
            'book_id' => $this->integer(),
            'borrower_id' => $this->integer(),
            'borrowed_on' => $this->dateTime()
                                                                                                                                        ->defaultValue(date('Y-m-d H:i:s')),
            'to_be_returned_on' => $this->dateTime()
        ]);

        // create index for column `book_id`
        $this->createIndex(
            'idx-loan-book_id',
            'loan',
            'book_id'
        );

        // add foreign key for table `post`
        $this->addForeignKey(
            'fk-loan-book_id',
            'loan',
            'book_id',
            'book',
            'id',
            'CASCADE'
        );

        // create index for column `borrower_id`
        $this->createIndex(
            'idx-loan-borrower_id',
            'loan',
            'borrower_id'
        );

        // add foreign key for table `post`
        $this->addForeignKey(
            'fk-loan-borrower_id',
            'loan',
            'borrower_id',
            'member',
            'id',
            'CASCADE'
        );
    }

    public function safeDown()
    {
        $this->dropForeignKey('fk-loan-borrower_id','loan');
        $this->dropIndex('idx-loan-borrower_id', 'loan');
        $this->dropForeignKey('fk-loan-book_id', 'loan');
        $this->dropIndex('idx-loan-book_id', 'loan');
        $this->dropTable('loan');
    }

Заполнение базы данных

Чтобы некоторые данные были доступны при создании таблиц, мы можем создать миграции для вставки поддельных данных в базу данных. Создайте две новые миграции с помощью следующей команды:

php yii migrate/create seed_member_table
php yii migrate/create seed_book_table

Откройте migrations/m<ГГММДД_ЧЧММСС>_seed_member_table.php и измените safeUpфункцию и добавьте insertFakeMembersфункцию после нее, как в приведенном ниже коде.

public function safeUp() {
    $this->insertFakeMembers();
}

private function insertFakeMembers() 
{
        $faker = \Faker\Factory::create();

        for ($i = 0; $i < 10; $i++) {
            $this->insert(
                'member',
                [
                    'name' => $faker->name
                ]
            );
        }
    }

Затем откройте migrations/m<ГГММДД_ЧЧММСС>_seed_book_table.php и измените safeUpфункцию и добавьте insertFakeBooksфункцию после нее, как в приведенном ниже коде.

public function safeUp() {

        $this->insertFakeBooks();
    }

private function insertFakeBooks() 
{
        $faker = \Faker\Factory::create();
        for ($i = 0; $i < 50; $i++) {
            $this->insert(
                'book',
                [
                    'name'         => $faker->catchPhrase,
                    'author'       => $faker->name,
                    'release_year' => (int)$faker->year,
                ]
            );
        }
    }

После внесения изменений запустите миграцию для создания таблиц с помощью следующей команды:

php yii migrate

При появлении запроса введите «да» и нажмите «Ввод», чтобы запустить миграцию. Откройте library-apiбазу данных в предпочитаемом вами приложении, чтобы увидеть новую структуру таблицы.

Структура таблиц базы данных

Таблица книг с образцами книг показана ниже.

Листинг книжной таблицы

Таблица членов с образцами элементов показана ниже.

Листинг таблицы участников

Создание моделей

Вместо написания необработанных SQL-запросов для взаимодействия с базой данных мы создадим классы Active Record для наших моделей. Это даст нам объектно-ориентированные средства доступа и хранения данных в базе данных. Создайте классы Active Record для сущностей Member , Book и Loan , используя приведенные ниже команды. При появлении запроса введите yesи нажмите Enter, чтобы продолжить.

php yii gii/model --tableName=member --modelClass=Member
php yii gii/model --tableName=book --modelClass=Book
php yii gii/model --tableName=loan --modelClass=Loan

Классы моделей сохраняются в каталоге моделей . Интересно, что они помогают нам генерировать функции для обработки сложных взаимосвязей в структуре нашей базы данных. Например, в models/Member.php для нас была создана getLoansфункция, которая извлекает все кредиты, связанные с данным участником. То же самое и с файлами models/Book.php .

Создайте контроллеры

Имея базу данных и модели, мы можем создавать контроллеры для обработки вызовов RESTful. Контроллеры Yii основаны на yii\rest\ActiveController , который обеспечивает общие действия RESTful. Это позволяет нам создавать конечные точки для обработки действий CRUD без необходимости самостоятельно писать шаблонный код.

Для начала создайте контроллеры для сущностей Member и Book, используя приведенные ниже команды. Введите «yes» и нажмите «Enter» при появлении запроса.

php yii gii/controller --controllerClass=app\\controllers\\MemberController --baseClass=yii\\rest\\ActiveController

php yii gii/controller --controllerClass=app\\controllers\\BookController --baseClass=yii\\rest\\ActiveController

Аргумент controllerClassуказывает имя создаваемого контроллера. Ожидается, что вы предоставите полностью квалифицированный класс с пространством имен.

Примечание . Двойные символы возврата ( \\), используемые при указании пространства имен, необходимы для исключения одиночного возврата в полном имени класса.

Классы контроллера хранятся в каталоге контроллера . Откройте controllers/BookController.php и отредактируйте содержимое, чтобы оно соответствовало следующему.

<?php

namespace app\controllers;

class BookController extends \yii\rest\ActiveController {

    public $modelClass = 'app\models\Book';

}

Таким же образом откройте controllers/MemberController.php и отредактируйте содержимое, чтобы оно соответствовало следующему.

<?php

namespace app\\controllers;

class MemberController extends \yii\rest\ActiveController
{
    public $modelClass = 'app\models\Member';

}

Затем измените urlManagerкомпонент в конфигурации вашего приложения, которую вы найдете в config/web.php .

В config/web.php, массив, хранящийся в $config, возвращается. Этот массив содержит componentsэлемент. В этом элементе замените закомментированную версию urlManagerэлемента следующим:

'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'enableStrictParsing' => true,
            'rules' => [
                ['class' => 'yii\rest\UrlRule', 'controller' => 'member'],
                ['class' => 'yii\rest\UrlRule', 'controller' => 'book'],
            ],
        ],

Это изменение добавляет правило URL-адреса для контроллеров Member и Book, чтобы можно было получить доступ к связанным данным и управлять ими с помощью красивых URL-адресов и значимых HTTP-команд.

Этот componentsэлемент также содержит requestэлемент, который содержит конфигурацию для Application Component запроса . Чтобы API мог анализировать ввод JSON, добавьте к requestэлементу следующее.

'parsers' => [
        'application/json' => 'yii\web\JsonParser',
    ],

На данном этапе у нас есть приложение, которое может обрабатывать следующие запросы:

МетодМаршрутОписание
GET/{members, books}Список всех участников или книг страница за страницей;
POST/{members, books}Создать нового участника или книгу;
GET/{members, books}/123Вернуть реквизиты члена или книги 123;
PATCH and PUT/{members, books}/123Обновить участника или книгу 123;
DELETE/{members, books}/123Удалить участника или книгу 123;

Обслуживайте приложение, запустив php yii serve и протестировав только что созданные конечные точки.

Например, чтобы получить список книг из базы данных, отправьте запрос GET на адрес http://localhost:8080/books. Для этого запустите Postman, если он еще не открыт.

Если вы впервые используете Postman, вам нужно ввести конечную точку API, где написано « Введите URL-адрес запроса », и выбрать метод (тип действия) слева от этого поля. Метод по умолчанию — GET, и он подходит для нашего варианта использования. Затем нажмите кнопку « Отправить », расположенную справа от поля URL-адреса запроса. Этот процесс изображен на изображении ниже:

Тестирование конечной точки публикации книг в Postman

В случае успеха вы увидите ответ JSON, содержащий списки книг из базы данных и код состояния 200 Ok .

Затем создайте нового участника, отправив запрос POST http://localhost:8080/membersи введя соответствующие данные, как показано на снимке экрана ниже.

Создание нового участника с использованием конечной точки участников в Postman

Обратите внимание, что ваши данные могут отличаться от этих, но вот как должны выглядеть необработанные запросы JSON:

{
    "name": "olususi oluyemi",
    "started_on": "2020-03-24 12:30:10"
}

Если запрос выполнен успешно, вы получите ответ JSON с кодом состояния 201 Created .

Последний контроллер, который нам нужен, — это контроллер LoanControllerдля управления запросами, связанными с кредитом. Что касается ссуд, API должен разрешать только запросы на создание новой ссуды, т. е. заимствование книги или просмотр всех ссуд в системе. Создайте с LoanControllerпомощью следующей команды. Введите yes и нажмите Enter при появлении запроса.

php yii gii/controller --controllerClass=app\\controllers\\LoanController

Примечание. Поскольку нам не нужны все действия RESTful, доступные в классе ActiveController, мы не указывали базовый класс. Сгенерированный контроллер по умолчанию будет расширять класс yii/web/Controller .

Прежде чем мы создадим действия для обработки запросов, нам нужно добавить новые правила маршрутизации для новых конечных точек API. В config/web.php добавьте следующее в конец rulesмассива элементов в файле urlManagerelement.

'GET loans' => 'loan/index',
'POST loans' => 'loan/borrow'

По завершении urlManagerкод должен выглядеть так:

'urlManager' => [
    'enablePrettyUrl' => true,
    'showScriptName' => false,
    'enableStrictParsing' => true,
    'rules' => [
        ['class' => 'yii\rest\UrlRule', 'controller' => 'member'],
        ['class' => 'yii\rest\UrlRule', 'controller' => 'book'],
        'GET loans' => 'loan/index',
        'POST loans' => 'loan/borrow'
    ],
],

Используя сокращенную нотацию, предоставленную Yii, мы можем указать маршрут для данного URL. URL предоставляется в качестве ключа с маршрутом к соответствующему действию в качестве значения. Добавляя к URL-адресу префикс HTTP-глагола, приложение может соответствующим образом обрабатывать URL-адреса с одним и тем же шаблоном (для разных действий). Подробнее об этом можно прочитать здесь .

Чтобы API мог обрабатывать запросы POST, проверка CSRF будет отключена в файле LoanController. Для этого в controllers/LoanController.php добавьте следующую переменную-член.

public $enableCsrfValidation = false;

Примечание. Отключение CSRF без дополнительных проверок может подвергнуть ваше приложение угрозам безопасности. Подробнее об этом читайте здесь .

Затем обновите метод LoanControlleractionIndexчтобы он обрабатывал запрос на получение всех кредитов, заменив существующий код метода кодом, приведенным ниже.

public function actionIndex() 
{
        $loans = Loan::find()->all();
        return $this->asJson($loans);
}

ПРИМЕЧАНИЕ . Не забудьте импортировать кредитную модель.

use app\models\Loan;

Перед созданием действия для обработки создания нового кредита добавьте вспомогательную функцию для возврата ответа об ошибке из API, добавив приведенный ниже код в конец файла LoanController.

private function errorResponse($message) {
                                
    // set response code to 400
    Yii::$app->response->statusCode = 400;

    return $this->asJson(['error' => $message]);
}

ПРИМЕЧАНИЕ . Не забудьте импортировать Yii.

use Yii;

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

  1. Убедитесь, что book_idуказанная книга соответствует существующей книге в базе данных.
  2. Убедитесь, что выбранную книгу можно взять напрокат, т . е. is_available_for_loanона установлена ​​на true.
  3. Убедитесь, что member_idпредоставленный элемент соответствует существующему члену в базе данных.

Если какое-либо из вышеперечисленных условий не выполняется, errorResponseследует вызвать функцию, вернув соответствующее ответное сообщение.

Перед созданием действия добавьте в модель Book функцию, позволяющую пометить книгу как заимствованную. Для этого в models/Book.php добавьте следующий код.

public function markAsBorrowed() 
{
        $this->is_available_for_loan = (int)false;
        $this->save();
    }

Затем создайте действие для обработки запроса на одолжение книги, добавив следующий код в файл controllers/LoanController.php.

public function actionBorrow() 
{
        $request = Yii::$app->request;

        $bookId = $request->post('book_id');
        $book = Book::findOne($bookId);

        if (is_null($book)) {
            return $this->errorResponse('Could not find book with provided ID');
        }

        if (!$book->is_available_for_loan) {
            return $this->errorResponse('This book is not available for loan');
        }

        $borrowerId = $request->post('member_id');

        if (is_null(Member::findOne($borrowerId))) {
            return $this->errorResponse('Could not find member with provided ID');
        }

        $loan = new Loan();
        $returnDate = strtotime('+ 1 month');
        $loan->attributes =
            [
                'book_id'           => $bookId,
                'borrower_id'       => $borrowerId,
                'borrowed_on'       => date('Y-m-d H:i:s'),
                'to_be_returned_on' => date('Y-m-d H:i:s', $returnDate)
            ];

        $book->markAsBorrowed();
        $loan->save();

        return $this->asJson(
            $loan
        );
    }

ПРИМЕЧАНИЕ . Не забудьте импортировать модели Book и Member.

use app\models\Member;
use app\models\Book;

Тестируйте новые маршруты

Вы можете протестировать новые маршруты, чтобы увидеть их в действии. Чтобы взять книгу, вы отправляете HTTP-запрос POST на эту конечную точку http://localhost:8080/loans . Введите конечную точку API в поле URL и выберите POST в раскрывающемся поле действия. Затем выберите Body в следующей строке и используйте следующий код в качестве примера необработанного запроса JSON:

{
    "book_id": 9,
    "member_id" : 10
}

Предоставление книги во временное пользование с помощью конечной точки кредита в Postman

Если запрос выполнен успешно, вы получите ответ JSON, аналогичный приведенному ниже, вместе с кодом состояния HTTP 200 OK:

{
    "book_id": 9,
    "member_id" : 10,
    "borrowed_on": "2021-03-25 21:39:32",
    "to_be_returned_on": "2021-04-25 21:39:32",
    "id": 6
}

Вывод

В этой статье мы создали RESTful API для небольшого библиотечного приложения с фреймворком Yii2. В ходе этого процесса мы увидели некоторые преимущества разработки с использованием среды Yii2, такие как создание функций (моделей, контроллеров и т. д.) с минимальным кодом/шаблоном, его безопасные по умолчанию функции для защиты приложений от SQL-инъекций и CSRF. атаки, и это лишь некоторые из них.

Кроме того, лежащая в основе философии структура кода позволяет нам использовать лучшие практики и шаблоны ООП, в результате чего код становится легче поддерживать.