ORM RedBeanPHP

Категория: RedBeanPHP

RedBeanPHP это отличное решение для первичного этапа разработки - прототипирования. Также, этот ORM вполне можно использовать на собственных закрытых проектах и не требовательных к обширной бизнес логики проектах. RedBeanPHP поддерживает расширение моделей кастомными методами и предоставляет SQL Builder. Но!

Тот же SQL Builder я нахожу крайне убогим, по мере обрастания уровня BL кастомным функционалом - вся прелесть простого экспорта и "жадной предзагрузки" сходят на нет, валидацию нужно прикручивать самому, менеджеры коллекций тоже отсутствуют (хотя в том же Eloquent от Laravel их тоже нет).

В общем на вкус и цвет..) Но это отличная (если не единственная) ORM подходящая для быстрого прототипирования схемы БД и приложения.

Установка

Автор рекомендует использовать для установки phar-архив, поскольку он содержит основные плагины и прост в обращении.

Внимание!

Автор отказывается предоставлять возможность установить RedBean через Composer начиная с версии 4.0, обсуждение сабжа.

Дабы не возится с алиасами секции use .. as ..в каждом контроллере:

use RedBean_Facade as R;

Используйте ф-цию class_alias:

class_alias('RedBean_Facade', 'R');

SQL Builder

Пример построения запроса с помощью SQL Builder (Mixing SQL and PHP):

// getNew() создает новый инстанс SQL Builder'а
$sub_builder = R::$f->getNew()->begin()
    ->select('count(id)')
    ->from('field')
    ->where('preset_id = preset.id AND is_filterable = ?')->put(true)
    ->limit(1);

// begin() - включает режим захвата для всех вызовов `R::$f->`
R::$f->begin()->select('*');

R::$f->addSQL(',') // нужно явно добавлять запятую :(
      ->open()   // открыть скобку
        ->nest($sub_builder)
      ->close()  // закрыть скобку
    ->addSQL(' as fields_count1')  // .. и вот таким макаром дописывать алиас :(

    // Добавление аналогичного запроса без использования Builder'а
    ->addSQL(',')
      ->open()
        ->addSQL('SELECT count(id) FROM field WHERE preset_id = preset.id')
      ->close()
    ->addSQL(' as fields_count2')
    ->from('preset')
    ->where() // Добавляет ключевое WHERE без условия
    //->where('created_at > ?')->put('2013')
    ->open()
        ->addSQL('created_at > ? OR updated_at > ?')->put('2013')->put('2013')
        ->or('updated_at > ?')->put('2013')
    ->close()
    ->and('name LIKE ?')->put('%ы');

$rows = R::$f->get(); // 'row|col|cell'
Внимание!

Этот SQL Builder не контролирует последовательность добавления выражений! Поэтому вы не можете добавить условие ->where() после того, как применили ->limit().

Для себя я решил вообще не использовать режим SQL Builder'а - проще уже составлять ручками нативный SQL запрос. Последовательность выражений не контролирует, дополнение кода в chain методах теряется, синтаксис больше запутывает и увеличивает объем кода. Однако, тогда возникает проблема экранирования.. Поскольку, mysql_real_escape_string($string) уберут из PHP 5.5, a mysqli_real_escape_string($link, $string) требует $link (ресурс соединения с БД).

Получение данных

Получение записи по ID

$bean = R::load('post', $id); // RedBean_OODBBean
if ($bean->id) { /* объект получен */ }

Методы и условия поиска

$nean = R::findOne('user',' email = ?', ['user@gmail.com']); // RedBean_OODBBean|null
$bean = R::findOrDispense('user','id = ?', [33]); // RedBean_OODBBean
$beans = R::find('user', 'WHERE email LIKE ?', ['%com']);    // RedBean_OODBBean[]
$beans = R::findAll('user', 'WHERE email = ?', ['user@gmail.com']); // RedBean_OODBBean[]
$rows = R::findAndExport($entity, ...); // array

$rows = R::getAll('SELECT id, email FROM user LIMIT ?', [10]); // array
$row = R::getRow('SELECT * FROM user WHERE email LIKE ?', ['%gmail.com']); // array
$rows = R::getAssoc('SELECT * FROM user WHERE email LIKE ?', ['%gmail.com']); // assoc
#Array (
#  [1] => user1@gmail.com
#  [2] => user2@gmail.com
#)

$beans = R::find('user','email = ?', ['user@gmail.com']); // RedBean_OODBBean[]
// SELECT * FROM `user`  WHERE 1 ORDER BY email

