Kohana для чайников. Простейший ORM

kohanaВ прошлый раз я писал о том как настроить фреймворк Kohana для работы с базами данных. Сегодня я постараюсь немного осветить саму работу с БД.

Там же, в прошлом посте, в качестве теста был приведён простейший пример запроса к серверу БД прямо в контроллере. Такая работа есть моветон, хотя и возможна. Для читаемости, расширяемости кода следует стараться придерживаться архитектуры MVC, в которой принято всю работу с данными выносить из контроллера в модели.

В Kohana работу с СУБД можно реализовать несколькими способами

  • Писать SQL-запросы вручную используя метод query(). Это даёт большую гибкость при написании, но много рутины. Требуется ручная проверка вводимых данных на предмет вредоносных включений
  • Использовать Query Builder. Он позволяет строить запросы независимые от конкретной СУБД. Кроме того они автоматически проверяются. Работает аналогично Active Record в CodeIgniter
    $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

Там есть ещё и другие соглашения, но они касаются связей таблиц друг с другом. Мы пока не будем этим пользоваться.

Итак у нас есть таблица

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


В принципе всё готово осталось только использовать. Например, можно вытащить список статей:

$articles = ORM::factory('article');
foreach ($articles->find_all() as $article)
{
    echo $article->title;
}

А можно в контроллере вытащить конкретную запись из таблицы

$article = ORM::factory('article',1);
echo $article->title;

Можно изменить запись

$article = ORM::factory('article',1);
$article->title= "Новый заголовок";
$article->text= "Новый текст";
$article->save();

Можно создать новую запись

$article = ORM::factory('article');  //просто не указываем код
$article->title= "Новый заголовок";
$article->text= "Новый текст";
$article->save();

Список возможностей этим не ограничивается, но мы остановимся. Мы попробуем создать контроллер для создания sitemap.

Создадим файл контроллера application/controllers/sitemap.php:

articles=$articles;
        $view->render(true);
    }
}
?>

Создадим вид application/view/sitemap.php:




find_all() as $article): ?>
                
                id; ?>
                monthly
                0.8
                


Вот впринципе и всё. Работает.

Простейший пример ORM. Kohana3
—————————————-
То же самое, но немного по другому.

Модель application/classes/model/article.php:


Контроллер application/classes/controllers/sitemap.php:

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:

'?>


find_all() as $article): ?>
                
                id; ?>
                monthly
                0.8
                


Готово.

Послесловие
———————
Рассмотренный нами случай ORM — простейший за счёт отсутствия связей между таблицами. Обычно модель выглядит сложнее.

PS: Чорд! Забыл сказать, чтобы включить модуль ORM в Kohana3 нужно в файле application/bootstrap.php раскомментировать соответствуюющую строчку:

