В прошлый раз я писал о том как настроить фреймворк Kohana для работы с базами данных. Сегодня я постараюсь немного осветить саму работу с БД.
Там же, в прошлом посте, в качестве теста был приведён простейший пример запроса к серверу БД прямо в контроллере. Такая работа есть моветон, хотя и возможна. Для читаемости, расширяемости кода следует стараться придерживаться архитектуры MVC, в которой принято всю работу с данными выносить из контроллера в модели.
В Kohana работу с СУБД можно реализовать несколькими способами
- Писать SQL-запросы вручную используя метод query(). Это даёт большую гибкость при написании, но много рутины. Требуется ручная проверка вводимых данных на предмет вредоносных включений
- Использовать Query Builder. Он позволяет строить запросы независимые от конкретной СУБД. Кроме того они автоматически проверяются. Работает аналогично Active Record в CodeIgniter
1 2 3 4 5 6 7 8
$query = $this->db->select() ->where('id', 3) ->from('products') ->get(); foreach ($query as $row) { echo $row['name']; }
- Использовать ORM. Чем собственно я и попытаюсь заняться в этом посте
Зачем использовать ORM
—————————————-
ORM (Object Relational Mapping) позволяет манипулировать данными обращаясь к полям таблиц как к свойствам объекта. Это позволяет порой радикально уменьшить количество кода, а соответственно уменьшается количество ошибок и улучшается читаемость/расширяемость.
Однако в некоторых случаях ORM оказывается слабым средством. Особенно, когда нужно составлять сложные аналитические запросы. В таких случаях стоит использовать другие средства.
Соглашения ORM
—————————————-
- Названия таблиц должны быть во множественном числе по всем правилам английской грамматики. Например: users, posts, articles.
- Названия моделей должны быть в единственном числе. Например: user, post, article.
- Таблица должна обязательно иметь автоинкрементное поле id
Там есть ещё и другие соглашения, но они касаются связей таблиц друг с другом. Мы пока не будем этим пользоваться.
Итак у нас есть таблица
1 2 3 4 5 6 | CREATE TABLE `articles` ( `id` int(11) NOT NULL auto_increment, `title` varchar(255) NOT NULL, `text` text NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; |
Соответственно, модель должна называться article.
Простейший пример ORM. Kohana2
—————————————-
Создаём файл модели application/models/article.php
1 2 3 | <?php Class Article_Model extends ORM {}; ?> |
В принципе всё готово осталось только использовать. Например, можно вытащить список статей:
1 2 3 4 5 | $articles = ORM::factory('article'); foreach ($articles->find_all() as $article) { echo $article->title; } |
А можно в контроллере вытащить конкретную запись из таблицы
1 2 | $article = ORM::factory('article',1); echo $article->title; |
Можно изменить запись
1 2 3 4 | $article = ORM::factory('article',1); $article->title= "Новый заголовок"; $article->text= "Новый текст"; $article->save(); |
Можно создать новую запись
1 2 3 4 | $article = ORM::factory('article'); //просто не указываем код $article->title= "Новый заголовок"; $article->text= "Новый текст"; $article->save(); |
Список возможностей этим не ограничивается, но мы остановимся. Мы попробуем создать контроллер для создания sitemap.
Создадим файл контроллера application/controllers/sitemap.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?php class Sitemap_Controller extends Controller{ public function index(){ header("Content-Type: text/xml;charset=utf8"); $articles = ORM::factory('article'); $view=new View('sitemap'); $view->articles=$articles; $view->render(true); } } ?> |
Создадим вид application/view/sitemap.php:
1 2 3 4 5 6 7 8 9 10 11 12 | <?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <?php foreach ($articles->find_all() as $article): ?> <url> <loc><?php echo url::base()."mycontroller/article/".$article->id; ?></loc> <changefreq>monthly</changefreq> <priority>0.8</priority> </url> <?php endforeach ?> </urlset> |
Вот впринципе и всё. Работает.
Простейший пример ORM. Kohana3
—————————————-
То же самое, но немного по другому.
Модель application/classes/model/article.php:
1 2 3 | <?php Class Model_Article extends ORM {}; ?> |
Контроллер application/classes/controllers/sitemap.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php class Controller_Sitemap extends Controller { public function action_index(){ $this->request->headers['content-type'] = 'text/xml;charset=utf8'; $articles = ORM::factory('article'); $view=new View('sitemap'); $view->articles=$articles; echo $view; } } ?> |
Вид application/views/sitemap.php:
1 2 3 4 5 6 7 8 9 10 11 12 | <?php echo'<?xml version="1.0" encoding="UTF-8"?>'?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <?php foreach ($articles->find_all() as $article): ?> <url> <loc><?php echo url::base()."mycontroller/article/".$article->id; ?></loc> <changefreq>monthly</changefreq> <priority>0.8</priority> </url> <?php endforeach ?> </urlset> |
Готово.
Послесловие
——————–
Рассмотренный нами случай ORM – простейший за счёт отсутствия связей между таблицами. Обычно модель выглядит сложнее.
PS: Чорд! Забыл сказать, чтобы включить модуль ORM в Kohana3 нужно в файле application/bootstrap.php раскомментировать соответствуюющую строчку:
1 2 3 4 5 6 7 8 9 10 11 12 | /** * Enable modules. Modules are referenced by a relative or absolute path. */ Kohana::modules(array( // 'auth' => MODPATH.'auth', // Basic authentication // 'codebench' => MODPATH.'codebench', // Benchmarking tool 'database' => MODPATH.'database', // Database access // 'image' => MODPATH.'image', // Image manipulation 'orm' => MODPATH.'orm', // Object Relationship Mapping // 'pagination' => MODPATH.'pagination', // Paging of results // 'userguide' => MODPATH.'userguide', // User guide and API documentation )); |
Посты по теме:
RSS-подписка
Подсказка:
public function action_index(){
$this->request->headers['content-type'] = ‘text/xml;charset=utf8′;
…
Автор, расскажите пожалуйста подробнее про связи между таблицами. Очень интересно почитать.
Спасибо за сайт!!!
@ Аркатов Дмирий:
Да!!!! Это то что нужно!!
@ Максим:
Планирую обязательно написать, когда сам достигну достаточного просветления в этом вопросе.
@altesack Огромное Спасибо за материал!!!
Тренируясь, обнаружил в ОRM метод load, пример:
$articles = ORM::factory(’article’);
$articles->load ()
Подскажите, пожалуйста, для каких он целей?
Архитектурно правильнее было бы наверно выбрать статьи из БД в контроллере, а не в представлении, а в представление передавать уже результат. Типа так: $view->articles=ORM::factory(’article’)->find_all()
Я правильно мыслю?
@ Nayjest:
Кстати нужно попробовать. Да, так выглядит правильнее.
@ Atures:
Я пока не могу ответить на этот вопрос, потому что пока немного плаваю в Kohana, а ещё больше в ORM.
А она Вам так сильно нужна?
@altesack
Не сильно, просто интересно ))) Возможно кто-то из комментирующих пользователей подскажет.
Модель application/classes/models/article.php:
Здесь application/classes/model/article.php
Вид application/view/sitemap.php:
Здесь application/views/sitemap.php:
Я про kohana 3
@ KotDev:
Точно! Опечатка. Исправляюсь. Спасибо
Доброго времени суток, в коде создания таблицы, после `text` text NOT NULL не стоит запятая.
С уважением Евгений
Подскажите пожалуйста, в кохане 3 у меня выдает ошибку на строчке:
$this->request->headers['content-type']=‘text/xml;charset=utf8′;
1.{PHP internal call} » Kohana_Core::shutdown_handler(arguments)
На что он ругается?
@ Евгений:
Спасибо поправил.
Насчёт ошибки – может перед вызовом инструкции на вывод уже было что-то отправлено?
1. все равно выдает ошибку на 5 строчке, $this->request->headers['content-type']=‘text/xml;charset=utf8′;
говорит что ErrorException [ Parse Error ]: syntax error, unexpected ‘=’
2. я так и не понял пути какие в третьей кохане писать view или views, также model или models?
p.s. Эта сфера для меня нова
@ Евгений:
Похоже, что у вас в ‘text/xml;charset=utf8′ неправильные кавычки
@ altesack:
Да, закрывающая кавычка в строчке: ‘text/xml;charset=utf8′ неправильная. Как только перебил ее вручную, сразу все заработало. Просьба изменить ее в коде тоже.
Теперь появилась другая ошибка, только уже в строчке:
пишет что syntax error, unexpected T_STRING. Опять видно очепятки в синтаксисе. Что это может быть?
извеняюсь, вставил код вместо текста, строчка на которую ругается кохана вот: …xml version=”1.0″ encoding=”UTF-8″… в файле APPPATH/views/sitemap.php
@ Евгений:
Опечатку справил.
Новую ошибку тоже. Заменил первую строчку.
@ altesack:
Спасибо за оперативность. После всех мыканий заработало, но выдало такую фразу:
Ошибка синтаксического анализа XML: объявление XML или текста не в начале сущности
Адрес: http://mysite.lan/sitemap
Строка 4, символ 1:
^
Это тот результат, что должен получится?
похоже что в этом файле application/views/sitemap.php ругается на синтаксис. Еще я заметил что в коде путь написан “mycontroller/article/” а не “controller/article/” Ведь папку вроде не изменяли…
@ Евгений:
mycontroller – имеется в виду название Вашего контроллера, который должен быть в ссылке на сайтмапе.
Class Controller_Welcome extends Controller_Template
{
public $template = ‘welcome’;
function action_index()
{
$this->request->headers['content-type'] = ‘text/xml;charset=utf8′;
$articles = ORM::factory(’article’);
$this->template->articles = $articles->find_all();
}
}
<?php echo'’?>
id; ?>
monthly
0.8
блин, как же трубно переходить с CakeHP на кохану или кодожжотер. Всё как-то недо… приходится писать то,что кейк за меня делал.
@ Alex:
А кейк ещё жив?
еще как жив!
Alex пишет:
интересно чем же вас не устраивает кейк
Всмысле “не устраивает”?? Почему на нём не пишу? Потому, что не знаю =)
Когда-то, когда принимал решение, за какой фреймворк садиться, кейк по сравнительным отзывам показался мне более трудным и старым (устаревшим).
Это было сугубо субъективное ощущение.
Тоесть получается что создав модель наследуя класс ORM мы все действия по записи данных и выдергиванию переносим в контроллер? Тоесть например добавление статьи в базу происходит так?
class Sitemap_Controller extends Controller{
public function action_add(){
header(”Content-Type: text/xml;charset=utf8″);
$articles = ORM::factory(’article’);
$articles->title = “dsdsd”;
$articles->text = “dsdsd”;
$articles->save();
……………………..
}
}
Так?
Ну примерно так, да. Кстати можно и не выносить в контроллер. Можно тот же метод создать в модели. Я в последнее время так стараюсь делать.
Еще вопрос у тойже таблицы articles есть связь с таблицей tags многим ко многим. В представляение я передаю данные так:
$posts = ORM::factory(’post’)->find_all(10);
$this->template->content = View::factory(’pages/posts’, array(’posts’=>$posts));
А в самом представлении обрабатываю так:
foreach($posts as $post){
echo $post->title;
….
foreach($post->tags->find_all() as $tag){
echo $tag->value.’ “;
}
}
Появляется соответсвенно вопрос а правильно ли что в представлении идет работа с данными? тоесть $post->tags->find_all() ??
А еще можно Джаббер или еще какойни будь видь связи с тем кто разобрался.. Просто возникают мелкие недопонимания и хочется знать как сделать правильнее!