Практика по Query Builder

Комментарии: 15  Просмотры: 16 067

В этом уроке будет немного практики по использованию Query Builder. С помощью Конструктора Запросов мы сделаем возможным комментирование наших статей и добавим вывод комментариев к каждой статье.
Естественно нам понадобится таблица, в которой будут храниться комментарии. Она, как и в случае со статьями, будет очень упрощенная:

CREATE TABLE IF NOT EXISTS `comments` (
  `id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'Идентификатор комментария',
  `article_id` int(10) NOT NULL COMMENT 'Идентификатор статьи',
  `user` varchar(50) NOT NULL COMMENT 'Имя пользователя',
  `message` text NOT NULL COMMENT 'Текст комментария',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Теперь сделаем Модель, которая будет работать с этой таблицей. Пока нам будет вполне достаточно двух методов: получение комментариев к определенной статье и добавление нового комментария. Создаем файл comment в папке model и пишем следующий код:

<?php defined('SYSPATH') or die('No direct script access.');
 
class Model_Comment extends Model
{
    protected $_tableComments = 'comments';
 
    /**
     * Get comments for article
     * @return array
     */
    public function get_comments($article_id)
    {
        $query = DB::select('user', 'message')
                ->from($this->_tableComments)
                ->where('article_id', '=', $article_id)
                ->execute()
                ->as_array(); 
 
        if($query)
            return $query;
        else
            return array();                       
    }
 
    /**
     * Create new comment
     */
    public function create_comment($article_id, $user, $message)
    {
        DB::insert($this->_tableComments, array('article_id', 'user', 'message'))
            ->values(array($article_id, $user, $message))
            ->execute();                       
    }
}

В модели комментариев метод get_comments получает нужные комментарии, которые относяться к текущей статье, а метод create_comment создает новый комментарий и для этого принимает такие параметры как: идентификатор статьи, имя пользователя и текст сообщения.
Переходим к контроллеру. Мы его уже создавали, только работу с базой эмулировали конструкцией switch-case. Как это выглядело, можно вспомнить Здесь. А теперь кода стало еще меньше:

<?php defined('SYSPATH') or die('No direct script access.');
 
class Controller_Comments extends Controller {
 
    public function action_index()
    {
        if($this->request->is_initial())
            Request::initial()->redirect(URL::site(''));
 
        $article_id = $this->request->param('id');
 
        $content = View::factory('/comments/show')
                    ->bind('comments', $comments);
 
        if($_POST)
        {
            $user = Arr::get($_POST, 'user');
            $message = Arr::get($_POST, 'message');
 
            Model::factory('Comment')->create_comment($article_id, $user, $message);
        }
 
        $comments = Model::factory('Comment')->get_comments($article_id);
        $this->response->body($content);
    }
 
} // Comments

Собственно блок со switch-case был заменен обращениями к модели. Если произошла отправка формы, то вызываем метод create_comment и передаем туда данные, после чего производим вывод. Если отправки формы не было, то просто производим вывод. Я намеренно не усложнял код всякими там проверками, т.к. это не относится к теме данного урока, да и не хочется перегружать вас лишней информацией. Также после отправки формы желательно сделать редирект на текущую страницу, чтобы пользователь постоянными «Обновить» не завалил вас спамом (впрочем если будет капча, то это ему затруднительно будет сделать).
Наконец, остался Вид, который также уже создан (файл views/comments/show), осталось только его немного отредактировать. У меня он получился таким:

<?php foreach($comments as $comment): ?>
 
    <div style="padding:10px; margin-bottom:10px; border-bottom:#999 1px dashed;">
        <strong><?php echo HTML::chars($comment['user']); ?></strong><br />
        <?php echo HTML::chars($comment['message']); ?>
    </div>
 
<?php endforeach; ?>
 
<div style="padding:10px;">
    <form action="" method="post">
        Ваше имя: <br />
        <input name="user" type="text" /><br /><br />
        Сообщение: <br />
        <textarea name="message" cols="25" rows="5"></textarea><br /><br />
        <input name="send" type="submit" value="Отправить" />
    </form>
</div>

Обратите внимание, что для защиты от XSS я обрабатываю данные специальным методом HTML::chars (я о нем рассказывал Здесь). Ну а от SQL-инъекций нас защищает сам Query Builder.
В итоге лично у меня все выглядит вот так:
Query Builder - вывод комментариев
Если у вас что-то неполучилось или что-то непонятно, как обычно задавайте свои вопросы в комментариях или на форуме. Я постараюсь ответить. Удачи.

<< Назад | Вперед >> | Обсудить на форуме


К записи оставлено 15 коммент.

Прошу прощения, код имел некоторые ошибки (в Модели), я их поправил. Просто не все сюда скопипастил из редактора.

Где прописан код в Query Builder от SQL-инъекций?

зри в код и все увидишь.

Вы планируете написание уроков по ORM?

А как реализовать комментарии, на странице где выводятся все статьи? Не совсем удобно, для пользователя нажимать на подробнее, а после этого уже читать коменты.
Хотя бы логику подскажите.

Вообще на всех блогах именно такая схема и стоит. Выводятся превью статей, под каждым превью стоит ссылка «Подробнее», иногда еще стоит ссылка «Комментарии — (количество)», которая впрочем ведет туда же, что и «Подробнее», только с якорем на комментарии. Сами подумайте, если превью 10 штук на странице и на каждое превью выводить простыни комментариев, что это будет.

Но в любом случае про логику. Для вывода комментариев нам достаточно знать ид статьи. Что мешает при получении списка статей для страницы всех статей и переборе их вытаскивать ид каждой статьи ?

На мой взгляд HTML::char лучше при записи в бд делать и записывать уже «чистые» комментарии…т.к. каждый раз при выводе комментов вызывать метод char не логично…хоть и статика)… SELEC-ов же больше чем INSERT-ов =)

Почему не логично ? И при чем тут селект. char к базе никак не обращается.

Я в смысле, что лучше один раз очистить и записать при INSERT, чем каждый раз вызывать статический метод при SELECT…

Да там такой метод.. Насколько я помню, он состоит из единственной функции htmlspecialchars.
Можно еще в контроллере для всего массива ее вызывать и передавать в вид уже преобразованный массив.

Ну я примерно тоже самое и имел ввиду) Только вызывать его перед записью в БД (тем более, что INSERTов точно будет меньше, чем SELECTов при данной задаче)… Ресурсы то может и крохотные получится с экономить, Но мы же правильные программеры =)
А так хороший у тебя блог =) Всё по делу =) Я правда любитель в коде сам всё смотреть =)

доброго времени суток всем. учусь вот кохане по вашим статьям Денис Васильевич. Все довольно понятно и прозрачно, однако решил сделать «шаг вправо шаг влево»
остановился на моменте взаимодействия с БД.
т.е. созданы простые статьи, и нормально работают комментарии к ним (все заносится в бд и выводится)…
решил попробовать сделать простейший чат на главной странице без привязки к статьям, (т.е. казалось бы просто по аналогии вынести все комментарии без разделения на статьи в одну страницу) в БД создал таблицу chat (id, user, chat, chat_msg, date)
сделал модель kohana\application\classes\Mode\chat.php:
http://pastebin.com/0Zgx4k1h
далее создал контроллер kohana\application\classes\Controller\chat.phpl
http://pastebin.com/CykqHtUZ
затем вид kohana\application\views\chat
http://pastebin.com/tkDctfSR

и далее пытаюсь вывести это все на главную через контроллер page.php
http://pastebin.com/iVtK0ixX
(то что не закомментированно (т.е. из статьи — выводится корректно)
если же заменить тем что закоменчено (своим кодом), то форма выводится но добавления комментариев в БД не происходит…((
в бутстрапе вроде прописал… ((
http://pastebin.com/L5njkJSH
голову поломал в чем может быть проблема ?

Ну для начала я не вижу, чтобы вы дату в модель передавали.

В PostgreSQL будет выглядеть так:
CREATE TABLE comments (
«id» serial NOT NULL,
«article_id» int NOT NULL ,
«user» varchar(50) NOT NULL ,
«message» text NOT NULL ,
primary key («id»)
);



Оставить комментарий или два

Пожалуйста, зарегистрируйтесь для комментирования.