Spec-Zone.ru › Phalcon 2

Урок 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API