DbHelp.ru
Маленький Yii блог
Комментарии
Гость: комент
Гость: wahaha
zolter: Друпал на Yii? ))
nastia: http://drupal.org/
JB: Да там просто ад)) мне всего 3 значения нужно чтобы выпадало, все равно спасибо)...



Сервер Ultima Online - Forest Wars (от создателя данного блога)

Шаг 3 : Что такое MVC?

Рубрика: Первые шаги

Оцените эту статью:

Рейтинг: 0.00 (0)
21 Июл. 2009
Опубликовать в Twitter Написать в Facebook Опубликовать в своем блоге livejournal.com

Yii Framework Blog img http://dbhelp.ru3 Рамки MVC весьма размыты, поэтому моё понимание может не совпадать с полученными вами ранее знаниями. Хотел бы напомнить что все сказанное в уроке является моим личным опытом, и я не навязываю вам думать точно также. Такой материал весьма тяжело рассказывать, поэтому прошу отнестись с пониманием. И так, начнем урок...

---

Введение

Model-view-controller или просто MVC  — это шаблон проектирования приложения. Он применятся как в web разработке, так и в обычном прикладном программировании. Выбор данной архитектуры подразумевает разделение приложения на три логические части (компоненты), каждая из которых выполняет определенный ряд своих функций.

Большая гибкость приложения на MVC и является самым главным плюсом к его использованию. Для себя я отметил следующие вещи:

  1. Вы можете изменять один компонент приложения при минимальном воздействии на другие.
  2. Все таблицы, html код, да и вообще вся верстка — теперь в отдельных файлах. Тонны php кода не будут мешаться с html (превращая приложения в кучу мусора).
  3. Исходя из пункта «два» -  верстальщики начнут вас уважать и благодарить :) Также вы с лёгкостью сможете подключить к приложению шаблонизатор (Smarty или др).
  4. У вас получиться во много раз уменьшить повторение кода

Yii Framework Blog img http://dbhelp.ruyii-poweredКонечно если есть «плюсы», то и есть «минусы». В принципе «минусы» можно найти в чем угодно, поэтому обвинения что «MVC приложение работает медленнее чем приложение на чистом php» — я не рассматриваю. Понимаете, за счет небольшого (именно небольшого) убытка в скорости, вы получаете приложение которое и через десять лет будет легко расширять (что не всегда можно сказать про крупный сайт на чистом php).

Э..?

Yii Framework Blog img http://dbhelp.ruhelloworld Давайте поговорим всё таки за счет чего достигаются все эти плюсы. Как я уже и говорил, приложение разбивается на три части. Рассмотрим подробнее что и для чего:

Контроллер (Controller)

В Yii контроллером называется класс наследуемый от CController или дочернего от него. Сам контроллер у нас занимается следующими действиями:

  • Обрабатывает данные которые пришли от пользователя (GET, POST запросы)
  • Содержит в себе общую логику приложения (проверки, анализ, редиректы). Проверяет имеет ли пользователь доступ к данному куску приложения и тп.
  • Говорит где и что должно быть показано пользователю, но сам ничего не выводит! Те. просто вызывает в нужном месте нужные нам отображения
  • Обращается к модели если надо получить какие то данные (например из базы)

Все данные которые пользователь указывает в адресной строке — попадают на обработку к контроллеру. Если вы не меняли маршруты-по-умолчанию у себя на сайте, то написав localhost/site/ - пользователь обратится к контроллеру Site.

Заметка : В приложении может быть как один контроллер, так и несколько. 

Внутри класса контроллера должны находиться экшинсы которые отвечают за отдельные части приложения. Экшинсы (Actions) — это простые методы, которые имеют приставку «action» перед своим именем.

Вот так выглядит простой контроллер Site с двумя экшинсами:

<?php
// После имени контроллера - обязательно приставка Controller!
// т.е. вместо Site - пишем SiteController. Также наш контроллер
// обязательно должен быть наследован от класса CController
// или дочернего от него
class SiteController extends CController
{
    // перед названием экшинса вы должны написать приставку action,
    // после этого названиея (с большой буквы).
    public function actionIndex()
    {
    }
    public function actionAuthor()
    {
    }
}

