В этой заметке мы рассмотрим процесс создания очень простой электронной энциклопедии с применением Slim Framework. Предположим, что у вас есть таблица базы данных, которая содержит огромное количество энциклопедических заметок и статей. Нам нужно показывать пользователю соответствующую заметку в красивом шаблоне.
Может быть, наша энциклопедия содержит ответы на часто задаваемые вопросы об автомобилях, а может это медицинский справочник. Не суть дела. Главное, что нам не нужен CRUD, так как за наполнение таблицы базы данных будет отвечать другая система. Но нам очень важна стабильность, скорость и простота поддержки этого нехитрого приложения.
Подобную энциклопедию можно очень легко создать средствами других фреймворков и CMS, но я попробую использовать микрофреймворк Slim. Почему? Всё очень просто: настоящая заметка рассчитана на начинающего программиста, который хочет начать изучение Slim Framework.
Приступим к написанию кода. Единственная задача энциклопедии — выводить содержимое в красивом шаблоне. Этот аскетичный функционал похож на API справочной системы. Начнём с реализации модели и всего, что с ней связано. Уникальное имя страницы будет единственным аргументом, который понадобится упомянутому в интерфейсе методу:
Далее необходимо реализовать класс, содержащий метод «getContentByAlias». Так как заметка рассчитана на начинающий уровень читателя, то я специально напомню о необходимости наложить индекс на столбец «alias». При большом количестве материалов в энциклопедии, индекс (по нему будет поиск) позволит повысить скорость работы сервера базы данных. Таких вопросов как кэширование (например, memcached или средствами nginx) мы касаться не будем, так как это отдельная и очень большая тема. А вот про базовые требования безопасности есть смысл упомянуть: переложите ответственность за экранирование на драйвер, например, таким образом:
Обратите внимание, что в запросе я достаточно явно указал подсистемам MySQL, что мне нужна только одна запись («LIMIT 1»), и получил данные с использованием «fetch», т.е. только одну запись. Я использую PDO, а соединение передаю через конструктор класса. Так как Slim Framework не содержит встроенных возможностей для работы с базами данных (в отличие от моих любимых Laravel и Yii 2.0), то нам придётся написать ещё один небольшой класс. Как вы успели заметить, нам понадобится Singleton, который позволит заполучить столь нужное соединение с базой данных. По моему субъективному мнению, подходящий способ реализации выглядит следующим образом:
Вот и настало время реализации самой фабрики. С большой вероятностью такая электронная энциклопедия не будет развиваться (в смысле нового функционала), однако, вероятность вовсе не нулевая, следовательно, нужно предусмотреть на самом начальном этапе проектирования возможность дорабатывать и развивать проект. Реализация фабрики получилась такой:
Я ничего не забыл? Ах, да. Где же сам Slim? Первым делом нам нужно подключить файлы, необходимые для нашего простого приложения — очень аскетичной электронной энциклопедии. На этом этапе мы укажем в настройках режим «debug»:
Помните, что мы должны показывать содержимое в шаблоне в зависимости от алиаса страницы? Посредством второго аргумента метода «render» осуществляется передача данных в представление (View в паттерне MVC). А ещё у нас было требование перенаправлять пользователя на главную страницу в том случае, если нет искомой записи в базе данных. Метод «redirect» решает эту задачу. С использованием «use» мы передаём в область видимости анонимной функции (замыкание или лямбда-функция появилась в PHP начиная с версии 5.3) требуемые массивы и другие переменные. Попробуем:
В тех случаях, когда представление является статическим контентом достаточно использовать такую реализацию:
Давайте подумаем о печальном. Ошибки могут случиться, а значит, мы обязательно должны продумать поведение электронной энциклопедии в подобных ситуациях. Как говорили древние мудрецы: «не единым XDEBUG-ом жив программист», что значит: «в реальных проектах есть смысл подробно логировать некоторые ошибки». Согласно рекомендациям поисковых систем необходимо выдавать 404-ый код ошибки при неудачной попытке найти страницу, однако, в данном примере, в случае любых ошибок я просто перенаправлю пользователя на главную страницу. Для работы с ошибками используйте следующие два замыкания (для примера у меня просто перенаправление с 301-ым кодом ответа):
Всё готово, осталось только вызвать метод «run»:
Просто? Хорошая документация поможет вам понять все возможности Slim Framework. Разумеется, я описал только один из возможных подходов. Необходимость применения того или иного фреймворка определяется программистом самостоятельно (или группой разработчиков на совещании).
Может быть, наша энциклопедия содержит ответы на часто задаваемые вопросы об автомобилях, а может это медицинский справочник. Не суть дела. Главное, что нам не нужен CRUD, так как за наполнение таблицы базы данных будет отвечать другая система. Но нам очень важна стабильность, скорость и простота поддержки этого нехитрого приложения.
Подобную энциклопедию можно очень легко создать средствами других фреймворков и CMS, но я попробую использовать микрофреймворк Slim. Почему? Всё очень просто: настоящая заметка рассчитана на начинающего программиста, который хочет начать изучение Slim Framework.
Приступим к написанию кода. Единственная задача энциклопедии — выводить содержимое в красивом шаблоне. Этот аскетичный функционал похож на API справочной системы. Начнём с реализации модели и всего, что с ней связано. Уникальное имя страницы будет единственным аргументом, который понадобится упомянутому в интерфейсе методу:
interface IPortal {
public function getContentByAlias($alias);
}
Далее необходимо реализовать класс, содержащий метод «getContentByAlias». Так как заметка рассчитана на начинающий уровень читателя, то я специально напомню о необходимости наложить индекс на столбец «alias». При большом количестве материалов в энциклопедии, индекс (по нему будет поиск) позволит повысить скорость работы сервера базы данных. Таких вопросов как кэширование (например, memcached или средствами nginx) мы касаться не будем, так как это отдельная и очень большая тема. А вот про базовые требования безопасности есть смысл упомянуть: переложите ответственность за экранирование на драйвер, например, таким образом:
class Portal implements IPortal {
private $_pdo;
private $_sqlGetContentByAlias = 'SELECT `title`, `content` FROM `pages` WHERE `alias` = ? LIMIT 1;';
/**
* Constructor
*/
public function __construct($_pdo) {
$this->_pdo = $_pdo;
}
/**
* Get content by alias
*/
public function getContentByAlias($alias) {
$stm = $this->_pdo->prepare($this->_sqlGetContentByAlias);
$stm->bindParam(1, $alias, PDO::PARAM_STR);
$stm->execute();
return $stm->fetch();
}
}
Обратите внимание, что в запросе я достаточно явно указал подсистемам MySQL, что мне нужна только одна запись («LIMIT 1»), и получил данные с использованием «fetch», т.е. только одну запись. Я использую PDO, а соединение передаю через конструктор класса. Так как Slim Framework не содержит встроенных возможностей для работы с базами данных (в отличие от моих любимых Laravel и Yii 2.0), то нам придётся написать ещё один небольшой класс. Как вы успели заметить, нам понадобится Singleton, который позволит заполучить столь нужное соединение с базой данных. По моему субъективному мнению, подходящий способ реализации выглядит следующим образом:
class Connect {
private static $_instance;
private $_pdo;
private $_pdoUrl = 'mysql:host=localhost;dbname=kalinin;charset=utf8';
private $_pdoUser = 'root';
private $_pdoPassword = '';
private $_pdoPrm = [PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC];
/**
* Constructor
*/
private function __construct() {
$this->_pdo = new PDO($this->_pdoUrl, $this->_pdoUser,
$this->_pdoPassword, $this->_pdoPrm);
}
/**
* Singleton
*/
private function __clone() {}
private function __wakeup() {}
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new self;
}
return self::$_instance;
}
/**
* Get connection
*/
public function getConnection() {
return $this->_pdo;
}
}
Вот и настало время реализации самой фабрики. С большой вероятностью такая электронная энциклопедия не будет развиваться (в смысле нового функционала), однако, вероятность вовсе не нулевая, следовательно, нужно предусмотреть на самом начальном этапе проектирования возможность дорабатывать и развивать проект. Реализация фабрики получилась такой:
require './app/models/iportal.php';
require './app/models/portal.php';
require './db/connect.php';
class Factory {
public static function create($type) {
$connection = Connect::getInstance()->getConnection();
switch ($type) {
case "simple":
return new Portal($connection);
break;
}
}
}
Я ничего не забыл? Ах, да. Где же сам Slim? Первым делом нам нужно подключить файлы, необходимые для нашего простого приложения — очень аскетичной электронной энциклопедии. На этом этапе мы укажем в настройках режим «debug»:
require 'Slim/Slim.php';
require './app/models/factory.php';
\Slim\Slim::registerAutoloader();
$app = new \Slim\Slim();
$app->config('debug', true);
$app->config(['templates.path' => './app/views/']);
$conf['url'] = 'http://127.0.0.1:8098/';
Помните, что мы должны показывать содержимое в шаблоне в зависимости от алиаса страницы? Посредством второго аргумента метода «render» осуществляется передача данных в представление (View в паттерне MVC). А ещё у нас было требование перенаправлять пользователя на главную страницу в том случае, если нет искомой записи в базе данных. Метод «redirect» решает эту задачу. С использованием «use» мы передаём в область видимости анонимной функции (замыкание или лямбда-функция появилась в PHP начиная с версии 5.3) требуемые массивы и другие переменные. Попробуем:
$app->get('/kalinin/:alias', function ($alias) use ($app, $conf) {
$model = Factory::create("simple");
$content = $model->getContentByAlias($alias);
if(empty($content)) {
$app->redirect($conf['url'], 301);
}
$app->render('page.php', $content);
});
В тех случаях, когда представление является статическим контентом достаточно использовать такую реализацию:
$app->get('/', function () use ($app) {
$app->render('main.php');
});
Давайте подумаем о печальном. Ошибки могут случиться, а значит, мы обязательно должны продумать поведение электронной энциклопедии в подобных ситуациях. Как говорили древние мудрецы: «не единым XDEBUG-ом жив программист», что значит: «в реальных проектах есть смысл подробно логировать некоторые ошибки». Согласно рекомендациям поисковых систем необходимо выдавать 404-ый код ошибки при неудачной попытке найти страницу, однако, в данном примере, в случае любых ошибок я просто перенаправлю пользователя на главную страницу. Для работы с ошибками используйте следующие два замыкания (для примера у меня просто перенаправление с 301-ым кодом ответа):
$app->notFound(function () use ($app, $conf) {
$app->redirect($conf['url'], 301);
});
$app->error(function (\Exception $e) use ($app, $conf) {
$app->redirect($conf['url'], 301);
});
Всё готово, осталось только вызвать метод «run»:
$app->run();
Просто? Хорошая документация поможет вам понять все возможности Slim Framework. Разумеется, я описал только один из возможных подходов. Необходимость применения того или иного фреймворка определяется программистом самостоятельно (или группой разработчиков на совещании).