Framework для создания API

в 7:37, , рубрики: api, framework, php, Песочница, я пиарюсь, метки: , ,

В наше время создавая онлайн сервис, нужно подумать о разработке мобильных и desktop приложений дня него, думаю что не нужно разъяснять почему. Так же нужно чтобы это не было просто «вакуумное» приложение, нужно да бы оно работало с данными, которые лежат на сервере. А так как приложение находится не на стороне сервера и не имеет прямого доступа к нему, неплохо бы им завязать дружеские отношения между собой и «обмениваться фотографиями». Эти взаимоотношения можно построить с помощью API.

В начале

Так как для взаимоотношений нужно как минимум две стороны, в нашем случае это будут:

  • Сервис
  • Приложение

Речь пойдет об минимальном API которое создано для сервиса, деятельность которого связана с книгами. В качестве фреймворка буде использоваться APIFramework.

Framework. Минимальные данные.

Код фреймворка написан на PHP. Имеет MVC-подобную структуру работы. Начало деятельности фреймворка лежит в контроллерах, которые с свою очередь работыотают с моделями и видами. Так как для мобильного приложение, вид в такой форме как мы его знаем — не подходит, в фреймворке он реализован в классе, который «на выходе может дарить вам» JSON, XML или обычный текст.
Фреймворк имеет минимальный комплект возможностей: обращаться к базе данных, хранить логи, загружать конфигурационные файлы ну и работать с контролерами.

Местожительства

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

Общение

Процесс обмена сообщениями между приложением и API основан на GET запросах, также есть возможность передавать данные при помощи POST.

Каждый запрос должен состоять из двух частей:

  • Объект обращения (контроллер)
  • Действие (вызываемый метод контроллера)

На практике это имеет вид:

http://api.<server>/controller.action

В результате запроса будет запущен контроллер (с лева от точки) и определенное действие (с права от точки). Эти два параметра обязательно должны разделятся точкой и не допускается пустые значения, в случае если это произойдет (передадутся пустые значение) — код будет переадресован на контроллер обработки ошибок. В любом случае, будет возвращено результат у формате «который заказывали» будь это JSON или XML.

Пример минимального ответа формата XML:

<?xml version="1.0" encoding="UTF-8">
<status>success</status>

API. Запуск.

Реализация интерфейса берет начало в создании класса самого интерфейса. Этот класс должен наследовать класс APIFramework, в котором реализованы методы для запуска всего процесса. Таких метода три.

Хочу напомнить:

Речь пойдет об минимальном API которое создано для сервиса, деятельность которого связана с книгами.

1-й метод. Инициализация

Так как база данных есть жизненно-важная часть, начнем с нее. Фреймворк поддерживает создание нескольких соединений с разными серверами, нужно всего лишь задать им имена, как на следующем примере соединение с именем local.

new Connection("local", "localhost", "root", "root", "library");

Интересно создается соединение в никуда, правда? На самом деле, у фреймворка есть есть менеджер соединений (он выполнен как одиночка), а класс Connection — всего лишь связной, который сообщает менеджеру об новых соединениях.

Следующим шагом идет подключение плагинов. Фреймворк не выставляет особых требований к плагинам. Это может быть класс, функция или что нибудь другое на языке PHP. Загрузка (подразумевается обычный include) плагинов осуществляется при помощи класса Plugins, который работает по схеме, которая описана выше.

new Plugins(array("User", "Session"));
new Plugins("Language");

Важно заметить, что конструктор принимает как одиночною строку так и массив строк, для подключения нескольких плагинов за один раз.

Последний момент, который хочу отметить — формирование списка доступных контроллеров.

new AvailableControllers(array("error", "books"));

Это не маловажная часть инициализации, именно по этому списку будет происходить проверка на наличие контроллера, который будет вызван из строки запроса:

http://api.<server>/controller.action

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

Не менее важный параметром является outputType. Именно он определяет в каком формате будет возвращен результат. Дабы установить (по умолчанию это JSON) формат возвращаемого значения нужно выполнить функцию:

$this->setOutputType("XML");

На этом базовая инициализация завершена. Правда, в теле инициализации также может присутствовать код разработчика, который необходим для работы, к примеру для создание класса пользователя или что-то другое.

Полный код функции инициализации


public function initialize() {

       new Connection("local", "localhost", "root", "root", "library");

       new Plugins(array("Users"));

       new AvailableControllers(array("error", "books"));

	   $this->setOutputType("JSON");

       $user_id = Session::getUser();
       if($user_id > 0)  $this->user = new User($user_id);	
}
2-й метод. Запуск

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


if(!$this->isValidateController()) {

	Registry::set("unknow_controller_name", $this->getController());

	$this->redirect("error.unknow_controller");
}

Метод getController — является членом родительского класса и возвращает имя текущего контроллера.
Метод redirect — используется для переадресации или простыми словами — для замены имени контроллера и его события.

Также в данном методе можно провести проверку на авторизацию пользователя, или же запретить доступ к некоторым контроллерам если пользователь не авторизированный.

Полный код функции запуска


public function execute() {

	if(!$this->isValidateController()) {

		$this->redirect("error.unknow_controller");
	}

	if(!$this->user) {

		if($this->getController() == "account") $this->redirect("error.unauthorized_user");	
	}
}

После выполнения функции запуска, «ядро» фреймворка будет иметь два ему нужный параметра: контроллер и действие. Их наличия достаточно чтобы продолжить работу. Следующим шагом будет запуск контроллера, речь о котором пойдет ниже.

3-й метод. Завершение работы

