RedBean PHP ORM – за и против

Владимир | | MySQL, PHP, Web разработка.

redbean_logo

Приветствую всех!

Сегодня я хочу рассказать о небольшой ORM библиотеке под названием RedBean с которой я на днях экспериментировал.

Прежде всего, пару слов о ORM (англ. Object-relational mapping, русск. Объектно-реляционная проекция) вообще.

Основное назначение таких библиотек – преобразование объектов, которые используются в программах, в записи реляционных баз данных и обратно.

Дело в том, что все данные в БД хранятся в таблицах (аналог двумерных массивов), а объекты могут быть организованы в сложные иерархии (например, объект типа «Post» может содержать массив объектов типа «Comment»). И для того, чтобы сохранить такую структуру в БД нужно использовать несколько таблиц с дополнительными полями, которые устанавливают связи между записями.

Все это увеличивает сложность SQL запросов и время разработки.

ORM библиотеки могут в таких ситуациях значительно упрощают жизнь разработчику. Но, естественно, за все нужно платить. В данном случае счет вы получите в виде повышенного потребления ресурсов.

Вернемся к RedBean.

Проиллюстрировать работу любой библиотеки проще всего на примере. Поэтому я написал небольшой скрипт.

<?php 
include 'oodb.php';

//подключаемся к БД
RedBean_Setup::kickstartDev('Post,Comment','mysql:host=localhost;dbname=my_database', 'user_name', 'password');

$post = new Post();
$post->title = 'Новая запись';
$post->content = 'Просто какой-то текст';
$id = $post->save();

$comment = new Comment;
$comment->author = 'admin';
$comment->text = 'Очень простой комментарий';
$comment->belongsTo($post);
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<title>Test RedBean</title>
</head>

<body>

<ul>
<?php
$posts = Post::listAll();
foreach ($posts as $post) {
	$curPost = new Post($post['id']);
	echo '<li><strong>'.$curPost->getTitle().'</strong><br />';
	echo $curPost->getContent();
	$comments = $curPost->getRelatedComment();
	echo '<ol>';
	foreach ($comments as $comment) {
		echo '<li>'.$comment->text.' ('.$comment->author.')</li>';
	}
	echo '</ol></li>';
}
?>
</ul>

</body> </html>

Посмотрим, что он делает.

1) Подключаем библиотеку (строка 2). Тут объяснять нечего, RedBean состоит всего из одного файла.

2) Подключаемся к базе данных и создаем объекты, которые будем в ней хранить (строка 5).
Обе операции выполняются с помощью метода kickstartDev. В первом параметре мы указываем перечень нужных объектов (они будут создаваться динамически). В остальных параметрах – данные, необходимые для подключения к базе (dns, имя пользователя и пароль).

Предварительно от нас требуется только создать пустую БД. Созданием таблиц, сохранением и чтением данных будет заниматься RedBean.

Примечание. Метод kickstartDev переводит библиотеку в т.н. fluid режим. При этом если вы измените, количество или названия свойств объектов, то RedBean автоматически изменит структуру таблиц в БД. Этот режим имеет смысл использовать во время разработки. После переноса приложения на «боевой» сервер kickstartDev лучше заменить на kickstartFrozen. Библиотека перейдет во frozen режим, при этом таблицы автоматически изменяться не будут, но и потребление ресурсов сократится.

3) Создаем данные и сохраняем их в БД (строки 7-15).

В данном случае создано два объекта типов Post и Comment, каждый из которых содержит несколько свойств.
Обратите внимание, что свойства создаются автоматически. От нас требуется указать только название свойства и его значение.
Мы просто присваиваем значения нужным свойствам и вызываем метод save(), который записывает объект в БД (строка 10).

Объект Comment принадлежит Post. Эту связь мы устанавливаем с помощью метода belongsTo. При этом использовать save не нужно. Сохранение в БД произойдет автоматически.

4) Читаем данные из базы (строки 29-40).
Тут тоже все достаточно прозрачно. С помощью статического метода listAll мы получаем массив со всеми объектами типа Post, находящимися в БД.

Чтобы получить доступ к комментариям мы создаем объект типа Post (строка 31) и при этом в качестве параметра указываем id.

После этого мы можем использовать метод getRelatedComment для получения массива с комментариями.
Примечание. Название метода getRelatedComment формируется следующим образом. К приставке getRelated добавляется имя объекта, который мы хотим получить.

Затем в цикле мы выводим комментарии.

В результате выполнения скрипта вы увидите такую страницу.

pic1

Теперь посмотрим, что у нас делается с базой данных.

Прежде всего, взгляните на перечень таблиц.

pic2

Как видите, RedBean создал 6 таблиц. Три из них (post, comment_post и comment) используются для хранения данных приложения, остальные – используются самой RedBean.

Теперь посмотрим, что сохранено в самих таблицах.

post

pic3

comment_post

pic4

comment

pic5

Думаю, вы уже догадались, в чем дело.

Между объектами Post и Comment отношение типа ОДИН-К-МНОГИМ и для его реализации не нужна промежуточная таблица. Т.е. если бы база создавалась вручную, мы бы обошлись всего двумя таблицами. Соответственно, упрощаются и SQL запросы.

Использовать или не использовать?

Это вопрос философский 🙂 . RedBean не единственная ORM библиотека. Например, существуют более мощные Propel и Doctrine.

Ответ будет зависеть от требований к конкретному приложению. В общем случае при использовании ORM увеличивается скорость разработки и упрощается поддержка, но при этом растет потребление ресурсов.

К тому же стоит учесть время, необходимое на изучение библиотеки. В этом RedBean значительно превосходит библиотеки типа Doctrine. Вряд ли у вас уйдет больше пары дней на эксперименты с RedBean.

