Суть MVC заключается не в расположении файлов внутри проекта, не в их устройстве и не в программной реализации. С идеями MVC меня в 2006 году познакомил мой преподаватель программирования в университете, после чего в течение года я писал свою реализацию MVC на php, не опираясь ни на один программный продукт реализованный на MVC (просто было лень искать), а используя только понятую мною (причем, возможно, не самым правильным образом) идею.
Я был потрясен, когда многое из того, к чему я пришел самостоятельно я обнаружил в RAILS, что и послужило поводом срочного перехода на RAILS, поскольку изобретать свой велосипед просто надоело. =)
MVC утверждает (в очень приблизительном виде и моей интерпретации), что средства работы с БД (Модель), должны быть полностью отделены от программной логики (Контроллера). Кроме того, программная логика (Контроллер) получив необходимые данные от БД (посредством Модели) должны передать уже подготовленные данные средству отображения (Виду).
И так - общая схема такая.
1. Контроллер, согласно пришедшим от пользователя параметрам вызывает Модель (функции работы с БД).
2. Получив необходимые данные от Модели, Контроллер выполняет подготовку данных для Вида (выполняет формирование переменных и массивов, их преобразования и т.д.) передает готовые данные (массивы и переменные) в Вид.
3. Вид - это перемешка HTML кода + вставка переменных и логики отображения (например цикл, пробегающий по массиву и отрисовывающий его элементы в тегах h3). Фактически - Вид - это HTML шаблонизатор, в который поступают уже готовые данные из Контроллера.
И в чем же отличие от PHP? В чем красота? Ну.. Если вам попадались только PHP проекты устроенные согласно всем канонам MVC - вы очень счастливый человек. Да! MVC можно реализовать на любом языке, просто ROR устроен так, что программы иначе здесь не создаются.
В ROR практически исключены ситуации, когда в HTML коде присутствует обращение к БД через SQL, тут же производится разбор данных и их проверка, и тут же данные отрисовываются.
MVC и ROR разработчики заявляют - ПРОГРАММНОЙ КАШЕ - НЕТ! ДАЕШЬ КРАСИВЫЙ И ПРАВЕЛЬНЫЙ MVC КОД!
Модель (Model)
Модель - это средство работы с таблицами БД. Набор функций и средств создания записей в таблице БД, получения данных из таблицы, обновление данных и удаления записей из таблиц.
Модель в понимании RAILS это класс унаследованный от ActiveRecord::Base.
Что это значит? Хм. Попробуем разобраться.
Что же нам дает то, что существует некоторый класс унаследованный от ActiveRecord::Base?
ActiveRecord - это один из подходов работы с БД.
В Rails ActiveRecord подход реализуется в одноименной библиотеке работы с БД -> ActiveRecord.
Все в Rails подчинено соглашениям - в этом потрясающем фреймворке трудно найти сотню конфигурационных файлов - большинство настроек существует по-умолчанию, витает в воздухе и за счет своей логичности порою задаешь себе вопрос - а зачем вообще нужны конфигурационные файла в других системах. Я не знаю где это настраивается, но по умолчанию я бы настроил, что бы это работало, так как мне нужно - и ЭТО ДЕЙСТВИТЕЛЬНО РАБОТАЕТ ТАК КАК ТЫ ОЖИДАЕШЬ! без дополнительных настроек и конфигураций.
Вот и ActiveRecord задает набор таких соглашений. За счет чего нам легко и просто работать с БД. Рассмотрим пример:
Предположим, что у нас существует миграция:
class CreateRoles < ActiveRecord::Migration
def self.up
create_table 'roles' do |t|
t.string 'name' # Английское имя роли
t.string 'title' # Название Роли
t.text 'description' # Текстовое описание Роли
t.text 'settings' # Настройки Роли
t.timestamps
end
end
def self.down
drop_table 'roles'
end
end
Миграции расположены в каталоге:
c:\ror_server\rails_apps\MY_SUPER_SITE\db\migrate\
Мы запустим выполнение миграции:
c:\ror_server\rails_apps\MY_SUPER_SITE>rake db:migrate
В результате в БД появится таблица roles в которой есть поля:
-roles
name
title
description
settings
created_at
updated_at
Мы создаем файл с описанием класса МОДЕЛИ для работы с данной таблицей:
class Role < ActiveRecord::Base
# Здесь может быть много кода
# Но пока что тут не будет ничего кроме
# 3х строчек с комментариями
end
Все Модели приложения хранятся в каталоге:
c:\ror_server\rails_apps\MY_SUPER_SITE\app\models\
Если знания в области программирования достаточны, то легко понять, что от любого класса можно создать экземпляр (для того они и нужны эти самые классы)
Это значит, что в приложении мы сможем выполнить следующую команду:
role= Role.new
Я надеюсь, что определение класса Role, мы еще не забыли?! =)
Я напомню - в этом классе нет ничего, кроме 3х строчек комментариев.
Однако класс Role унаследован от ActiveRecord::Base, а это значит, что ActiveRecord уже послал SQL запрос в БД и узнал, какие поля находятся в таблице с именем roles и автоматически создал необходимые переменные в классе Role.
Почему именно в таблице в именем roles???
Это очень просто - это негласное СОГЛАШЕНИЕ ActiveRecord и большинства элементов фреймворка - если класс Role, то логичнее всего предположить, что он связан с таблицей roles! Я бы лично сделал именно так! И создатель ActiveRecord тоже так же подумал! Ура!
Следовательно, я могу попробовать сделать следующее:
role= Role.new
role.name= 'administrator'
role.title= 'Роль администратора'
role.description= 'В этой записи в таблице roles хранятся настройки админа'
role.settings= 'админ может все!'
Замечательно! И что дальше! А вот что! Мы выполняем волшебный метод ActiveRecord с названием SAVE
role= Role.new
role.name= 'administrator'
role.title= 'Роль администратора'
role.description= 'В этой записи в таблице roles хранятся настройки админа'
role.settings= 'админ может все!'
role.save
В результате выполненных нами действий, мы создали программное отображение новой записи в таблице БД. Мы заполнили соответствующие поля и выполнили метод save. В результате чего в БД появилась новая запись.
Так мы, не используя SQL запросов можем работать с БД. 99% работы с БД в Rials производится через программное отображение записей таблицы БД.
При сохранении объекта role (выполнение функции role.save) ActiveRecord самостоятельно заполнил поля created_at и updated_at. Следить за изменением данных полей нам не требуется.
Модель (Выборка данных из БД) Метод Find
В приложении нам придется не только создавать записи, но в основном получать их, обновлять или передавать в Вид для отображения. Как же получить необходимые данные из БД? Кночно же, как и все в Rails - все очень просто.
Вспомним определение класса Roles:
class Role < ActiveRecord::Base
# Здесь может быть много кода
# Но пока что тут не будет ничего кроме
# 3х строчек с комментариями
end
Казалось бы - класс абсолютно пустой, но уже сам факт его существования и то, что он унаследован от ActiveRecord::Base нам вполне достаточно.
Всякий класс, унаследованный от ActiveRecord автоматически становится счастливым обладателем метода find, а помощью, которого можно выполнять большинство требуемых выборок из таблицы связанной СОГЛАШЕНИЯМИ с данным классом.
Предположим, что в таблице roles уже есть несколько записей:
(Что бы не засорять эфир - оставлены только некоторые поля)
id |name | title | created_at | updated_at
1 | administrator | Администратор портала | 2009-07-22 16:45:48 | 2009-07-22 16:45:48
2 | registrated_user | Зарегистрированный пользователь | 2009-07-22 16:45:48 | 2009-07-22 16:45:48
3 | guaranted_user_role | Заверенный пользователь | 2009-07-22 16:45:48 | 2009-07-22 16:45:48
4 | site_administrator_role | Администратор сайта | 2009-07-22 16:45:49 | 2009-07-22 16:45:49
Так в приложении мы можем выполнить следующие манипуляции:
# Найти все записи в таблице roles
roles= Role.find(:all)
# Найти первую запись в таблице roles
roles1= Role.find(:first)
# Найти все записи в таблице roles поле created_at в которых равно '2009-07-22 16:45:49'
roles2= Role.find_all_by_created_at('2009-07-22 16:45:49')
# Найти все записи в таблице roles где created_at= '2009-07-22 16:45:49' И поле title= 'Администратор портала'
roles3= Role.find_all_by_created_at_and_title('2009-07-22 16:45:49', 'Администратор портала')
# Найти все записи в таблице roles где created_at= '2009-07-22 16:45:49' ИЛИ поле title= 'Администратор портала'
roles4= Role.find_all_by_created_at_or_title('2009-07-22 16:45:49', 'Администратор портала')
После того как мы нашли некоторый массив записей или одну запись, мы можем выполнить над каждой записью некоторые действия и сохранить результат в БД.
Так например выше в переменную roles1 был отправлен результат поиска первой записи в таблице. roles1= Role.find(:first) Теперь можно изменить переменную roles1 и выполнив сохранение методом save - мы изменим запись в таблице БД.
# Найти первую запись в таблице roles
# Изменить поле title
# Сохранить изменения в БД
roles1= Role.find(:first)
roles1.title= 'Изменил текст в этом поле'
roles1.save
Кроме того, Класс Модели может включать в себя ряд фильтров и функций валидации данных перед сохранением и многое другое - но об этом в следующий раз.
Контроллер (Controller)
Контроллер фактически это файл со списком обработчиков действий для некоторого раздела сайта.
Так например контроллер Pages будет отвечать в проекте за все производимые над страницами сайта.
Контроллер Pages будет включать в себя обработчики действий (функции обеспечивающие работу пользователя на сайте).
Как правило базовый контроллер состоит из набора функций:
-index - список всех объектов данного класса (как правило с логикой пагинации [перелистывание списка объектов по страницам] )
-show - показ всех данных для одного объекта
-new - отработка страницы для создания нового объекта
-create - алгоритм создания нового объекта
-edit - отработка страницы для редактирования объекта
-update - алгоритм обновления объекта
-destroy- убить объект и перейти на какую нибудь страницу =) гыгыгыг!
Контроллер - контроллер, controller
Функция Контроллера - обработчик, action
После выполнения алгоритма внутри обработчика (action) данные передаются в файл отображения (Вид), который носит точно такое же имя как и обработчик. Так если был выполнен обработчик index, то все данные, которые получены в результате его выполнения поступают в файл отображения index.html.erb
ERB - это стандартный шаблонизатор Rails - именно тут и генерируется HTML код.
После выполнения некоторых действий, например destroy, как правило, не происходит отрисовки файла destroy.html.erb (такого файла вообще не существует), а происходит переадресация пользователя к другому действию контроллера - REDIRECT. [redirect_to(admins_users_url) - фактически переадресация на index]
Пример Контроллера построенного с помощью Скаффолда для администрирования объектов класса User.
Такой код обеспечивает весь набор действий CRUD для класса User, т.е. полное БАЗОВОЕ управление пользователями на сайте.
__Смотрим пример и находим места, где действия КОНТРОЛЛЕРА обращаются к БД, посредством класса МОДЕЛИ User__
Полученные данные сохраняются в переменных обозначенных через символ @ - благодаря чему носят глобальный характер и существуют до момента отрисовки в ВИДЕ.
class Admins::UsersController < ApplicationController
# GET /users
def index
@users = User.paginate(:all,
:order=>"created_at ASC", #ASC, DESC
:page => params[:page],
:per_page=>6
)
respond_to do |format|
format.html
end
end
# GET /users/1
def show
@user = User.find(params[:id])
respond_to do |format|
format.html
end
end
# GET /users/new
def new
@user = User.new
respond_to do |format|
format.html
end
end
# GET /users/1/edit
def edit
@user = User.find(params[:id])
end
# POST /users
def create
@user = User.new(params[:user])
respond_to do |format|
if @user.save
flash[:notice] = 'user успешно создано.'
format.html { redirect_to(admins_user_path(@user)) }
else
format.html { render :action => "new" }
end
end
end
# PUT /users/1
def update
@user = User.find(params[:id])
respond_to do |format|
if @user.update_attributes(params[:user])
flash[:notice] = 'user успешно обновлено.'
format.html { redirect_to(edit_admins_user_path(@user)) }
else
format.html { render :action => "edit" }
end
end
end
# DELETE /users/1
def destroy
@user = User.find(params[:id])
@user.destroy
respond_to do |format|
format.html { redirect_to(admins_users_url) }
end
end
end
ВИД (View)
Вид - это файлы отвечающие за отрисовку даннх в виде HTML.
Для каждого класса существует своя папка со своими VIEW файлами.
В файлы Вида приходят данные из Контроллера, внутри переменных обозначенных через символ @
Так например вид может получить от Контроллера переменную @users или @pages_tree
Стандартным шаблонизатором Rails является ERB - он очень похож на PHP видом записи.
HTML теги перемешаны с выводом данных из переменных контроллера:
<div class="site_map">
<div class="title">
<img class="symbol" src="/images/basic/site_map.jpg" alt="" />Карта сайта</div>
<div class="lm55">
<%= show_map(@sections) %>
<%= parse_site_map(@sections) %>
</div>
</div>
Здесь:
<%= show_map(@sections) %>
<%= parse_site_map(@sections) %>
Это вывод на экран результата 2х функций, которые в качестве параметра принимают переменные поступившие из Контроллера.