Мы уже более-менее разобрались с двумя компонентами архитектуры MVC, остался третий M - Model (Модель).
Модель - это PHP класс предназначенный для работы с информацией предоставленной или запрошенной контроллером. Например у вас есть гостевая книга, контроллер передаёт запрос в модель на получение последних десяти записей, модель возвращает эти записи контроллеру, который может передать эти данные в отображение. Контроллер так же может посылать новые записи в модель, обновлять или удалять существующие.
Проще говоря, модель занимается обработкой и управлением данными.
Для начала мы должны определиться с где и какие данные у нас. Это XML-лента, CSV, JSON, База Данных или что-то ещё? Думаю не стоит усложнять и в этом примере мы будем работать с нашим другом базой данных MySQL. Следующим шагом настроим соединение с базой данных MySQL.
Давайте откроем файл инициализации 'application/bootstrap.php' найдём следующую строку:
// 'database' => MODPATH.'database', // Database access
раскомментируем её:
'database' => MODPATH.'database', // Database access
Теперь сохраним его. Мы указали файлу инициализации фреймворка загрузить модуль базы данных, но нам необходимо его настроить. Скопируем 'database.php' из 'modules/database/config/' в 'application/config/'. Откроем файл 'application/config/database.php' и отредактируем его в соответствии с нашими настройками. Мои выглядят следующим образом:
<?php defined('SYSPATH') or die('No direct access allowed.'); return array ( 'default' => array ( 'type' => 'mysql', 'connection' => array( /** * Следующие параметры доступны для MySQL: * * string hostname имя хоста, или сокет * string database имя базы данных * string username имя пользователя * string password пароль пользователя * boolean persistent использовать постоянное соединение? * * Порты и сокеты могут быть добавлены к имени хоста */ 'hostname' => 'localhost', 'database' => 'kohana_test', 'username' => 'kohana_user', 'password' => FALSE, 'persistent' => FALSE, ), 'table_prefix' => '', 'charset' => 'utf8', 'caching' => FALSE, 'profiling' => TRUE, ), 'alternate' => array( 'type' => 'pdo', 'connection' => array( /** * Следуюшие параметры доступны для PDO: * * string dsn имя источника данных * string username имя пользователя * string password пароль пользователя * boolean persistent Использовать постоянное соединение? */ 'dsn' => 'mysql:host=localhost;dbname=kohana_test', 'username' => 'kohana_user', 'password' => FALSE, 'persistent' => FALSE, ), /** * Следующие дополнительные параметры доступны для PDO: * * string identifier set the escaping identifier */ 'table_prefix' => '', 'charset' => 'utf8', 'caching' => FALSE, 'profiling' => TRUE, ), );
Сохраним его. Имя базы: kohana_test, имя пользователя: kohana_user, без пароля. Вы можете использовать свои значения или создать аналогичную базу и пользователя у себя.
Если у нас используется одна база для нескольких приложений, то можно задать префикс таблиц для модуля базы данных. Например, если хотим что бы имя таблиц нашего приложения начиналось с 'ko3_', укажем параметр 'table_prefix' => 'ko3_'. Соответственно у таблиц создаваемых в ручную нужно добавлять этот префикс самостоятельно. При работе с таблицами через модуль базы данных префикс указывать не нужно, он будет добавляться автоматически.
Вот SQL запрос создания таблицы:
CREATE TABLE `posts` ( `id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT, `title` VARCHAR(255) DEFAULT NULL, `post` TEXT, PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC;
Запустите его в вашем любимом MySQL клиенте. Возможно вы заметили, что указана кодировка utf8 в обеих конфигурациях и параметрах создания таблицы. Это позволит нам работа с i18n (интернационализацией).
Создадим новый каталог 'model' в 'application/classes/'. В каталоге 'application/classes/model/' создадим файл 'post.php' со следующим содержимым:
<?php defined('SYSPATH') or die('No direct script access.'); class Model_Post extends Kohana_Model { /** * Get the last 10 posts * @return ARRAY */ public function get_last_posts($limit, $offset = 0) { // SQL: SELECT * FROM 'posts' ORDER BY 'id' DESC LIMIT 0, 10 return DB::select() // SELECT - DB:SELECT ->from('posts') // Из таблицы 'post' ->order_by('id','DESC') // Сортируем по 'id' в обртном порядке ->limit($limit) // Количество записей с результатом ->offset($offset) // пропустив $offset (по умолчанию 0) записей ->execute() // Выполняем ->as_array(); // Результат ввиде массива } } ?>
Разберём код метода 'get_last_posts()': мы используем Query Builder фреймворка Kohana3 для быстрого и легко читаемого построения запроса.
select() - запрашиваем все поля, from('post') - таблицы 'post', limit($limit) - количество строк с результатом запроса, offset($offset) пропустив $offset (по умолчанию 0) строк результата, execute() - выполнить запрос, as_array() - данные вернуть в виде массива с результатами запроса. Вот и всё, ничего сложного.
В SQL наш запрос можно записать так:
SELECT * FROM 'posts' ORDER BY 'id' DESC LIMIT 0, 10;
Для каждого типа запроса к базе используется свой класс для SELECT - DB::select, для INSERT - DB::insert, для UPDATE - DB::update и для DELETE - DB::delete.
Для наглядности, несколько примеров использования разных типов запросов и, соответственно, классов:
DB::insert
INSERT INTO `users` (`username`, `password`) VALUES ('fred', 'p@5sW0Rd')
$query = DB::insert('users', array('username', 'password'))->values(array('fred', 'p@5sW0Rd'));
DB::update
UPDATE `users` SET `username` = 'jane' WHERE `username` = 'john'
$query = DB::update('users')->set(array('username' => 'jane'))->where('username', '=', 'john');
DB::delete
DELETE FROM `users` WHERE `username` IN ('john', 'jane')
$query = DB::delete('users')->where('username', 'IN', array('john', 'jane'));
Теперь у нас есть модель и методом, я уверен, вы хотите его попробовать в деле. Откроем в редакторе 'first.php' из каталога 'application/classes/controller' и добавим в него метод 'action_posts()' со следующим содержимым:
public function action_posts() { $posts = Model::factory('post'); $first = array(); $this->template->title = 'Kohana 3.0 Model Test'; $this->template->meta_keywords = 'PHP, Kohana, KO3, Framework, Model'; $this->template->meta_description = 'A test of the KO3 framework Model'; $this->template->styles = array(); $this->template->scripts = array(); // Получаем 10 последних записей $first['posts'] = $posts->get_last_posts(10); $this->template->content = View::factory('pages/posts', $first); }
В основном этот метод занимается тем, что вызывает метод модели 'get_last_posts()' и получает массив данных, которые мы передаём в отображение. Кстати, о отображениях... Создадим новый файл с именем 'posts.php' в каталоге 'application/views/pages/' и следующим содержимым:
<?php foreach($posts as $post): echo '<div'.HTML::attributes(array('class'=>'post')).">\n"; echo "\t<h2>".$post['title']."</h2>\n"; echo "\t<div>".$post['post']."</div>\n"; echo '</div>'."\n"; endforeach; ?>
В этом отображении в цикле перебирается массив '$post' с данными переданными контроллером, из массива выводятся записи 'titte' и 'post'. Но так как в нашей таблице нет записей, то соответственно ничего выводится не будет. Сейчас мы это исправим, выполним SQL запрос который добавит записи в таблицу:
INSERT INTO `posts`(`id`,`title`,`post`) VALUES (1,'Тестовое сообщение','Здесь у нас немного текста.'); INSERT INTO `posts`(`id`,`title`,`post`) VALUES (2,'Ещё одно сообщение','Ещё немного текста');
Теперь откроем в браузере http://kohana.local/first/posts и должны увидеть две записи на экране.
Добавим возможность добавлять новые записи в базу данных. Откроем модель записей 'application/classes/model/post.php' и создадим новый метод:
/** * Создание записей в таблице * @param string $title Текст заголовка * @param string $post Текст сообщения */ public function add_post($title, $post) { // INSERT INTO 'posts' SET 'title' = $title, 'post' = $post DB::insert('posts',array('title','post')) // Добавляем записи 'title' и 'post' в таблицу 'posts' ->values(array($title, $post)) // 'title' = $title, 'post' = $post ->execute(); }
Вставка происходит тоже довольно просто. Сохраним модель. Вернёмся к отображению 'application/views/pages/posts.php' и изменим содержимое:
<?php if (!empty($msg)): echo '<div '.HTML::attributes(array('class'=>'alert '.$msg_type)).'>'.$msg."</div>\n"; endif; foreach($posts as $post): echo '<div'.HTML::attributes(array('class'=>'post')).">\n"; echo "\t<h2>".$post['title']."</h2>\n"; echo "\t<div>".$post['post']."</div>\n"; echo '</div>'."\n"; endforeach; echo Form::open(url::base().'first/posts/',array('method' => 'post'))."\n"; echo '<div>'; echo "\t".Form::label('title', 'Заголовок')."\n"; echo "\t".Form::input('title')."\n"; echo "</div>\n"; echo '<div>'; echo "\t".Form::label('post', 'Сообщение')."\n"; echo "\t".Form::textarea('post' ,NULL ,array('rows' => 5, 'cols' => 20))."\n"; echo "</div>\n"; echo Form::submit('submit', 'Отправить')."\n"; echo Form::close()."\n"; ?>
Как вы должны заметить, в этом отображении мы используем хэлперы. С хэлпером 'HTML' мы уже немного знакомы, сейчас мы использовали метод 'attributes' который перебирает массив переданных ему параметров и формирует из них атрибуты html тэгов. Второй хэлпер 'Form' генерирует элементы формы.
Form::open($action = NULL, array $attributes = NULL) - генерирует тэг открытия формы,
Form::label($input, $text = NULL, array $attributes = NULL) - генерирует тэг подписи,
Form::input($name, $value = NULL, array $attributes = NULL) -генерирует тэг input,
Form::textarea($name, $body = '', array $attributes = NULL, $double_encode = TRUE) - генерирует тэг textarea
Form::submit($name, $value, array $attributes = NULL) - генерирует тэг submit,
Form::close() - генерирует тэг закрытия формы.
Вернёмся к контроллеру 'application/classes/controller/first.php' и добавим новый метод '_add_post()':
/** * Метод посредник для добавления записи в таблицу * @param string $title Текст заголовка * @param string $post_content Текст сообщения * @access private */ private function _add_post($title, $post_content) { // Загружаем модель $post = Model::factory('post'); // Проверям обязательные поля if(empty($title)) { return(array('error' => 'Пожалуйста введите заголовок.')); } elseif(empty($post_content)) { return(array('error' => 'Пожалуйста введите сообщение.')); } // Записываем в базу данных $post->add_post($title, $post_content); return TRUE; }
Код приведённый выше является посредником между 'action_posts()' и моделью, которая сохраняет записи. Вернёмся к 'action_posts' и добавим немного кода:
public function action_posts() { // Загружаем модель $posts = Model::factory('post'); $first = array(); $this->template->title = 'Kohana 3.0 Model Test'; $this->template->meta_keywords = 'PHP, Kohana, KO3, Framework, Model'; $this->template->meta_description = 'A test of the KO3 framework Model'; $this->template->styles = array(); $this->template->scripts = array(); $firs['msg'] = ''; $firs['msg_type'] = ''; // Обрабатываем POST if($_POST) { $ret = $this->_add_post( Security::xss_clean(Arr::get($_POST, 'title', '')), // Очищаем элемент $_POST['title'] Security::xss_clean(Arr::get($_POST, 'post', ''))); // Очищаем элемент $_POST['post'] if(isset($ret['error'])) { $first['msg'] = $ret['error']; // Текст ошибки $first['msg_type'] = 'error'; // Класс сообщения в отображении } else { $first['msg'] = 'Сохранено.'; $first['msg_type'] = 'success'; } } // Получаем 10 последних записей $first['posts'] = $posts->get_last_posts(10); $this->template->content = View::factory('pages/posts', $first); }
Сохраним контроллер и обновим страницу браузера. Теперь мы можем видеть довольно уродливую форму внизу страницы. Заполните поля и нажмите кнопку "Отправить". Ваше сообщение должно появится вверху страницы под надписью "Сохранено", если вы заполнили не оба поля, то увидите сообщение об ошибке.
Что бы наша страница не была такой страшной добавим файл CSS-стиля:
#container { width: 640px; } h2 { font-size: 1.3em; font-weight: bold; } .alert { font-size: 1.3em; font-weight: bold; margin: 10px; padding: 5px; } .error { background: #fcc; } .success { background: #cfc; } div.post { padding: 5px 5px; margin: 10px; color: #222; background: #eee; border: 1px solid #aaa; display: block; } label { width: 40px; display: block; margin: 2px 0; } form > div { margin: 5px; }
Сохраним его в 'assets/css/' под именем 'post.css'.
В контроллере 'application/classes/controller/first.php' в методе 'action_post()' изменис строку :
$this->template->styles = array();
на
$this->template->styles = array('assets/css/post.css' => 'screen');
Как вы заметили в нашей модели отсутствует механизм изменения записей, можете рассматривать это как домашнее задание.
P.S. За основу взяты материалы с Inside DealTaker и Unofficial Kohana 3.0 Wiki
P.P.S. Я изменил оригинальный исходный код материалов этой статьи, что бы показать больше возможностей фреймворка Kohana 3 и по возможности придерживаться стандарта написания кода фреймворка Conventions and Coding Style. Так же было изменено имя контроллера шаблона Controller_Default_Template и он был вынесен в подкаталог 'default'.
Код к данному руководству можно найти на Google Code.
svn checkout http://dev-mark.googlecode.com/svn/trunk/dev-mark/Kohana3-tutorial Kohana3-tutorial
Другие части руководства:
- Kohana 3: Установка под Ubuntu
- Kohana 3: Первые шаги (Установка и настройка)
- Kohana 3: Работа с отображениями
- Kohana 3: Работа с контроллерами
- Kohana 3: Работа с моделью
2 комментария:
Может немного не по теме, но для новичков может пригодится. Security::xss_clean() в kohana 3.1 уже отсутствует. Сам только начал изучать kohana, поэтому еще не разобрался, чем можно заменить.
Как раз по теме, я начал переводить когда актуальной была Kohana 3.0, а в 3.1 много изменилось.
В 3.0 в Security::xss_clean() использовался вот этот код http://svn.bitflux.ch/repos/public/popoon/trunk/classes/externalinput.php
Можно расширить хелпер Security добавив в класс новый метод xss_clean().
Спасибо, что напомнили по xss_clean()!
Отправить комментарий