$beans = R::findAll('user', 'WHERE email = ?', ['user@gmail.com']); // RedBean_OODBBean[]
// SELECT * FROM `user` WHERE email = 'user@gmail.com'
Сбросить индексы массива

Массив результирующих записей содержит PK ID в качестве ключей. Если вам необходимо сбросить индексацию - есть простой прием:

$beans = array_values($beans);

Условия и сортировка при получении связанных записей:

$beans = $owner->withCondition(' archived_at IS NULL ')->with(' ORDER BY name ASC ')->ownWish;

Получить кастомные поля:

$table_name = R::getCell('SHOW TABLES LIKE ?', [$entity]);

Обновление

$params = array('name' => 'New name');
$bean = R::load($entity, $id);
$bean->import($params);
$id = R::store($bean); // int

Удаление

R::trash($bean); // null

Relations

Many-to-One

Назначить пользователю роль:

$role = R::dispense('role');
$role->name = 'Role 1';
$role->ownUser[] = $user; // user.role_id
R::store($role);


Для получения пользователя по группе также используется префикс own:, но чтобы получить группу пользователя, достаточно просто обратится к свойству group:

$user = current($role->ownUser);
$role = $user->group;

Many-to-Many

Назначить пользователю несколько ролей:

$user = R::load('user', 1);
$role = R::load('role', 2);
$user->sharedRole[] = $role; // создается таблица role_user. Почему не user_role?
R::store($user);
echo $user->countShared('role'); // 1 связанная запись
Примечание

Метод shared*  в отличие от link исключает дублирование связей в одной таблице. То есть, если вы добавили связь user_id = 1role_id = 2 - то при следующем добавлении такой же связи действие будет проигнорировано. Однако, метод shared* не позволяет сохранять дополнительные данные в связующую таблицу.

Связать записи с указанием таблицы и дополнительных данных:

// Все руки не доходят..

Получить связанные записи (указывать связующую таблицу не обязательно):

$roles = $user->sharedRole; // RedBean_OODBBean[]
$roles = $user->via('user_role')->sharedRole;
$roles = $user->via('user_role')->withCondition('field > ?', ['val'])->sharedRole;

# >> 
# SELECT * FROM user WHERE (id IN (1));
# SELECT
#   role.*, role_user.user_id AS linked_by
# FROM  role_user
# INNER JOIN role ON (role.id = role_user.role_id AND role_user.user_id IN (1))
# WHERE created_at > '1384363629'

Получить связанные записи с дополнительным условием фильтрации:

Прочее

Получить связующий bean:

$links = $user->withCondition("user_role.role_id = ?", [2])->ownUserRole;
$link = current($links);
$link->updated_at = date('Y-m-d H:i:s');
R::store($link); // вы можете также сохранять bean связующей таблицы

Конвертирование, импорт, экспорт

Важно!

Если вы сконвертируете bean в массив и добавите массив данных в новое свойство - у вас отвалится export() этого bean'а.

Импорт

Конвертировать массивы в коллекцию bean'ов:

$rows = R::getAll($sql);
$users = R::convertToBeans('user', $rows);

Экспорт

Экспортировать все связанные записи одного bean или для массива бобов:

$filters = ['bean', 'bean2']; // имена таблиц
R::exportAll($bean, false, $filters);
Примечание

Метод exportAll() экспортирует все связанные записи, и лишь потом фильтрует их. При этом фильтр работает только по имени таблицы, не зависимо от промежуточной связи.

Метод экспортирует только найденные бобы в ассоциативный массив:

$row = R::findAndExport($entity, 'id=?', [33]);
if (isset($row['id']) && $row['id']) { ... }

Preload (Lazy load)

Ленивая загрузка и экспорт (конвертирование bean'ов в массив) данных:

$preset = R::load('preset', 3);
R::preload($preset, 'ownProduct|product,ownProduct.ownAttr|attr');
$rows = R::beansToArray([$preset]);
Примечание

Почему-то у меня не работала ленивая загрузка, при указании имени бинов без расширенного описания, как-то: 'product,product.attr'. При этом, наличие FK ключей (индексов) никак не влияет на результат. 

Поиск с ленивой загрузкой и трансформацией в массив:

$beans = R::findAll($entity, $query);
$beans = array_values($beans);
$beans = $this->loadRelatedObjects($entity, $beans, 'ownOptions|options');
$result = R::beansToArray($beans);

Хелперы

Определить имя связующей таблицы:

$table = R::getCell('SHOW TABLES LIKE ?', ['user_role']);
$relation_name = $table ? $table : 'user_table';

Метаданные

R::renameAssociation(); // how to use?
$bean->getMeta('that this?') // how to use?

#orm, #redbean

категория: RedBeanPHP