Важно запомнить:

  • все экшинсы должны начинаться с приставки «action»
  • все контроллеры должны после имени иметь приставку «Controller»
  • не забывайте наследовать свой контроллер от CController или дочернего от него класса.

Давайте теперь разберем для чего нам экшинсы.

Когда вы набрали в браузере : localhost/site/index/ - вы обратились к контроллеру Site и его экшинсу index (actionIndex). В данном случае весь код который будет внутри экшинса (к которому мы обращаемся) — будет выполнен.

Теперь давайте немного модернизируем наш код:

    public function actionIndex()
    {
        die ("Привет! Это actionIndex()");
    }
    public function actionAuthor()
    {
        die ("Привет! Это actionAuthor()");
    }

Когда мы обратимся к нашему приложению по адресу localhost/site/index/ мы получим на экране надпись:

Привет! Это actionIndex()

А если наберем localhost/site/author/ :

Привет! Это actionAuthor()

Заметка: Функция die() в php — аналогична функции echo. Единственное отличие в том что после вывода  — приложение дальше не выполняется. 

Как вы понимаете благодаря такой разбивке контроллеров вы можете легко планировать страницы внутри своего приложения.

Также хочу добавить, что написав localhost/site/author/?test=hello_world — вы также как и раньше обратитесь к экшинсу author, но на этот раз в нем будет доступна переменная $_GET['test'] со значением «hello_world».

Заметка: Для того что бы использовать красивые адреса вида localhost/site/author/2007/09/01/ и т.п. — надо использовать маршруты! Рассматривать данный материал мы будем отдельно, не в этом уроке!

Обычно все контроллеры приложения в Yii располагаются по адресу protected/controllers/. Имя контроллера также должно иметь приставку Controller. К примеру, если вы хотите что бы по адресу localhost/user/index/ выводился текст «Привет Вася!», вы должны выполнить следующие действия:

  1. В папке protected/controllers/ создать файл UserController.php
  2. В нем разместить следующий код:
    <?php
    class UserController extends CController
    {
        public function actionIndex()
        {
            die ("Привет Вася!");
        }
    }
  3. Набрать в браузере localhost/user/index/ или localhost/user/
Заметка: Хочу отметить что за вывод страницы localhost/ также отвечает контроллер. (Да, он не указан в адресе после localhost, но он все равно есть). В Yii конфиге вы можете указать какой контроллер будет выполняться «по умолчанию» (обычно это SiteController)

Модель (Model)

Модель это класс для работы приложения с базой. Для каждой таблицы с которой предстоит работать — создается своя модель (в Yii это класс наследуемый от CModel или производного от него).

Модель в нашем приложении выполняет следующие действия:

  1. Облегчает работу с данными в базе (при наследовании от CActiveRecord или производного от него)
  2. Проверка данных перед сохранением/добавлением согласно правилам (rules)
  3. Содержит в себе пользовательские методы для более продвинутой работы с базой

Самая простая модель выглядит следующим образом:

<?php
// Наследуем нашу модель от CActiveRecord
class Users extends CActiveRecord
{       
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }
    /**
     * Специально что бы указать с какой таблицей
     * в базе данных работает наша модель.
     *
     * @return unknown
     */
    public function tableName()
    {
        // в нашем случае это таблица user
        return 'user';
    }
}

Заметка: Вы можете спокойно использовать данный каркас у себя в работе (изменяя лишь название класса и таблицу с которой работает модель).

Перед нами модель для работы с пользователями (с таблицей user). Благодаря тому что мы наследуем её от класса CActiveRecord — мы получаем дополнительный функционал (методы find, count, findAll и др.)

Для того что бы обратиться к модели из контроллера необходимо написать:

Название_модели::model()->название_метода();

Либо предварительно создав экземпляр модели:

$model = new Название_модели();
$model->название_метода();

Заметка: В отличии от контроллера после названия класса модели — приставок писать не надо. Мы можем называть наши модели как хотим. Файл модели обычно располагается в папке protected/models и совпадает с названием класса. 

Посмотрите как просто выглядит запрос для получения пользователя с логином - «zolter»:

$user_info = User::model()->findByAttributes(array('login' => 'zolter'));

