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

в 10:55, , рубрики: orm, php, RedBeanPHP, Веб-разработка, Песочница, метки: , ,

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

Перед написанием поста я потрудился и сделал тестовое приложение для того, что бы убедиться на своем опыте в возможности невероятно облегчить работу. Ведь я наверно не один, кто прописывает поля в нескольких местах с жутким осознанием бессмысленности этой работы, особенно на начальном этапе разработки. Наконец появился аналог 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. Данную тему я затрону детально во 3ей части обзора.
Фреймворк поддерживает 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

Пример создания новой записи:

// Создаем объект (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 ); 

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

  • Модели
  • Транзакции
  • и другие темы

В третей части обсудим дополнительные возможности RedBeanPHP:

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

Ресурсы

Официальный сайт RedBeanPHP

Автор: olegl84


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js