Пример доски объявлений на Kohana

Пример доски объявлений на Kohana

Kohana — довольно молодой PHP фреймворк, форк CI, всецело завязанный на ООП. К достоинствам Kohana можно отнести использование всех возможностей PHP5 на 100%, высокую скорость работы, «легковесность» и простоту как использования, так и изучения. Из минусов отчетливо выделяется небольшое комьюнити, как следствие, не шибко качественная документация и небольшое количество модулей и библиотек.

Не так давно своё знакомство с фреймворками я начинал именно с Kohana и, надо сказать, я был удивлен, насколько легко он мне поддался. Думал будет намного сложнее. Но очень сказывалась нехватка документации и примеров кода. Зарывшись в маны и разбор исходников, через некоторое время, я восполнил интересующие меня пробелы относительно Kohana, и поэтому решил написать эту статью, дабы другим хабралюдям, оказавшимся в схожей ситуации не пришлось по ночам не досыпать и лечить головные боли.

Под катом пример доски объявлений, написанный с помощью Kohana, возможно местами он не претендует на рациональность и здравый смысл, но всё-же я надеюсь услышать конструктивную критику.

Статья рассчитана на людей, имеющих понятие об MVC и ООП, но не имевших, либо мало имевших, дело с фреймворками.

Начнем

Недавно передо мной встала задача написать небольшую доску объявлений на базе новостного сайта, где пользователи могли бы оставлять свои объявления о купле, продаже и прочем. Написать очень быстро. Честно говоря, до этого случая весь мой опыт с фреймворками заключался в установке сэндбокса Symfony и последующем его удалением. А всё, что я знал о фреймворках, так это то, что большинство из них «используют MVC» и то, что они очень облегчают жизнь. Т.к. в то время я уже почитывал Хабр, мне почему-то запала в голову одна из публикаций, которая утверждала, что фреймворк Kohana «cоздан быть легким, быстрым и простым в использовании». Думаю именно поэтому я выбрал его. Итак,

Что мы хотим получить в результате?
  • авторизация/регистрация пользователей
  • просмотр объявлений всеми пользователями;
  • добавлять объявления могут только зарегистрированные;
  • править/удалять могут только авторы и админы;
  • существуют главные категории;
  • существуют под-категории;
  • под-категории содержат объявления;

С тем, что от нас нужно, мы определились, теперь придумаем, как мы это будем реализовывать.

Для регистрации пользователей будем использовать модуль Auth, который входит в стандартную поставку версии 2.3. Для его использования создадим в базе несколько таблиц:

# в этой таблице мы будем хранить пользователей <br/> CREATE TABLE IF NOT EXISTS `users` ( `id` int (11) unsigned NOT NULL auto_increment, `username` varchar (32) NOT NULL default '' , `password` char (50) NOT NULL default '' , `email` varchar (127) NOT NULL default '' , ` join ` int (10) unsigned NOT NULL default '0' , `last_login` int (10) unsigned NOT NULL default '0' , `logins` int (10) unsigned NOT NULL default '0' , PRIMARY KEY (`id`), UNIQUE KEY `uniq_username` (`username`), UNIQUE KEY `uniq_email` (`email`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=3; INSERT INTO `users` (`id`, `username`, `password`, `email`, ` join `, `last_login`, `logins`) VALUES (1, 'admin' , '098f6bcd4621d373cade4e832627b4f6' , 'example@example.com' , 1215075372, 0, 0);

# тут у нас будут описания прав пользователей <br/> CREATE TABLE IF NOT EXISTS `roles` ( `id` int (4) unsigned NOT NULL auto_increment, `name` varchar (32) NOT NULL , `description` varchar (255) NOT NULL , PRIMARY KEY (`id`), UNIQUE KEY `uniq_name` (`name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=3; INSERT INTO `roles` (`id`, `name`, `description`) VALUES (1, 'login' , 'Зарегистрированный' ); INSERT INTO `roles` (`id`, `name`, `description`) VALUES (2, 'admin' , 'Админ' );

# эта таблица будет содержать привязку прав к пользователю <br/> CREATE TABLE IF NOT EXISTS `roles_users` ( `user_id` int (10) unsigned NOT NULL , `role_id` int (10) unsigned NOT NULL , PRIMARY KEY (`user_id`,`role_id`), KEY `fk_role_id` (`role_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; INSERT INTO `roles_users` (`user_id`, `role_id`) VALUES (1, 2);