В данном примере я использовал встроенный метод  findByAttributes в котором указал что необходимо найти запись где поле «login» равняется значению «zolter». Если такая запись в таблице users существует — в переменной $user_info будет сохранен результат (обьект).

Получить доступ к его полям вы можете следующим образом:

echo $user_info->login; // получу логин (zolter)
echo $user_info->id; // получу id

Заметка: Встроенных методов достаточно много. Более подробно они будут описаны в «Шаг 4: Модель». Самую полную информацию по всем методам вы можете получить в API (http://www.yiiframework.com/doc/api/CActiveRecord) [англ] или статье «Active Record» [рус.] (http://www.yiiframework.ru/doc/guide/ru/database.ar)

Если вам не хватает встроенных методов модели — вы можете легко расширить их. Для этого просто добавьте новый метод в свою модель и обращайтесь к нему аналогичным образом.

Заметка: Для каждой таблицы с которой работает приложение должна быть создана соответствующая модель.

Отображение (View)

Главной функцией отображения является вывод данных (которые возможно пришли из контроллера) на экран. Обычно файл отображения содержит в себе элементы пользовательского интерфейса. В нем также допускается использование php для реализации простой-логики. К примеру, выставления проверок: «если пользователь имеет статус админ — тогда выводим этот кусок файла, иначе — другой». Следуя требованиям MVC в отображении должен находиться минимум кода логики приложения (для этого специально существует контроллер).

Заметка: В некоторой документации View называют «отображением», в некоторой — «представлением». Мне больше нравиться первый вариант. 

Вот пример простого отображения, которое служит для вывода меню на сайте:

<h1>меню</h1>
<a href='#'>пункт 1</a>
<a href='#'>пункт 2</a>
<?php if ($user['status'] == «admin»): ?>
     <a href='#'>меню админа</a>
<?php endif; ?>

Как вы видите это простой html код. Ничего сложного! В нем также я влепил проверку "является ли пользователь админом" (если да — тогда выводим дополнительный пункт меню).

Переменная $user может быть объявлена в этом же отображении, а может быть передана с контроллера.

Заметка: для того чтобы вывести отображение из контроллера — используется метод render(«название_отображения»). По умолчанию будет выведено на экран отображение с переданным названием, из папки : views/название_контроллера/название_отображения/

Если в контроллере Test написать $this->render('my_view_file') — файл отображения будет подгружаться из адреса protected/views/test/my_view_file/. Поэтому обычно для каждого контроллера в папке views создается своя папка, со своими отображениями.

Давайте используя уже полученные ранее знания, решим следующую задачу:

Пускай у нас есть таблица users (с полями: id, login, passwd). Зайдя по адресу localhost/admin/index/ на экране должна быть показана информация о пользователе с логином "admin".

Создаем модель для работы с таблицей users. Назавём её User.php и поместим по адресу protected/models:

<?php
class User extends CActiveRecord
{       
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }
    public function tableName()
    {
        // в нашем случае это таблица users
        return 'users';
    }
}

Теперь создадим контроллер который используя модель — получит данные пользователя «admin», и передаст их в отображение. Контроллер создаем в папке protected/controllers и называем AdminController.php :

<?php
class AdminController extends CController
{
    public function actionIndex()
    {
        // Проверяем есть ли такой пользователь в базе.
        // Если есть - сохраняем его данные в $user_info
        if ($user_info = User::model()->findByAttributes(array('login' => 'zolter'))) {
            $this->render('index', array(
                // передаем параметнную $user_info в отображение
                // в котором она будет доступна по имени - "$info"
                'info'    =>    $user_info,
            ));
        } else {
            die('нет такого юзера');           
        }
    }
}

Ну и осталось дело за малым, создаем отображением. Для этого в папке views создаем папку admin (согласно названию нашего контроллера). Теперь в папке admin создаем файл index.php (согласно названию экшинса который его вызывает) :

<pre>
     Id : <?php echo $info->id; ?>
     Логин: <?php echo $info->login; ?>
     Пароль: <?php echo $info->passwd; ?>
</pre>

При запуске localhost/admin/index вы должны увидеть на экране логин, пароль и id админа. (если не забыли предварительно создать его в таблице users).