В общем, думаю, что в ряде случаев использование RedBean будет вполне оправданно.

В заключение хочу сказать, что это статью нельзя рассматривать как руководство по использованию библиотеки. Поэтому я очень рекомендую вам почитать документацию на сайте.

  • В качестве ознакомления — очень даже сойдёт (-: Не только Доктриной мир полон (-:
    Спасибо (-:

  • В качестве ознакомления — очень даже сойдёт (-: Не только Доктриной мир полон (-:
    Спасибо (-:

  • Я давно сам написал подобный класс и наследую его от более сложными.
    Честно говоря никогда не задумывался что существует готовое решение.

    пример использования моего класса примерно таков

    $page = new Object('page');

    $page->insert(array(
    'title' => 'Заголовок'
    'content' => 'Контент'
    ));

    //---------

    $page->id = 800;
    $page->get();

    // В $page->data[] - все что получено под маской select(*)

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

    • Все типы отношений поддерживаются. Поэтому проблем с сохранением тегов не будет.

  • Я давно сам написал подобный класс и наследую его от более сложными.
    Честно говоря никогда не задумывался что существует готовое решение.

    пример использования моего класса примерно таков

    $page = new Object('page');

    $page->insert(array(
    'title' => 'Заголовок'
    'content' => 'Контент'
    ));

    //---------

    $page->id = 800;
    $page->get();

    // В $page->data[] - все что получено под маской select(*)

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

    • Все типы отношений поддерживаются. Поэтому проблем с сохранением тегов не будет.

  • bersy

    можно рассматривать как руководство, для вообще не имеющих никакого представления об ORM, типа меня, да да на блог подписаны не только мега-кодеры 🙂

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

    ну а при использовании конечно надо будет покопаться в оф. доках

  • bersy

    можно рассматривать как руководство, для вообще не имеющих никакого представления об ORM, типа меня, да да на блог подписаны не только мега-кодеры 🙂

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

    ну а при использовании конечно надо будет покопаться в оф. доках

  • Есть ли готовые ORM для CodeIgniter ?

    • Есть, например, этот. Но возможности очень ограниченные.
      Кроме того, не обязательно чтобы ORM был разработан специально для CI. В общем-то ничто не мешает использовать все остальные ORM вместе с CI.

  • Есть ли готовые ORM для CodeIgniter ?

    • Есть, например, этот. Но возможности очень ограниченные.
      Кроме того, не обязательно чтобы ORM был разработан специально для CI. В общем-то ничто не мешает использовать все остальные ORM вместе с CI.

  • Fominov

    Все равно немного грязный код получается. В Ruby On Rails все это сделано намного чище и красивее. После Рельсов я удивился, что в Codeigniter нет такой функциональности. При этом названная Active Records сильно урезана по функционально.

    • Если не сложно, приведите пример кода Ruby On Rails. Мне просто интересно в чем чистота заключается 😉

      • Ну например вот
        /models/post.rb
        class Post < ActiveRecord::Base
        end

        /controllers/post_controller.rb
        def index
        @myposts = Post.find :all
        @admin = session[:admin]
        end

        def view
        if params[:id]
        @mypost = Post.find(params[:id])
        @post_name = @mypost.post_name
        @post_text = @mypost.post_text
        @post_id = @mypost.id
        @comments = Comment.find_all_by_post_id(params[:id])
        @user_name = cookies[:user_name]
        @user_email = cookies[:user_email]
        end
        end

        • Спасибо!
          Я Руби не изучал, но в принципе код читается легко.
          Насколько я понял класс Comment такой же как и Post?
          Дело в том, что в разных PHP фреймворках реализации Active Record немного отличаются. Я недавно начал изучать Yii, в их варианте можно указать массив со связями между таблицами. Например, указать по каком поля связаны таблицы Post и Comment.
          После этого массив с комментариями можно прочитать как обычное свойство класса ($post->comments). Запрос будет выполнен фреймворком при первой попытке доступа к этому свойству, т.е. явно вызывать метод вроде find_all_by_post_id не нужно.

        • Да, можно создавать связи между таблицами, просто когда я писал этот код, я в этом еще не разбирался.

        • Богдан

          Полезный пример.
          Спасибо.

  • Все равно немного грязный код получается. В Ruby On Rails все это сделано намного чище и красивее. После Рельсов я удивился, что в Codeigniter нет такой функциональности. При этом названная Active Records сильно урезана по функционально.

    • Если не сложно, приведите пример кода Ruby On Rails. Мне просто интересно в чем чистота заключается 😉

      • Ну например вот
        /models/post.rb
        class Post < ActiveRecord::Base
        end

        /controllers/post_controller.rb
        def index
        @myposts = Post.find :all
        @admin = session[:admin]
        end

        def view
        if params[:id]
        @mypost = Post.find(params[:id])
        @post_name = @mypost.post_name
        @post_text = @mypost.post_text
        @post_id = @mypost.id
        @comments = Comment.find_all_by_post_id(params[:id])
        @user_name = cookies[:user_name]
        @user_email = cookies[:user_email]
        end
        end

        • Спасибо!
          Я Руби не изучал, но в принципе код читается легко.
          Насколько я понял класс Comment такой же как и Post?
          Дело в том, что в разных PHP фреймворках реализации Active Record немного отличаются. Я недавно начал изучать Yii, в их варианте можно указать массив со связями между таблицами. Например, указать по каком поля связаны таблицы Post и Comment.
          После этого массив с комментариями можно прочитать как обычное свойство класса ($post->comments). Запрос будет выполнен фреймворком при первой попытке доступа к этому свойству, т.е. явно вызывать метод вроде find_all_by_post_id не нужно.

        • Да, можно создавать связи между таблицами, просто когда я писал этот код, я в этом еще не разбирался.