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

kohanaВ прошлый раз я писал о том как настроить фреймворк 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
	));

Посты по теме:

  1. Kohana для чайников. Простейший роутинг
  2. Kohana для чайников. Настраиваем базу данных
  3. Kohana для чайников. Инсталляция.
  4. Kohana для чайников. Hello world.
  5. Kohana для чайников. Избавляемся от index.php

Категории Kohana |
автор: altesack / Понедельник, Январь 18, 2010 / 21 комментов »

21 комментов

    Подсказка:
    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 – имеется в виду название Вашего контроллера, который должен быть в ссылке на сайтмапе.

Ответить