Как это работает?

  1. Вы набрали localhost/admin/index тем самым обратились в контроллер Admin, и экшинс Index
  2. Далее начал работать код из actionIndex()
  3. Вы запросили из модели User данные, где логин равен значению «zolter». Т.к. Модель User связана с таблицей users — она сделала запрос в БД к этой таблице.
  4. После того как такие данные были найдены — методом render они были переданы в отображение index.php
  5. После этого на экран было показано отображением

Вывод..

Я не ставил перед собой цель описать весь функционал и возможности контроллера-модели-отображения. Надеюсь тем кто не понимал что такое MVC — стало немного понятнее.

Писал в спешке, надеюсь ничего не забыл. Если встретите ошибки в тексте — выделяйте их через Ctrl+Enter (буду очень благодарен).



Если хотите опубликовать этот материал у себя - пожалуйста, разместите ссылку на страницу откуда вы его взяли.
Другие yii статьи:

  1. Сегодня мы поговорим с вами о том как изменить генератор символов на капче. Часто мне стали приходить сообщения на почту ... "Как на Yii капче выводить цифры"

  2. Немного обсудив своё творение «Компонент Rss ленты v 1.0» на русском форуме я решил немного его переделать. Мною было ... "Компонент Rss ленты v2.0"

  3. Интеграция Zend/Pdf в Yii Framework... next   Введение Yii является одним из распостраненных PHP фреймворков. В этой статье мы рассмотрим пример интеграции библиотеки ... "Отображаем PDF на Yii при помощи Zend"

andy_s

Было сказано: Вторник, 21 Июль 2009

Читал в спешке, вроде всё понравилось, начинающим очень поможет понять хотя бы основные моменты. Отдельный плюс за описание MVC в контексте Yii :)

P.S. Не понимаю, почему вы пишите "экшинсы", а не "экшены". Экшинс - это уже множественное число (да и буква И спорная :))

[adm] zolter

Было сказано: Вторник, 21 Июль 2009

согласен. просто привык так :)
потом наверно поправлю, что б правильнее было :)

[guest] andead

Было сказано: Четверг, 23 Июль 2009

очень доступно. спасибо!

[adm] zolter

Было сказано: Четверг, 23 Июль 2009

Пожалуйста)
К сожалению, что то случилось с расширением которое должно подсвечивать синтаксис php. Постараюсь завтра исправить, то читать код наверное не приятно

[guest] Гость

Было сказано: Понедельник, 03 Август 2009

Просто и понятно, спасибо автору!!!!!!

EverCelt

Было сказано: Четверг, 20 Август 2009

вопрос по поводу контроллеров. развернул тестовое приложение testdrive:
виджетот mainmenu создаю меню
array('label'=>'Home', 'url'=>array('/site/index')),
array('label'=>'Contact', 'url'=>array('/site/contact')),
array('label'=>'Test', 'url'=>array('/site/test1'))...
но доступ все равно происходт вот так
http://www.yii.test/testdrive/index.php?r=site/contact
,a не http://www.yii.test/testdrive/site/contact
p.s. извиняюсь, если глупый вопрос:) или по невнимательности:)

EverCelt

Было сказано: Четверг, 20 Август 2009

версия 1.0.8

EverCelt

Было сказано: Четверг, 20 Август 2009

вопрос снят:) не до конца изучил доки

EverCelt

Было сказано: Четверг, 20 Август 2009

чтобы правильно выполнялось "localhost/site/index/ - вы обратились к контроллеру Site и его экшинсу index (actionIndex)"
необходимо изучить
http://www.yiiframework.ru/doc/guide/ru/topics.url
http://dbhelp.ru/how-to-remove-indexphp/page/

[guest] zolter

Было сказано: Пятница, 21 Август 2009

:)

Alexdamo

Было сказано: Пятница, 16 Октябрь 2009

Почему контроллер не может найти свой view.
Есть контроллер TestController extends CController.
В папке views есть папка test.
И вот при вызове render() он не находит в ней view, а если в render прописать так: render ('test/index'), то находит. В контроллерах, созданных автоматически (таких как SiteController), все ОК.

