Урок 3: Создание простого REST API
В этом уроке мы объясним, как создать простое приложение, предоставляющее RESTful API с использованием различных методов HTTP:
- GET для получения и поиска данных
- POST для добавления данных
- PUT для обновления данных
- DELETE для удаления данных
Определение API
API состоит из следующих методов:
| Метод | URL | Действие |
|---|---|---|
| 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 ^(.*)$ index.php?_url=/$1 [QSA,L]
</IfModule>
Затем в файле index.php мы создаем следующее:
$app = new \Phalcon\Mvc\Micro(); //define the routes here $app->handle();
Теперь мы создадим маршруты, как определено выше:
$app = new Phalcon\Mvc\Micro();
//Retrieves all robots
$app->get('/api/robots', function() {
});
//Searches for robots with $name in their name
$app->get('/api/robots/search/{name}', function($name) {
});
//Retrieves robots based on primary key
$app->get('/api/robots/{id:[0-9]+}', function($id) {
});
//Adds a new robot
$app->post('/api/robots', function() {
});
//Updates robots based on primary key
$app->put('/api/robots/{id:[0-9]+}', function() {
});
//Deletes robots based on primary key
$app->delete('/api/robots/{id:[0-9]+}', function() {
});
$app->handle();
Каждый маршрут определяется методом с тем же именем, что и метод HTTP, в качестве первого параметра передается шаблон маршрута, а затем обработчик. В данном случае обработчик — это анонимная функция. Следующий маршрут: «/api/robots/{id:[0-9]+}», например, явно устанавливает, что параметр «id» должен иметь числовой формат.
Когда определенный маршрут соответствует запрошенному URI, приложение выполняет соответствующий обработчик.
Создание модели
Наш API предоставляет информацию о «роботах», эти данные хранятся в базе данных. Следующая модель позволяет нам получить доступ к этой таблице объектно-ориентированным способом. Мы реализовали некоторые бизнес-правила, используя встроенные валидаторы и простые проверки. Это позволит нам быть уверенными, что сохраненные данные соответствуют требованиям нашего приложения:
use Phalcon\Mvc\Model,
Phalcon\Mvc\Model\Message,
Phalcon\Mvc\Model\Validator\InclusionIn,
Phalcon\Mvc\Model\Validator\Uniqueness;
class Robots extends Model
{
public function validation()
{
//Type must be: droid, mechanical or virtual
$this->validate(new InclusionIn(
array(
"field" => "type",
"domain" => array("droid", "mechanical", "virtual")
)
));
//Robot name must be unique
$this->validate(new Uniqueness(
array(
"field" => "name",
"message" => "The robot name must be unique"
)
));
//Year cannot be less than zero
if ($this->year < 0) {
$this->appendMessage(new Message("The year cannot be less than zero"));
}
//Check if any messages have been produced
if ($this->validationHasFailed() == true) {
return false;
}
}
}
Теперь мы должны настроить подключение, которое будет использоваться этой моделью, и загрузить его в наше приложение:
// Use Loader() to autoload our model
$loader = new \Phalcon\Loader();
$loader->registerDirs(array(
__DIR__ . '/models/'
))->register();
$di = new \Phalcon\DI\FactoryDefault();
//Set up the database service
$di->set('db', function(){
return new \Phalcon\Db\Adapter\Pdo\Mysql(array(
"host" => "localhost",
"username" => "asimov",
"password" => "zeroth",
"dbname" => "robotics"
));
});
//Create and bind the DI to the application
$app = new \Phalcon\Mvc\Micro($di);
Получение данных
Первый «обработчик», который мы будем реализовывать, это метод GET, который возвращает всех доступных роботов. Давайте воспользуемся PHQL для выполнения этого простого запроса, возвращая результаты в формате JSON:
//Retrieves all robots
$app->get('/api/robots', function() use ($app) {
$phql = "SELECT * FROM Robots ORDER BY name";
$robots = $app->modelsManager->executeQuery($phql);
$data = array();
foreach ($robots as $robot) {
$data[] = array(
'id' => $robot->id,
'name' => $robot->name,
);
}
echo json_encode($data);
});
PHQL, позволяет нам писать запросы, используя высокоуровневый, объектно-ориентированный диалект SQL, который интерпретируется в правильные SQL-запросы в зависимости от используемой системы баз данных. Оператор «use» в анонимной функции позволяет легко передавать переменные из глобальной области видимости в локальную.
Обработчик поиска по имени будет выглядеть так:
//Searches for robots with $name in their name
$app->get('/api/robots/search/{name}', function($name) use ($app) {
$phql = "SELECT * FROM Robots WHERE name LIKE :name: ORDER BY name";
$robots = $app->modelsManager->executeQuery($phql, array(
'name' => '%' . $name . '%'
));
$data = array();
foreach ($robots as $robot) {
$data[] = array(
'id' => $robot->id,
'name' => $robot->name,
);
}
echo json_encode($data);
});
Поиск по полю «id» очень похож, в этом случае мы также сообщаем, найден ли робот или нет:
//Retrieves robots based on primary key
$app->get('/api/robots/{id:[0-9]+}', function($id) use ($app) {
$phql = "SELECT * FROM Robots WHERE id = :id:";
$robot = $app->modelsManager->executeQuery($phql, array(
'id' => $id
))->getFirst();
//Create a response
$response = new Phalcon\Http\Response();
if ($robot == false) {
$response->setJsonContent(array('status' => 'NOT-FOUND'));
} else {
$response->setJsonContent(array(
'status' => 'FOUND',
'data' => array(
'id' => $robot->id,
'name' => $robot->name
)
));
}
return $response;
});
Вставка данных
Принимая данные в виде строки JSON, вставленной в тело запроса, мы также используем PHQL для вставки:
//Adds a new robot
$app->post('/api/robots', function() use ($app) {
$robot = $app->request->getJsonRawBody();
$phql = "INSERT INTO Robots (name, type, year) VALUES (:name:, :type:, :year:)";
$status = $app->modelsManager->executeQuery($phql, array(
'name' => $robot->name,
'type' => $robot->type,
'year' => $robot->year
));
//Create a response
$response = new Phalcon\Http\Response();
//Check if the insertion was successful
if ($status->success() == true) {
//Change the HTTP status
$response->setStatusCode(201, "Created");
$robot->id = $status->getModel()->id;
$response->setJsonContent(array('status' => 'OK', 'data' => $robot));
} else {
//Change the HTTP status
$response->setStatusCode(409, "Conflict");
//Send errors to the client
$errors = array();
foreach ($status->getMessages() as $message) {
$errors[] = $message->getMessage();
}
$response->setJsonContent(array('status' => 'ERROR', 'messages' => $errors));
}
return $response;
});
Обновление данных
Обновление данных аналогично вставке. Переданный в качестве параметра «id» указывает, какой робот должен быть обновлен:
//Updates robots based on primary key
$app->put('/api/robots/{id:[0-9]+}', function($id) use($app) {
$robot = $app->request->getJsonRawBody();
$phql = "UPDATE Robots SET name = :name:, type = :type:, year = :year: WHERE id = :id:";
$status = $app->modelsManager->executeQuery($phql, array(
'id' => $id,
'name' => $robot->name,
'type' => $robot->type,
'year' => $robot->year
));
//Create a response
$response = new Phalcon\Http\Response();
//Check if the insertion was successful
if ($status->success() == true) {
$response->setJsonContent(array('status' => 'OK'));
} else {
//Change the HTTP status
$response->setStatusCode(409, "Conflict");
$errors = array();
foreach ($status->getMessages() as $message) {
$errors[] = $message->getMessage();
}
$response->setJsonContent(array('status' => 'ERROR', 'messages' => $errors));
}
return $response;
});
Удаление данных
Удаление данных аналогично обновлению. Переданный в качестве параметра «id» указывает, какой робот должен быть удален:
//Deletes robots based on primary key
$app->delete('/api/robots/{id:[0-9]+}', function($id) use ($app) {
$phql = "DELETE FROM Robots WHERE id = :id:";
$status = $app->modelsManager->executeQuery($phql, array(
'id' => $id
));
//Create a response
$response = new Phalcon\Http\Response();
if ($status->success() == true) {
$response->setJsonContent(array('status' => 'OK'));
} else {
//Change the HTTP status
$response->setStatusCode(409, "Conflict");
$errors = array();
foreach ($status->getMessages() as $message) {
$errors[] = $message->getMessage();
}
$response->setJsonContent(array('status' => 'ERROR', 'messages' => $errors));
}
return $response;
});
Тестирование приложения
Используя curl, мы протестируем каждый маршрут нашего приложения, проверяя его правильную работу:
Получение всех роботов:
curl -i -X GET http://localhost/my-rest-api/api/robots
HTTP/1.1 200 OK
Date: Wed, 12 Sep 2012 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: Wed, 12 Sep 2012 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: Wed, 12 Sep 2012 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: Wed, 12 Sep 2012 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: Wed, 12 Sep 2012 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":["The robot name must be unique"]}
Или обновление робота с неизвестным типом:
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: Wed, 12 Sep 2012 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":["Value of field 'type' must be part of
list: droid, mechanical, virtual"]}
Наконец, удаление робота:
curl -i -X DELETE http://localhost/my-rest-api/api/robots/4
HTTP/1.1 200 OK
Date: Wed, 12 Sep 2012 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.
© 2011–2016 Phalcon Framework Team
Licensed under the Creative Commons Attribution License 3.0.
https://docs.phalconphp.com/en/2.0.0/reference/tutorial-rest.html