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.