[adm] zolter

Было сказано: Пятница, 16 Октябрь 2009

Т.е. вы пишите:
$this->render('index') - не работает
А:
$this->render('test/index') - работает?

[adm] zolter

Было сказано: Пятница, 16 Октябрь 2009

п.с. при вызове рендера всегда надо указывать какой файл вы хотите отобразить. т.е. для экшинса index в скобках render-а надо писать 'index', для test - писать render('test') и т.д.

Alexdamo

Было сказано: Пятница, 16 Октябрь 2009

Все, разобрался. Дело было в том, что я создал в классе контроллера конструктор. Как только убрал конструктор, все заработало. Если используем конструктор в контроллере, то надо обязательно вызывать его родительский конструктор с указанием id контроллера. Тогда он находит свои views в нужной папке

Т.е. если контроллер TestingController, то конструктор:
{
parent::__construct('Testing');
}

Если конструктор нужен, конечно.
Вот еще один урок о том, что родительский конструктор надо не забывать вызывать.

[adm] zolter

Было сказано: Пятница, 16 Октябрь 2009

Уву. бывает такое :)

[guest] Vic

Было сказано: Пятница, 05 Март 2010

Вроде бы всё не плохо, но только делать die() в контроллере -- верх неприличия.

[guest] zolter

Было сказано: Пятница, 05 Март 2010

Это мой подход, делайте echo :)

[guest] Гость

Было сказано: Среда, 07 Июль 2010

Все почитал вроде все понятно =)) Немогу до конца вкурить как же все таки использовать функции мускула по типу mysql_fetch_array и др. если не трудно помогите буду оч благодарен =))

[guest] wh1te

Было сказано: Вторник, 27 Июль 2010

Спасибо!

[guest] lastalert

Было сказано: Пятница, 30 Июль 2010

Отличная статья.
Автору спасибо =)

lavrenovnn

Было сказано: Среда, 11 Август 2010

Огромная благодарность автору!!! Читая доки, многое не понимал ... прочитав три ваши статьи ... особенно текущую - всё стало понятно!!! Теперь уии со мной!
Единственная заминка была с представлением (1.1.3), выдавал ошибку render - потому как нужен макет, заменив его на renderPartial - вывод без макета - работает отлично!

[guest] zolter

Было сказано: Среда, 11 Август 2010

Спасибо, очень рад что мои статьи все еще помогают :)
Надеюсь в скором времени разгребу с работой и продолжу писать

[guest] Darth_Ixis

Было сказано: Суббота, 28 Август 2010

Экшинсы...бррр)

А статья замечательная, лаконично написано :)

[guest] zolter

Было сказано: Суббота, 28 Август 2010

У мну свой диалект :)

[guest] Гость

Было сказано: Понедельник, 13 Сентябрь 2010

в примере где идет проверка существования логина admin в коде ты ищешь логин zolter ;)

[guest] zolter

Было сказано: Понедельник, 13 Сентябрь 2010

О точно, спасибо поправлю

[guest] Azariil

Было сказано: Вторник, 23 Ноябрь 2010

Одного не пойму - почему если вся логика работы с БД должна быть в Модели, то почему мы запросы к БД через AR делаем в контроллере? По идее за это все должна отвечать модель? Или я чегото недопонимаю?

[guest] Breeze

Было сказано: Воскресенье, 30 Январь 2011

Вот кое-что интересное по MVC принципы на примерах.
Помогает лучше понять суть как по мне:
1)http://chtivo.webhost.ru/articles/mvc.php
2)http://chtivo.webhost.ru/articles/mvc_rate.php

[guest] zolter

Было сказано: Понедельник, 31 Январь 2011

to Breeze
Действительно полезный материал и очень подробный. Спасибо!

[guest] Гость

Было сказано: Вторник, 15 Февраль 2011

Все сделал по примеру - выскакивает ошибка AdminController cannot find the requested view "index". Подскажите, что делать?

[guest] alex3d

Было сказано: Воскресенье, 20 Февраль 2011

Спасибо за статью! Блин... реклама девок в нижнем белье слева от статей явно не дает сконцентрироваться на материале.... :)))))))))))

[guest] Гость

