Pull to refresh

Пишем простую энциклопедию на Slim Framework

Reading time5 min
Views25K
В этой заметке мы рассмотрим процесс создания очень простой электронной энциклопедии с применением 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. Разумеется, я описал только один из возможных подходов. Необходимость применения того или иного фреймворка определяется программистом самостоятельно (или группой разработчиков на совещании).
Tags:
Hubs:
Total votes 19: ↑15 and ↓4+11
Comments9

Articles