atv
Новичок
Диагноз - ORM
Как сказал fisher, ORM - это инфекция. Встречайте ещё одного больного , тобиш меня. После того разговора я ещё долго думал на тему ORM, и решил что рано ещё списывать её со счетов. И решил я так потому, что, как мне кажеться, смог привнести нечто новое в её реализацию.
У меня появилась пара идей по реализации, и я решил проверить их на практике. Что из этого получилось, смотрите ниже.
Итак, возможности. Прежде всего стандартные возможности ORM:
- Возможность указывать маппинг или использовать маппинг по умолчанию.
- Отложенная загрузка атрибутов.
Теперь возможности, ради которых затевалась эта работа.
- Как можно более простое и прозрачное использование. Точнее сформулировать пока не могу Подробности будут в примере.
- Отсутствие требования наследовать отображаемый класс от базового класса библиотеки.
- Навигация по свзязанным объектам и коллекциям через свойства оображаемого объекта. Вот такая пространная формулировка Пример всё прояснит.
- Повторное использование существующих отображаемых объектов. Опять же подробности в примере
Саму работу можно взять здесь. А теперь пример использования.
Вот вроде бы и всё, может что-то осталось за кадром, так что задавайте вопросы.
Пожелания, подсказки, идеи и доработки также приветсвуются
Да, забыл написать недостатки.
- Для отображения данных на объект используется декоратор, поэтому проверки вида
не пройдут.
- Некоторые функциональные возможности реализованы в ущерб производительности, в частности связывание коллекций происходит на стороне PHP. Данных о производительности и её снижении пока нет. (я даже пока не представляю что и как замерять :? )
- Может ещё какие есть, пока не помню
Как сказал fisher, ORM - это инфекция. Встречайте ещё одного больного , тобиш меня. После того разговора я ещё долго думал на тему ORM, и решил что рано ещё списывать её со счетов. И решил я так потому, что, как мне кажеться, смог привнести нечто новое в её реализацию.
У меня появилась пара идей по реализации, и я решил проверить их на практике. Что из этого получилось, смотрите ниже.
Итак, возможности. Прежде всего стандартные возможности ORM:
- Возможность указывать маппинг или использовать маппинг по умолчанию.
- Отложенная загрузка атрибутов.
Теперь возможности, ради которых затевалась эта работа.
- Как можно более простое и прозрачное использование. Точнее сформулировать пока не могу Подробности будут в примере.
- Отсутствие требования наследовать отображаемый класс от базового класса библиотеки.
- Навигация по свзязанным объектам и коллекциям через свойства оображаемого объекта. Вот такая пространная формулировка Пример всё прояснит.
- Повторное использование существующих отображаемых объектов. Опять же подробности в примере
Саму работу можно взять здесь. А теперь пример использования.
Код:
Структура БД.
_____________
_____________ | orders |
| sellers | |-------------| _______________
|-------------| | order_id | | customers |
| seller_id |<------| seller_id | |---------------|
| seller_name | | customer_id |------->| customer_id |
|_____________| | order_sum | | customer_name |
|_____________| |_______________|
PHP:
<?php
require_once('Collection.php');
// определяем классы для отображения.
class Seller
{
// указываем отображение свойств класса на поля таблицы.
protected $map = array(
'seller_id' => 'id',
'seller_name' => 'name'
);
}
class Customer
{
protected $map = array(
'customer_id' => 'id',
'customer_name' => 'name'
);
}
class Order
{
protected $map = array(
'order_id' => 'id',
'seller_id' => 'seller_id',
'customer_id' => 'customer_id',
'order_sum' => 'sum'
);
// указываем отображение свойств класса на связи таблиц.
protected $relations = array(
'sellers' => 'seller',
'customers' => 'customer'
);
}
$db = new PDODB();
$db->dsn = 'mysql:host=localhost;dbname=orm';
$db->username = 'root';
$db->password = 'db_pass';
// вся работа ведётся через объекты коллекции.
$sellers = new Collection($db, 'sellers', 'id', 'Seller');
$customers = new Collection($db, 'customers', 'id', 'Customer');
$orders = new Collection($db, 'orders', 'id', 'Order');
// устанавливаем связи между коллекциями
$sellers->setOne2ManyRelation($orders, 'seller_id');
$customers->setOne2ManyRelation($orders, 'customer_id');
// всё, ORM готова к работе.
// чтобы легче было разобраться в коде, я буду приводить для сравнения
// SQL запрос, которому соответсвует указанный код.
// Это не тот SQL запрос, который генерируется ORM.
/*
SELECT * FROM sellers;
*/
// чтобы получить весь список служащих (и тех у которых нет заказов),
// независимо от связи с orders, нужно указать объединение
$sellers->join = Collection::LEFT_JOIN;
foreach ($sellers as $seller) {
print $seller->name;
}
/*
SELECT * FROM sellers JOIN orders USING(seller_id) WHERE order_sum > 50;
*/
// вот тут заключается особенность этой ORM, order_sum относиться к таблице
// orders, значит для неё и используем фильтр.
$orders->setFilter("sum > 50");
// так как выше по коду для $sellers было установлено объединение,
// то теперь его нужно убрать, так как оно уже не нужно.
$sellers->join = null;
// и снова перебираем список
foreach ($sellers as $seller) {
print $seller->name;
}
/*
SELECT * FROM sellers JOIN orders USING(seller_id) JOIN customers USING(customer_id)
WHERE order_sum = 100 AND customer_name = 'Customer 1';
*/
$orders->setFilter("sum = 100");
$customers->setFilter("name = 'Customer 1'");
foreach ($sellers as $seller) {
print $seller->name;
}
/*
SELECT * FROM orders JOIN sellers USING(seller_id) WHERE seller_name = 'Seller 1';
*/
$sellers->setFilter("name = 'Seller 1'");
// вариант первый
foreach ($orders as $order) {
print $order->sum;
}
// вариант второй
$seller = $sellers->getIterator()->current();
foreach ($seller->orders as $order) {
print $order->sum;
}
// это и есть навигация по связанным объектам.
// ещё пример навигации
$order = $orders->getIterator()->current();
print $order->seller->name;
print $order->customer->name;
// примеры создания изменения и удаления.
// создание... начинаем транзакцию
$db->beginTransaction();
$seller = $sellers->createItem();
$seller->id = mt_rand();
$seller->name = 'Seller 1';
$customer = $customers->createItem();
$customer->id = mt_rand();
$customer->name = 'Customer 1';
$order = $orders->createItem();
$order->id = mt_rand();
$order->sum = 75;
$order->seller_id = $seller->id;
$order->customer_id = $customer->id;
$db->commit();
// всё, объекты сохранены и готовы к дальнейшей работе
print $seller->orders->current()->sum;
print $order->seller->name;
print $order->customer->id;
// обратите внимание
$sellers->setFilter("name = 'Seller 1'");
$seller1 = $sellers->getIterator()->current();
// в приложении уже существует объект ссылающийся на данную запись в БД - это
// объект $seller, поэтому $seller1 будет содержать ссылку на объект $seller
// обновление...
$db->beginTransaction();
$seller1->name = 'New Seller Name';
$db->commit();
// обратите внимание что
print $seller->name // New Seller Name
// удаление...
$db->beginTransaction();
$sellers->removeItem($seller1);
$db->commit();
?>
Пожелания, подсказки, идеи и доработки также приветсвуются
Да, забыл написать недостатки.
- Для отображения данных на объект используется декоратор, поэтому проверки вида
PHP:
<?php $object instanceof Seller ?>
- Некоторые функциональные возможности реализованы в ущерб производительности, в частности связывание коллекций происходит на стороне PHP. Данных о производительности и её снижении пока нет. (я даже пока не представляю что и как замерять :? )
- Может ещё какие есть, пока не помню