Было сказано: Воскресенье, 15 Май 2011

Просто.... большое спасибо... в жопу всю эту официальную документацию, такое ощущение что они хотят тебя запутать, я думаю она будет полезна уже людям которые полностью в "теме". С удовольствием читаю дальше.... )))

[guest] Гость

Было сказано: Понедельник, 26 Сентябрь 2011

Хороший материал:) с официальной документации действительно тяжелова-то стартануть...

[guest] Гость

Было сказано: Среда, 14 Декабрь 2011

Большое спасибо автору за статью.

[guest] Гость

Было сказано: Четверг, 12 Январь 2012

Спасибо, друг! Просто без понтов объяснил суть, теперь смогу и сам что-то написать.

[guest] Гость

Было сказано: Четверг, 12 Январь 2012

неудобно читать зеленые замечания из-за того, что их нужно прокручивать

[guest] Гость

Было сказано: Четверг, 12 Январь 2012

$user_info = User::model()->findByAttributes(array('login' => 'zolter'));
Ну ладно - это очень простой запрос типа select * from user where login=zolter
А как быть со сложными запросами ?
Там где возвращается набор записей, где они должны быть как-то упорядочены, где накладываются условия из других таблиц...

[guest] zolter

Было сказано: Четверг, 12 Январь 2012

Да очень просто, тогда будет не findBy, а findAllBy. т.е.:

$user_info = User::model()->findAllByAttributes(array('status' => 1), array('order' => 'id DESC'))

т.е. это селект всех пользователей со статусом 1 и отскортированых по полю id

[guest] Гость

Было сказано: Воскресенье, 15 Январь 2012

ну хорошо, это
select * from user where status = 1 order by id desc

а как будет, например, слудующий запрос -
SELECT groups.id, groups.name, users.name
FROM groups
JOIN users ON users.id = groups.owner
WHERE archived = 1 AND groups.owner = $owner
ORDER BY groups.name, users.name ASC

? ;)

[guest] zolter

Было сказано: Воскресенье, 15 Январь 2012

$user = Yii::app()->db->createCommand()
->select('g.id, g.name, u.name')
->from('groups g')
->join('users u', 'u.id = g.owner')
->where('u.archived=1 AND g.owner=:owner', array(':owner'=>$owner))
->queryRow();

разве не круто?

[guest] Гость

Было сказано: Воскресенье, 15 Январь 2012

гм...
наверное это действительно круто,но я че-то синтаксиса не понял ;)
как это работает createcommand()->select()->from()->join()->where()->queryrow() ?!
А нельзя ли как-нибудь просто использовать чистый sql без всяких извращений ? ;)
чтобы не утомлять вас своими вопросами,может бытьподскажете, нет ли какой статейки для чайников, популярно описывающей как пользоваться этой расчудесной activerecord ?

[guest] Гость

Было сказано: Воскресенье, 15 Январь 2012

и все-таки задам еще один вопрос по поводу приведенной вами конструкции createcommand.
- где здесь модели для таблиц users и groups и как обращаться к полям результирующего набора данных ?

$user->g.id
$user->g.name
$user->u.name
?

[guest] zolter

Было сказано: Воскресенье, 15 Январь 2012

Запрос который я привел выше - не требует наличия моделей для этих таблиц. Я просто привел пример коснтруктора запросов который позволяет их удобно делать. Если хотите писать чистым SQL кодом - пожалуйста, такая возможность так же существует:

$sql="SELECT username, email FROM tbl_user";
$dataReader=$connection->createCommand($sql)->query();

По сути красота AR используется только для CRUD операций. Для чего то более сложного - либо createCommand либо через with и тп.

Хорошие статьи - в документации:

http://www.yiiframework.com/doc/guide/1.1/ru/database.dao

http://www.yiiframework.com/doc/guide/1.1/ru/database.query-builder

http://www.yiiframework.com/doc/guide/1.1/ru/database.arr

[guest] zolter

Было сказано: Воскресенье, 15 Январь 2012

Синтаксис ->select()->from()->join()->where()->queryrow() это число для удобства и читаемости. Как уже привел выше - можно и руками писать запросы :)

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


Код:
Имя: