Pull to refresh

RedBeanPHP — CodeFirst PHP фреймворк

Reading time 6 min
Views 30K
RedBeanPHP
В данном посте речь пойдет об весьма интересном ORM фреймворке RedBeanPHP. Примечателен он, прежде всего, возможностью создавать структуру базы данных на лету. К тому же фреймворк прост в использовании как две копейки. Моё повествование будет разделено на 3 части.
Во второй части основная тема будет — модели. В третей — изменение логики работы фреймворка.

Перед написанием поста я потрудился и сделал тестовое приложение с 15 000 записей, для того, что бы убедиться на своем опыте в возможности невероятно облегчить работу. Ведь я наверно не один, кто прописывает поля в нескольких местах с жутким осознанием бессмысленности этой работы, особенно на начальном этапе разработки. Наконец появился аналог Entity Framework Code First из .NET, который в свое время вызвал у меня дикий восторг. Итак по порядку.

Истоки


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

Начало


Для подключения необходимо добавить лишь один файл, в котором содержится весь код.

require('rb.php');

Весьма просто, разве нет? Заглянем внутрь и обнаружим, что файл данных весит 260 кбайт и представляет собой собранную из множества файлов версию фреймворка. На GitHub можно скачать обычную версию из примерно 40 файлов. В этом случае для подключения необходим следующий код:

require('redbean.inc.php');

Структура


Фреймворк имеет хорошо организованную структуру классов. В состав входят драйвера для PDO и Oracle. Это значит что RedBeanPHP поддерживает широкий спектр баз данных. В документации указывается поддержка следующих баз данных: MySQL 5, SQLite, PostgreSQL, CUBRID, Oracle. Поддержка последней не входит в rb.php, её надо загружать из GitHub отдельно. Впрочем нам никто не мешает написать собственный драйвер, унаследовав от класса RedBean_Driver.
А для того, чтобы модифицировать внутреннюю логику работы фреймвока, необходимо создать свою версию QueryWriter. Данную тему я затрону детально в третьей части обзора.
Фреймворк поддерживает Plugins, и имеет простенький Logger класс для отображения всех запросов фреймворка к базе на экран. Запись в файл я не нашел, но никаких проблем не представляет унаследовать собственный класс.
Вот код логгера идущего в поставке:

class RedBean_Logger_Default implements RedBean_Logger {
  public function log() {
    if (func_num_args() > 0) {
      foreach (func_get_args() as $argument) {
        if (is_array($argument)) echo print_r($argument,true); else echo $argument;
		echo "<br>\n";
      }
    }
  }
}

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

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


Для подключения необходимо указать строку формата PDO:

R::setup('mysql:host=localhost;dbname=mydatabase',
        'user','password'); //mysql

R::setup('pgsql:host=localhost;dbname=mydatabase',
        'user','password'); //postgresql

R::setup('sqlite:/tmp/dbfile.txt',
        'user','password'); //sqlite

Пример создания новой записи и новой таблицы в случае её отсуствия (только в fluid режиме):

// Создаем объект (bean) работающий с таблицей book
$book = R::dispense( 'book' );

// выставляем значение полей, тип поля будет автоматически модифицирован в зависимости от значения
$book->title = 'Gifted Programmers';
$book->author = 'Charles Xavier'; 
$book->price = 99.99; 
//Сохраняем, первичный ключ id создается автоматически
$id = R::store($book);

А так загружается существующий “bean”.

$book = R::load('book', $id);

Автор называет bean’ми объекты (бин — англ. боб растений), содержащие данные. Данный объект не является моделью и имеет целью просто хранение данных и работа с записями в базе. Как с ними связать модель содержащую бизнес-логику я расскажу во второй части обзора.

Фреймворк по наличию поля id у объекта решает создать или обновить запись. Как изменить эту логику я покажу в третьей части.

Поиск bean происходит посредством задания WHERE выражения:

$needles = R::find('needle',
        ' haystack = :haystack ORDER BY :sortorder', 
            array( 
                ':sortorder'=>$sortorder, 
                ':haystack'=>$haystack 
            )
        );


Там же, во втором параметре, вы можете указать ORDER BY и LIMIT конструкции.

Связи


Работа с реляционной базой данных предполагает наличие связанности между таблицами. RedBeanPHP поддерживает все необходимые типы связей. Для создания связи один-ко-многим мы используем свойство с префиксом own.

// Создаем bean работающий с таблицей village
$village = R::dispense('village');
// Создаем bean’ы работающие с таблицей building
list($mill,$tavern) = R::dispense('building',2);
    
// для связывания просто присвойте массив bean в поле с префиксом own
$village->ownBuilding = array($mill,$tavern);
     
R::store($village);    

В базе данных cоздастся таблица building. В таблицу building будет добавлено поле village_id, указывающее на поле id таблицы village. Так же будет создана связь в родительской таблицу и созданы необходимые индексы. Связь будет иметь свойство каскадного обновления и удаления данных.

Если посмотреть с другой стороны связи, то возможно обращение к родительскому объекту таким образом:

$village = $mill->village

RedBeanPHP поддерживает концепцию “ленивой загрузки”. То есть загрузка связанных данных произойдет в момент обращения к полю объекта.

// запроса к таблице building не происходит 
$village = R::load('village',$id);
// осуществляется запрос к building
$buildings = $village->ownBuilding; 

Own поле является ассоциативным массивом, поэтому мы может заменить отдельный объект:

$village->ownBuilding[$id] = $house;

Что бы удалить связь между объектами необходимо вызвать следующий код (сами объекты удалены не будет):

unset($village->ownBuilding[$someID]); 
R::store($village);

Для удаления bean и связанных с ним родительских объектов:

R::trash( $book );
// или в случае массива
R::trashAll( $books );

Для очистки таблицы:

R::wipe( 'book' );

Для создания связи многие-ко-многим используем свойство с префиксом shared.

$army = R::dispense('army');
$village->sharedArmy[] = $army;
$village2->sharedArmy[] = $army;

В результате будет создана дополнительная таблица army_village и будут созданы нужны связи и индексы. В остальном работа с shared списками аналогична own списком.

Обычно на тип bean’а указывает имя поля объекта.

// $village отображает таблицу village
$village = $building->village    

Но иногда необходимо присвоить bean полям объекта не следующим этому правилу.

list($teacher,$student) = R::dispense('person',2);
$project->student = $student; 
$project->teacher = $teacher; 

При сохранении фреймфок руководствуется типом bean указаным в dispence, и оба дочерних объекта будут сохранены в таблице person, а в таблице project будут созданы поля student_id и teacher_id.
В таком случае при выборке bean из базы может произойти не однозначная ситуация, так как фреймворк не может определить какой тип находиться в полях $project->student и $project->teacher. В данном случае указание типа осуществляется посредством вызова fetchAs:

$teacher = $project->fetchAs('person')->teacher;

Деревья

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

//$subbean и $bean имеют тип category
$subbean = R::dispense('category');
$subbean->title = $title;
$subbean->url = $url;
$subbean->parent = $bean;
R::store($subbean);

В таблице будет создано поле parent_id и необходимые связи и индексы.

Быстродействие


Фреймфорк имеет 2 режима работы: fluide и frozen. Во fluide режиме RedBeanPHP сам позаботиться о структуре вашей таблицы, принеся в жертву быстродействие. После завершения разработки увеличить производительность можно переведя фреймворк с режим frozen:

R::freeze( true ); 

Запросы к базе


ORM поддерживает SQL запросы.

R::exec( 'update page set title="test" where id=1' );

Так же присуствует построитель запросов:

R::$f->begin()
    ->select('*')->from('bean')
    ->where(' field1 = ? ')->put('a')->get('row');


Краткий обзор моделей


Забегу немного во вторую часть и опишу как содается модель в RedBeanPHP.

В начале вы работаете с bean, без привлечения моделей. Когда вам нужна модель, вы создаете класс вида Model_BeanName, например Model_User, и там описываете дополнительную логику. Связывание bean и модели происходит автоматически.

$user = R::dispence('user');
$user->modelMethod();

То есть, начинаем с ActiveRecord, и при необходимости расширяемся до моделей. Автор называет этот подход FUSE (англ. сплавка).

Во второй части мы мы продолжим разговор о функциях фреймворка для ежедневной работы:
  • Модели
  • Транзакции
  • и другие темы

В третей части обсудим дополнительные возможности RedBeanPHP:
  • Отладка
  • Сервер для Javascript приложения.
  • Плагины
  • Практический пример изменения внутренней логики
  • и другие темы


Ресурсы


Официальный сайт RedBeanPHP
Tags:
Hubs:
+30
Comments 47
Comments Comments 47

Articles