# a эта таблица будет использоваться модулем Auth для хранения залогиненых пользователей <br/> DROP TABLE IF EXISTS `d_user_tokens`; CREATE TABLE IF NOT EXISTS `user_tokens` ( `id` int (11) unsigned NOT NULL auto_increment, `user_id` int (11) unsigned NOT NULL , `user_agent` varchar (40) NOT NULL , `token` varchar (32) NOT NULL , `created` int (10) unsigned NOT NULL , `expires` int (10) unsigned NOT NULL , PRIMARY KEY (`id`), UNIQUE KEY `uniq_token` (`token`), KEY `user_id` (`user_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `categories` ( `id` int (10) unsigned NOT NULL auto_increment, `parent_id` int (10) unsigned NOT NULL default '0' , `name` varchar (150) character set utf8 NOT NULL , PRIMARY KEY (`id`), KEY `parent_id` (`parent_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=14; INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (1, 0, 'Недвижимость' ); INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (2, 1, 'Квартиры' ); INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (3, 1, 'Комнаты' ); INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (4, 1, 'Дома, дачи' ); INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (5, 0, 'Авто' ); INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (6, 5, 'Легковые автомобили' ); INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (7, 5, 'Запчасти, аксессуары' ); INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (8, 5, 'Мотоциклы, мопеды' ); INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (9, 0, 'Работа' ); INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (10, 9, 'Админ персонал' ); INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (11, 9, 'ИТ, интернет' ); INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (12, 9, 'Работа на дому' ); INSERT INTO `categories` (`id`, `parent_id`, `name`) VALUES (13, 9, 'Другие сферы' );

CREATE TABLE IF NOT EXISTS `items` ( `id` int (10) unsigned NOT NULL auto_increment, `title` varchar (250) character set utf8 NOT NULL , `category_id` int (10) unsigned NOT NULL default '0' , `content` text NOT NULL , `user_id` int (10) unsigned NOT NULL , `datepub` int (10) unsigned NOT NULL default '0' , PRIMARY KEY (`id`), KEY `title` (`title`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=2; INSERT INTO `items` (`id`, `title`, `category_id`, `content`, `user_id`, `datepub`) VALUES (1, 'Тестовое объявление' , 2, 'Это просто объявление. Таких тут вскоре будет много' , 1, 1229251545);

Модели

Модели — это часть MVC, которая представляет данные и реагирует на запросы, являясь конечной инстанцией между скриптом и БД.

Теперь о моделях, которые мы будем использовать. Для работы с пользователями у нас есть стандартные модели модуля Auth, для обращения к статьям и категориям, создадим свои — создайте файл /application/models/category.php и пропишите в нем следующий класс:

Теперь на русском — для модели категорий мы создали расширенный ORM (об этом немного ниже) класс таблицы `categories` с древовидной структкрой (ORM_Tree), у записи которой могут быть потомки в виде записей из этой же таблицы (protected $children = `categories`, по умолчанию родитель — это поле `parent_id`) и каждая запись которой может иметь много вложенных записей таблицы `items`. А для модели объявлений у нас получился простой ORM класс таблицы `items`, которая содержит одну запись из таблицы `users` и пренадлежит таблице `categories`

Контроллеры

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

  1. для главной страницы, заодно и категорий
  2. для под-категорий
  3. для просмотра объявлений
  4. для входа пользователей
  5. для их регистрации

Дальше много кода, большинство из которого интуитивно понятна, и занакома людям, работавшим с ORM и MVC.

Что такое ORM?

В методе index мы собираем массив главных категорий с объектами подкатегорий в нем и кол-вом объявлений(count) в подкатегориях и выводим это всё дело через вид category, который будет описан в главе «Виды».

Метод view показывает нам содержимое всех подкатегорий категории с id, переданным в параметре (как он туда попадёт — в главе «Окончательные настройки»). Также работает метод viewsub с разницей в том, что он применим для подкатегорий.

Теперь контроллер объявлений, items.php:

Думаю, единственный, до этого времени не встречавшийся нам класс — это Forge. Он используется для облегчения работы с формами, но, к сожалению, он исключен из стандартной поставки Kohana. Где его взять, я расскажу ниже.

Ну и контроллер пользователей, user.php:

Интересный момент при создании формы регистрации, а именно метод matches() объекта, который возвращает метод Forge password. При обращении к нему, параметром нужно указать другой объект password, при несоответствии значений которых скрипт выдаст ошибку.

Виды (представления), отвечают за отображение информации и являются конечной прослойкой между пользователем и приложением. Теперь немного о видах. Всего в нашем движке мы использовали три вида, поочередно о каждом:

Как Вы помните, мы ему передавали массив с категориями и подкатегориями, который он и обрабатывает, в цикле выводя их все.

Это «обвертка» для всех страниц, которые мы отображаем. В блоке enter у нас стоит такой-себе триггер, который, в зависимости от статуса пользователя отображает или его имя со ссылкой на выход, или ссылки на вход и регистрацию.

Этот вид показывает нам, само объявление, выводя ссылки на удаление и редактирование в случае, если пользователь админ или автор объявления. Также можно заметить использование двух хелперов — html и text. html::specialchars делает строку «безопасной», html::anchor делает ссылку — первый параметр адрес, второй — текст, ну а text::auto_p автоматически добавляет абзацы к plain text, как сказано в офф. документации «nl2br() on steroids».

📎📎📎📎📎📎📎📎📎📎