/**
 * 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
	));

Kohana для чайников. Простейший ORM: 30 комментариев

  1. Аркатов Дмирий

    Подсказка:
    public function action_index(){
    $this->request->headers['content-type'] = ‘text/xml;charset=utf8′;

  2. Максим

    Автор, расскажите пожалуйста подробнее про связи между таблицами. Очень интересно почитать.
    Спасибо за сайт!!!

  3. altesack Автор записи

    @ Аркатов Дмирий:
    Да!!!! Это то что нужно!!
    @ Максим:
    Планирую обязательно написать, когда сам достигну достаточного просветления в этом вопросе.

  4. Atures

    @altesack Огромное Спасибо за материал!!!

    Тренируясь, обнаружил в ОRM метод load, пример:

    $articles = ORM::factory(‘article’);
    $articles->load ()

    Подскажите, пожалуйста, для каких он целей?

  5. Nayjest

    Архитектурно правильнее было бы наверно выбрать статьи из БД в контроллере, а не в представлении, а в представление передавать уже результат. Типа так: $view->articles=ORM::factory(‘article’)->find_all()

    Я правильно мыслю?

  6. altesack Автор записи

    @ Nayjest:
    Кстати нужно попробовать. Да, так выглядит правильнее.
    @ Atures:
    Я пока не могу ответить на этот вопрос, потому что пока немного плаваю в Kohana, а ещё больше в ORM.

    А она Вам так сильно нужна? :)

  7. Atures

    @altesack
    Не сильно, просто интересно ))) Возможно кто-то из комментирующих пользователей подскажет.

  8. KotDev

    Модель application/classes/models/article.php:
    Здесь application/classes/model/article.php

    Вид application/view/sitemap.php:
    Здесь application/views/sitemap.php:

  9. altesack Автор записи

    @ KotDev:
    Точно! Опечатка. Исправляюсь. Спасибо :)

  10. Евгений

    Доброго времени суток, в коде создания таблицы, после `text` text NOT NULL не стоит запятая.
    С уважением Евгений

  11. Евгений

    Подскажите пожалуйста, в кохане 3 у меня выдает ошибку на строчке:
    $this->request->headers['content-type']=‘text/xml;charset=utf8′;
    1.{PHP internal call} » Kohana_Core::shutdown_handler(arguments)
    На что он ругается?

  12. altesack Автор записи

    @ Евгений:
    Спасибо поправил.
    Насчёт ошибки — может перед вызовом инструкции на вывод уже было что-то отправлено?

  13. Евгений

    1. все равно выдает ошибку на 5 строчке, $this->request->headers['content-type']=‘text/xml;charset=utf8′;
    говорит что ErrorException [ Parse Error ]: syntax error, unexpected ‘=’
    2. я так и не понял пути какие в третьей кохане писать view или views, также model или models?

    p.s. Эта сфера для меня нова :)

  14. altesack Автор записи

    @ Евгений:
    Похоже, что у вас в ‘text/xml;charset=utf8′ неправильные кавычки

  15. Евгений

    @ altesack:
    Да, закрывающая кавычка в строчке: ‘text/xml;charset=utf8′ неправильная. Как только перебил ее вручную, сразу все заработало. Просьба изменить ее в коде тоже.
    Теперь появилась другая ошибка, только уже в строчке:

    пишет что syntax error, unexpected T_STRING. Опять видно очепятки в синтаксисе. Что это может быть?

  16. Евгений

    извеняюсь, вставил код вместо текста, строчка на которую ругается кохана вот: …xml version=»1.0″ encoding=»UTF-8″… в файле APPPATH/views/sitemap.php

  17. altesack Автор записи

    @ Евгений:
    Опечатку справил.
    Новую ошибку тоже. Заменил первую строчку.

  18. Евгений

    @ altesack:
    Спасибо за оперативность. После всех мыканий заработало, но выдало такую фразу:
    Ошибка синтаксического анализа XML: объявление XML или текста не в начале сущности
    Адрес: http://mysite.lan/sitemap
    Строка 4, символ 1:
    ^
    Это тот результат, что должен получится?

  19. Евгений

    похоже что в этом файле application/views/sitemap.php ругается на синтаксис. Еще я заметил что в коде путь написан «mycontroller/article/» а не «controller/article/» Ведь папку вроде не изменяли…

  20. Altesack

    @ Евгений:
    mycontroller — имеется в виду название Вашего контроллера, который должен быть в ссылке на сайтмапе.

  21. Евгений

    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

  22. Alex

    блин, как же трубно переходить с CakeHP на кохану или кодожжотер. Всё как-то недо… приходится писать то,что кейк за меня делал.

  23. Николай

    Alex пишет:

    еще как жив!

    интересно чем же вас не устраивает кейк

  24. altesack Автор записи

    интересно чем же вас не устраивает кейк

    Всмысле «не устраивает»?? Почему на нём не пишу? Потому, что не знаю =)
    Когда-то, когда принимал решение, за какой фреймворк садиться, кейк по сравнительным отзывам показался мне более трудным и старым (устаревшим).
    Это было сугубо субъективное ощущение.

  25. BASSON

    Тоесть получается что создав модель наследуя класс 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();
    ……………………..
    }
    }
    Так?

  26. altesack Автор записи

    Ну примерно так, да. Кстати можно и не выносить в контроллер. Можно тот же метод создать в модели. Я в последнее время так стараюсь делать.

  27. BASSON

    Еще вопрос у тойже таблицы 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() ??
    А еще можно Джаббер или еще какойни будь видь связи с тем кто разобрался.. Просто возникают мелкие недопонимания и хочется знать как сделать правильнее!

Комментарии запрещены.