Этот метод позволяет выполнить действия, после запуска и контроллера но перед отправкой результатов. В нашем случае с книгами, нет ничего особенного что нужно выполнить в данном методе.

public function finalize() {
}

API. Контроллер.

Контроллер — это своего рода шкатулка с определенным набором инструментов (действий). Каждый контроллер должен наследовать класс Controller, который дает доступ к внешним данным (параметрам переданные через GET или POST запросы):

$book_id = $this->get("book_id");

Также не мало важной частью контроллера является подключение моделей:

new UseModel("Books");

Ну и наконец «обзавестись» событиями или действиями. Подразумевается функция, с именем которой совпадает с именем действия в строке запроса идущее после точки. Пример такого действия, в нашем случае буде получения списка доступных книг, со стороны запроса это будет выглядеть:

http://api.<server>/books.get_available

Соответственно, у нас должен бить контроллер с именем Books и указанное действие в виде функции.

public function get_available() { }

Следуя названию функции, она должна возвратить список доступных книг. Для этого будет использоваться модель и вид (как говорилось выше, под видом подразумевается класс, который возвращает результат в виде JSON или XML.

Модель мы подключили при помощи UseModel. Осталось только наполнить вид, процедура наполнения проста — и ее можно увидеть в полном коде действия.

Полный код действия get_available


public function get_available() {

       // Наша модель
       $books = new Books();

       // Массив из книг, предоставленный моделью
       $items = $books->getAvailable();

       // Наш вид
       $view = new View();

       $view->add("available_books", $items);

       $this->out($view);
}

В конечном итоге, когда вид сформован — нужно его оправить на «выход» при помощи функции контроллера out, в которую передаем вид.

В контроллере количество функций-действий должно быть равно их явному количеству. То есть, если объект (контроллер) выполняет всего одно действие — количество функций должно быть равно 1, если таких действий 2 — соответственно нужно создать две функции с именами этих действий.

Полный код контроллера

class BooksController extends Controller {
	
	public function initialize() {

		new UseModel("Books");
	}

	public function get_available() {

		$books = new Books();

		$items = $books->getAvailable();

		$view = new View();
		$view->add("available_books", $items);
		$this->out($view);
	}
}

В результате обращения к контроллеру по адресу:

http://api.<server>/books.get_available

Будет следующий JSON:

{"available_books":[
{"id":1,"title":"Book's title","pages_count":374},
{"id":2,"title":"Book's title 2","pages_count":965},
{"id":3,"title":"Book's title 3","pages_count":145}
]}

API. Модель.

Модель предоставляет собой объект который знает где и какие данные лежат, а также как с ними работать.

Если рассматривать работу модели, которая использовалась выше для получения списка доступных книг получается следующая вещь. Имеется класс с именем Books (он наследует класс Model), который имеет метод getAvailable, который возвращает массив доступных книг.

Для получается списка книг, нам потребуется простой доступ к базе данных. Для этого создам объект типа Query, который делает это возможным.

$q = new Query("SELECT * FROM `books`");

Следующим шагом должен быть «переход» по строках, которые получены из базы, для это у объекта есть метод isNext который будет возвращать истину (true) до тех пор, пока «не закончатся строки».

$items = array();

while($q->isNext()) {

	$items [] = array(

		"id" => $q["id"],
		"title" => $q["title"],
		"pages_count" => $q["pages_count"]
	);
}

В результате данного метода будет возвращен «накопленный» массив книг.

Полный код модели

class Books extends Model {
	
	public function getAvailable() {

		$q = new Query("SELECT * FROM `books`");

		$items = array();

		while($q->isNext()) {

			$items [] = array(

				"id" => $q["id"],
				"title" => $q["title"],
				"pages_count" => $q["pages_count"]
			);
		}

		return $items;
   }
}

API. Вид.

Пожалуй самый простой из перечисленного, так как ему необходимо всего один метод, add:

$view->add("status", "success");
$view->add("available_books", $items);

API. Структура.

Дерево каталогов выше созданного API имеет следующий вид:

Скрытый текст

boooks-app
- app
    - configuration
       - main.cfg
    - controllers 
       - books 
            books.controller.php
       - error
            error.controoler.php
    - models
       - books
             books.model.php
    - plugins
       - users
            users.plugin.php
            users.class.php
      book.api.php
- cache
    - configuration
    - logs
 - htdocs
      .htaccess
      index.php

Для корректной работы фреймворка потребуется htaccess и его возможности.

.htaccess

RewriteEngine On

RewriteCond %{REQUEST_URI} !/index.php
RewriteCond %{REQUEST_URI} !/favicon.ico

RewriteRule ^(.*) index.php?__api_framework_request_string=$1&%{QUERY_STRING} [L]

Options -Indexes

Файл index.php имеет следующий вид:

index.php


error_reporting(0);

define("APPLICATION_NAME", "Books", true);

define("API_FRAMEWORK_PATH", "<framework path>/", true);
define("APPLICATION_PATH", "<application path>/", true);

require_once API_FRAMEWORK_PATH . "framework.php";
require_once APPLICATION_PATH . "app/" . APPLICATION_NAME . ".API.php";

$application_name = strval(APPLICATION_NAME) . "API";

$application = new $application_name();

Заключение

0. Спасибо что уделили время на прочтение.
1. Надеюсь материал был полезен.
2. Всегда рад критики и благодарности.
3. Фреймворк разработан собственными руками.
4. api-framework

Спасибо.

Автор: littleone

Источник

Поделиться

* - обязательные к заполнению поля