002 REST
Создание простого REST API.
В этом уроке мы объясним, как создать простое приложение, которое предоставляет RESTful , используя различные методы HTTP:
GET
извлечение и поиск данныхPOST
добавление данныхPUT
обновление данныхDELETE
удаление данных
Примечание: Это всего лишь пример приложения. Ему не хватает многих функций, таких как аутентификация, авторизация, дезинфекция ввода и управление ошибками, чтобы назвать несколько. Пожалуйста, используйте его как строительный блок для вашего приложения или как учебник, чтобы понять, как вы можете построить REST API с Phalcon. Вы также можете посмотреть на проект rest-api.
Определение API.
API состоит из следующих методов:
Method | URL | Action |
---|---|---|
GET |
/api/robots | Извлекает всех роботов |
GET |
/api/robots/search/Astro | Поиск роботов с именем "Astro" |
GET |
/api/robots/2 | Извлекает роботов на основе первичного ключа |
POST |
/api/robots | Добавление нового робота |
PUT |
/api/robots/2 | Обновление роботов на основе первичного ключа |
DELETE |
/api/robots/2 | Удаление роботов на основе первичного ключа |
Создание приложения.
Поскольку приложение настолько просто, мы не будем реализовывать полную среду MVC для его разработки. В этом случае мы будем использовать микро-приложение для достижения нашей цели.
Следующей структуры файлов более чем достаточно:
my-rest-api/ models/ Robots.php index.php .htaccess
Во-первых, нам необходим файл .htaccess
, содержащий все правила перезаписи URI запроса в index.php
файл (точка входа приложения):
<IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^((?s).*)$ index.php?_url=/$1 [QSA,L] </IfModule>
Основная часть нашего кода будет помещена в index.php
. Файл создается следующим образом:
<?php use Phalcon\Mvc\Micro; $app = new Micro(); $app->handle( $_SERVER["REQUEST_URI"] );
Теперь мы создадим маршруты, как мы определили выше:
<?php use Phalcon\Mvc\Micro; $app = new Micro(); $app->get( '/api/robots', function () { } ); $app->get( '/api/robots/search/{name}', function ($name) { } ); $app->get( '/api/robots/{id:[0-9]+}', function ($id) { } ); $app->post( '/api/robots', function () { } ); $app->put( '/api/robots/{id:[0-9]+}', function ($id) { } ); $app->delete( '/api/robots/{id:[0-9]+}', function ($id) { } ); $app->handle( $_SERVER["REQUEST_URI"] );
При добавлении маршрутов мы используем фактические методы HTTP в качестве имен методов, вызываемых в объекте приложения. Это позволяет нам легко определять точки прослушивания для приложения на основе этих методов HTTP.
Первый параметр каждого вызова метода-это маршрут, а второй-обработчик, т. е. что мы делаем, когда пользователь вызывает этот маршрут. В нашем примере для каждого обработчика определены анонимные функции. Для следующего маршрута: /api/robots/{id:[0-9]+}
, например, явно указывает, что параметр id
должен иметь числовой формат.
Когда определенный маршрут соответствует запрошенному URI, приложение выполняет соответствующий обработчик.
Создание модели.
Наш API предоставляет информацию о robots
, эти данные хранятся в базе данных. Следующая модель позволяет получить доступ к этой таблице объектно-ориентированным способом. Мы реализовали некоторые бизнес-правила, используя встроенные валидаторы и простые валидации. Это даст нам душевное спокойствие, что сохраненные данные отвечают требованиям нашего приложения. Этот файл модели Robots.php
должен быть помещен в папку models
.
<?php namespace MyApp\Models; use Phalcon\Mvc\Model; use Phalcon\Messages\Message; use Phalcon\Validation; use Phalcon\Validation\Validator\Uniqueness; use Phalcon\Validation\Validator\InclusionIn; class Robots extends Model { public function validation() { $validator = new Validation(); $validator->add( "type", new InclusionIn( [ 'message' => 'Тип должен быть "droid", "mechanical", или "virtual"', 'domain' => [ 'droid', 'mechanical', 'virtual', ], ] ) ); $validator->add( 'name', new Uniqueness( [ 'field' => 'name', 'message' => 'Имя робота должно быть уникальным', ] ) ); if ($this->year < 0) { $this->appendMessage( new Message('Год не может быть меньше нуля') ); } if ($this->validationHasFailed() === true) { return false; } } }
Мы прикрепляем к модели три валидатора. Первый проверяет тип робота. Это должен быть droid
, mechanical
или virtual
. Любое другое значение вернет валидатору значение false
, и операция (insert/update) завершится ошибкой. Второй валидатор проверяет уникальность имени для нашего робота. Последний валидатор проверяет поле year
на положительное число.
База данных
Нам нужно подключить наше приложение к базе данных. Для этого примера мы будем использовать популярный MariaDB или аналогичные варианты, такие как MySQL, Aurora и т.д. В дополнение к настройке базы данных, мы собираемся настроить автозапуск, так что наше приложение будет знать, где искать необходимые файлы.
Теперь мы должны установить соединение, которое будет использоваться этой моделью, и подключить его в наше приложение [файл: index.php
]:
<?php use Phalcon\Loader; use Phalcon\Mvc\Micro; use Phalcon\Di\FactoryDefault; use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql; $loader = new Loader(); $loader->registerNamespaces( [ 'MyApp\Models' => __DIR__ . '/models/', ] ); $loader->register(); $container = new FactoryDefault(); $container->set( 'db', function () { return new PdoMysql( [ 'host' => 'localhost', 'username' => 'asimov', 'password' => 'zeroth', 'dbname' => 'robotics', ] ); } ); $app = new Micro($container);
Извлечение данных.
Get
Первый обработчик, который мы реализуем, - это тот, который извлекает данные из базы данных, когда запрос выполняется с помощью метода GET
HTTP. Конечная точка возвращает все записи из базы данных с помощью запроса PHQL и возвращает результаты в формате JSON.
Обработчик для get()
и /api/robots
становится:
<?php $app->get( '/api/robots', function () use ($app) { $phql = 'SELECT id, name ' . 'FROM MyApp\Models\Robots ' . 'ORDER BY name' ; $robots = $app ->modelsManager ->executeQuery($phql) ; $data = []; foreach ($robots as $robot) { $data[] = [ 'id' => $robot->id, 'name' => $robot->name, ]; } echo json_encode($data); } );
PHQL, позволяет нам писать запросы с использованием высокоуровневого объектно-ориентированного диалекта SQL, который внутренне преобразует ваш запрос в правильные инструкции SQL в зависимости от используемой системы баз данных. Оператор use
в анонимной функции предлагает инъекцию объекта из локальной области в анонимную функцию.
Get - Text
Мы можем заставить роботов использовать их имя или часть имени. Эта функция поиска также будет get()
, что касается метода HTTP, и она будет привязана к конечной точке /api/robots/search/{name}
. Реализация аналогична описанной выше. Нам просто нужно немного изменить запрос.
Обработчик поиска по имени будет выглядеть как [Файл: index.php
]:
<?php // Поиск роботов с именем $name в их имени $app->get( '/api/robots/search/{name}', function ($name) use ($app) { $phql = 'SELECT * ' . 'FROM MyApp\Models\Robots ' . 'WHERE name ' . 'LIKE :name: ' . 'ORDER BY name' ; $robots = $app ->modelsManager ->executeQuery( $phql, [ 'name' => '%' . $name . '%' ] ) ; $data = []; foreach ($robots as $robot) { $data[] = [ 'id' => $robot->id, 'name' => $robot->name, ]; } echo json_encode($data); } );
Get - id
Получить робота с помощью их id
iаналогично вышеописанным операциям. Нам просто нужно будет настроить запрос, который мы запускаем к базе данных. Используемый метод HTTP также будет get()
, а конечная точка будет /api/robots/{id:[0-9]+}
. Для этого обработчика мы также сообщаем, если робот не был найден.
Поиск по полю id
очень похож, в этом случае мы также уведомляем, найден робот или нет [Файл: index.php
]:
<?php use Phalcon\Http\Response; $app->get( '/api/robots/{id:[0-9]+}', function ($id) use ($app) { $phql = 'SELECT * ' . 'FROM MyApp\ModelsRobots ' . 'WHERE id = :id:' ; $robot = $app ->modelsManager ->executeQuery( $phql, [ 'id' => $id, ] ) ->getFirst() ; $response = new Response(); if ($robot === false) { $response->setJsonContent( [ 'status' => 'NOT-FOUND' ] ); } else { $response->setJsonContent( [ 'status' => 'FOUND', 'data' => [ 'id' => $robot->id, 'name' => $robot->name ] ] ); } return $response; } );
Вставка данных.
Принимая данные в виде строки JSON, вставленной в тело запроса, мы также используем PHQL для вставки [Файл: index.php
]:
<?php use Phalcon\Http\Response; $app->post( '/api/robots', function () use ($app) { $robot = $app->request->getJsonRawBody(); $phql = 'INSERT INTO MyApp\ModelsRobots ' . '(name, type, year) ' . 'VALUES ' . '(:name:, :type:, :year:)' ; $status = $app ->modelsManager ->executeQuery( $phql, [ 'name' => $robot->name, 'type' => $robot->type, 'year' => $robot->year, ] ) ; $response = new Response(); if ($status->success() === true) { $response->setStatusCode(201, 'Created'); $robot->id = $status->getModel()->id; $response->setJsonContent( [ 'status' => 'OK', 'data' => $robot, ] ); } else { $response->setStatusCode(409, 'Conflict'); $errors = []; foreach ($status->getMessages() as $message) { $errors[] = $message->getMessage(); } $response->setJsonContent( [ 'status' => 'ERROR', 'messages' => $errors, ] ); } return $response; } );
После выполнения запроса к нашей базе данных, используя PHQL, мы создаем совершенно новый объект Response
. Если запрос был выполнен правильно, мы манипулируем ответом, чтобы иметь код состояния 201
и текст Created
. Мы, наконец, обновляем id
недавно созданной записи и отправляем робота обратно с ответом.
Если что-то не так, мы меняем код состояния ответа на 409
с текстом Conflict
и собираем все ошибки, которые были произведены операцией базы данных. Затем мы отправляем эти сообщения об ошибках обратно с ответом.
Обновление данных.
Обновление данных аналогично вставке. Для этой операции мы используем метод put()
HTTP и конечную точку /api/robots/{id:[0-9]+}
. Переданный параметр id
в URL-адресе-это идентификатор обновляемого робота. Данные представлены в формате JSON.
<?php use Phalcon\Http\Response; /app->put( '/api/robots/{id:[0-9]+}', function ($id) use ($app) { $robot = $app->request->getJsonRawBody(); $phql = 'UPDATE MyApp\Models\Robots ' . 'SET name = :name:, type = :type:, year = :year: ' . 'WHERE id = :id:'; $status = $app ->modelsManager ->executeQuery( $phql, [ 'id' => $id, 'name' => $robot->name, 'type' => $robot->type, 'year' => $robot->year, ] ) ; $response = new Response(); if ($status->success() === true) { $response->setJsonContent( [ 'status' => 'OK' ] ); } else { $response->setStatusCode(409, 'Conflict'); $errors = []; foreach ($status->getMessages() as $message) { $errors[] = $message->getMessage(); } $response->setJsonContent( [ 'status' => 'ERROR', 'messages' => $errors, ] ); } return $response; } );
Эта операция очень похожа на ту, которую мы используем при вставке данных. Если операция обновления прошла успешно, мы отправляем обратно полезную нагрузку JSON с OK
.
Если что-то не так, мы меняем код состояния ответа на 409
с текстом Conflict
и собираем все ошибки, которые были произведены при работе с базой данных. Затем мы отправляем эти сообщения об ошибках обратно с ответом.
Удаление данных.
Удаление почти идентично процессу обновления. Для этой операции мы используем метод delete()
HTTP и конечную точку /api/robots/{id:[0-9]+}
. Переданный параметр id
pв URL-адресе-это идентификатор удаляемого робота.
Файл: index.php
снова меняется::
<?php use Phalcon\Http\Response; // Deletes robots based on primary key $app->delete( '/api/robots/{id:[0-9]+}', function ($id) use ($app) { $phql = 'DELETE ' . 'FROM MyApp\Models\Robots ' . 'WHERE id = :id:'; $status = $app ->modelsManager ->executeQuery( $phql, [ 'id' => $id, ] ) ; $response = new Response(); if ($status->success() === true) { $response->setJsonContent( [ 'status' => 'OK' ] ); } else { $response->setStatusCode(409, 'Conflict'); $errors = []; foreach ($status->getMessages() as $message) { $errors[] = $message->getMessage(); } $response->setJsonContent( [ 'status' => 'ERROR', 'messages' => $errors, ] ); } return $response; } );
Если операция удаления выполнена успешно, мы отправляем обратно полезную нагрузку JSON с OK
.
Если что-то не так, мы меняем код состояния ответа на 409
с текстом Conflict
и собираем все ошибки, которые были произведены при работе с базой данных. Затем мы отправляем эти сообщения об ошибках обратно с ответом.
Создание базы данных
Теперь мы создадим базу данных для нашего приложения. Выполните запросы SQL следующим образом:
CREATE DATABASE `robotics`; CREATE TABLE `robotics`.`robots` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(200) COLLATE utf8_bin NOT NULL, `type` varchar(200) COLLATE utf8_bin NOT NULL, `year` smallint(2) unsigned NOT NULL, PRIMARY KEY (`id`) )
Тестирование нашего приложения
С помощью curl мы протестируем каждый маршрут в нашем приложении, проверяя правильность его работы.
Получить все роботы:
curl -i -X GET http://localhost/my-rest-api/api/robots
HTTP/1.1 200 OK
Date: Tue, 21 Jul 2015 07:05:13 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 117
Content-Type: text/html; charset=UTF-8
[{"id":"1","name":"Robotina"},{"id":"2","name":"Astro Boy"},{"id":"3","name":"Terminator"}]
Поиск робота по имени:
curl -i -X GET http://localhost/my-rest-api/api/robots/search/Astro
HTTP/1.1 200 OK
Date: Tue, 21 Jul 2015 07:09:23 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 31
Content-Type: text/html; charset=UTF-8
[{"id":"2","name":"Astro Boy"}]
Получение робота по его id:
curl -i -X GET http://localhost/my-rest-api/api/robots/3
HTTP/1.1 200 OK
Date: Tue, 21 Jul 2015 07:12:18 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 56
Content-Type: text/html; charset=UTF-8
{"status":"FOUND","data":{"id":"3","name":"Terminator"}}
Вставить нового робота:
curl -i -X POST -d '{"name":"C-3PO","type":"Droid","year":1977}' http://localhost/my-rest-api/api/robots
HTTP/1.1 201 Created
Date: Tue, 21 Jul 2015 07:15:09 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 75
Content-Type: text/html; charset=UTF-8
{"status":"OK","data":{"name":"C-3PO","type":"Droid","year":1977,"id":"4"}}
Попробуйте вставить нового робота с именем существующего:
curl -i -X POST -d '{"name":"C-3PO","type":"Droid","year":1977}' http://localhost/my-rest-api/api/robots
HTTP/1.1 409 Conflict
Date: Tue, 21 Jul 2015 07:18:28 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 63
Content-Type: text/html; charset=UTF-8
{"status":"ERROR","messages":["Имя робота должно быть уникальным"]}
Или обновить робота неизвестного типа:
curl -i -X PUT -d '{"name":"ASIMO","type":"humanoid","year":2000}'
http://localhost/my-rest-api/api/robots/4
HTTP/1.1 409 Conflict
Date: Tue, 21 Jul 2015 08:48:01 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 104
Content-Type: text/html; charset=UTF-8
{"status":"ERROR","messages":["Значение поля type должно быть частью списка: Droid, Mechanical, Virtual"]}
Наконец, удалите робота:
curl -i -X DELETE http://localhost/my-rest-api/api/robots/1
HTTP/1.1 200 OK
Date: Tue, 21 Jul 2015 08:49:29 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 15
Content-Type: text/html; charset=UTF-8
{"status":"OK"}
Вывод
Как мы видели, разработка RESTful API с в Phalcon проста с помощью микро-приложений и PHQL.