Первый альфа релиз phpLightOrm

atv

Новичок
Первый альфа релиз phpLightOrm

phpLightOrm - это небольшая, быстрая и мощная ORM библиотека написанная на PHP 5. В ней имеются все основные возможности ORM плюс кеширование объектов, автоматическое управление используемой памяти, а также некоторые другие возможности.

Цель проекта:
Во первых - испытать на практике целесообразность некоторых идей, а именно, кеширования объектов, управления памятью, использования паттерна "Декоратор", в котором помещается функционал по связи объекта с базой данных.

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

В третьих - постараться избавиться от недостатков, которые присутствуют в других ORM проектах.

Я надеюсь, библиотека будет интересна многим разработчикам, жду конструктивных предложений и замечаний, а также, приглашаю разработчиков к совместной работе над проектом.

Ну и, наконец, примеры использования:
PHP:
 set_include_path('external/PHP_Application');

 require_once('Collection.php');
 require_once('db/PDODB.php');

 // Declare classes for OR mapping.
 class Order
 {
     // mapping of table fields on properties of object
     // ('field_name' => 'property_name')
     protected $map = array(
         'order_id'      => 'id',
         'seller_id'     => 'sellerId',
         'customer_id'   => 'customerId',
         'order_sum'     => 'sum'
     );

     // mapping of relations on properties of object
     // ('name _of_related_table' => 'name_of_related_property')
     protected $relations = array(
         'sellers'       => 'seller',
         'customers'     => 'customer'
     );

     // lazy load properites
     protected $lazyProperties = array(
         'sum'
     );

     // event handlers
     protected $handlers = array(
         'beforeSave'    => 'saveHandler',
         'beforeRemove'  => 'removeHandler'
     );

     // handler for "beforeSave" event
     public function saveHandler($collection)
     {
         // some code
     }

     // handler for "beforeRemove" event
     public function removeHandler($collection)
     {
         // some code
     }
 }

 class Seller {[...]}
 class Customer {[...]}
 class Address {[...]}

 // initialization of database connection
 $db = new PDODB; // or many other DB driver
 $db->dsn = DB_DSN;
 $db->username = DB_USERNAME;
 $db->password = DB_PASSWORD;
 $db->connect();

 // initialization of ORM collections
 $sellers = new Collection($db,
     'sellers',    // name fo table
     'Seller'      // name of model class
 );
 $customers = new Collection($db, 'customers', 'Customer');
 $orders = new Collection($db, 'orders', 'Order');
 $address = new Collection($db, 'address', 'Address');

 // initialization of collection relations
 $sellers->setOne2ManyRelation(
     $orders,      // depend collection
     'id',         // unique key
     'sellerId'    // foreign key
 );
 $customers->setOne2ManyRelation($orders, 'id', 'customerId');
 $customers->setOne2OneRelation($address, 'id', 'customerId');

 // examples of selection

 // by filter on one collection
 foreach ($sellers->filter('name = ?', 'Seller 1')->order('name') as $seller) {
     print $seller->name;
 }

 // by filter on many related collections
 // this is similar to
 // SELECT sellers.* FROM sellers
 // JOIN orders ON sellers.seller_id = orders.seller_id
 // WHERE seller_name LIKE 'Seller' AND order_sum > 20
 foreach ($selers->filter('name like ?', 'Seller')->using($orders, 'sum > ?', 20) as $seller) {
     print $seller->name;
 }

 // repeated use of the adjusted iterator
 // if the size of the buffer is more than quantity of lines returned by query,
 // that, at a reuse, objects will be are taken from cache.
 // this is similar to
 // SELECT orders.* FROM orders
 // JOIN sellers ON sellers.seller_id = orders.order_id
 // JOIN customers ON customers.customer_id = orders.customer_id
 // WHERE seller_name = 'Seller 2' AND customer_name = 'Customer 3'
 $iterator = $orders
     ->using($sellers, 'name = ?', 'Seller 2')
     ->using($customers, 'name = ?', 'Customer 3')
     ->fetch();

 foreach {$iterator as $order} {
     print $order->sum;
 }

 // use of relations between objects
 $seller1 = $sellers->filter('name = ?', 'Seller 1')->firstItem();
 // we receive all orders of the specified seller
 // in this case buffering of orders too works
 foreach ($seller1->orders as $order) {
     print $order->sum;
     // we receive the client of the specified order
     print $order->customer->name;
 }

 // use of bulk loading
 // all related objects will be loaded by three query (by quantity of used collections)
 foreach ($sellers->bulkLoad($orders, $customers) as $seller) {
     // the $order objects will be received from cache
     foreach ($seller->orders as $order) {
         // the $customer objects will be receaved from cache
         print $order->customer->name;
     }
 }

 // use of limited of quantity of the loaded objects
 foreach ($sellers->fetch()->limit(2, 1) as $seller) {
     print $seller->name;
 }

 // compulsory preservation of objects in cache
 foreach ($sellers->filter('name like ?', 'Seller')->fetch()->persist() as $seller) {
     print $seller->name;
 }
 // at the following iteration objects will undertake from cache
 foreach ($sellers as $seller) {
     print $seller->name;
 }

 // example of addition of objects
 try {
     $db->beginTransaction();

     $seller = $sellers->createItem();
     $seller->name = 'Seller 1';

     $customer = $customers->createItem();
     $customer->name = 'Customer 1';

     $order = $orders->createItem();
     $order->sum = 50;

     // we establish the relation between objects
     $order->seller = $seller;
     $order->customer = $customer;

     $db->commit();
 } catch (Exception $e) {
     $db->rollback();
     throw $e;
 }

 // example of removal of objects
 $seller = $sellers->filter('name = ?', 'Seller 1')->firstItem();

 try {
     $db->beginTransaction();

     $sellers->removeItem($seller);

     $db->commit();
 } catch (Exception $e) {
     $db->rollback();
     throw $e;
 }

 // removal by condition
 $sellers->removeBy('name = ? OR name = ?', 'Seller 1', 'Seller 3');
 

magic

lancer
Автор оригинала: atv
А что смущает, приставка php, или само название?
Приставка php и отношение проекта к PHP лицензии.
3. The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact [email protected].
http://www.php.net/license/3_01.txt
 

Alexandre

PHPПенсионер
сорри за оффтоп:
интересно, а phpMyAdmin & phpDocumentator - согласовывали приставку php в названии своих проектов?

что до проекта, екачнул, разбираюсь. интересно.
 

atv

Новичок
Приставка php и отношение проекта к PHP лицензии.
Это опен сорс библиотека. Почему она не может распространятся под PHP лицензией? А приставка означает что библиотека выполнена на PHP. Насколько я знаю, очень многие проекты используют название ЯП в названии проекта. Хотя я не силён в этом вопросе.
 

Alexandre

PHPПенсионер
atv что тебе мешает спросить у [email protected] ?
magic прав, лицензию надо соблюдать. php - это зарегистрированная марка, и данное словосочетание можно употреблять с разрешения владельца (в данном случае PHP Group). Думаю, что пхп-доки а админы это дело утрясли, по этому - разрешат и тебе.
 

tony2001

TeaM PHPClub
The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission.
"derived" - означает "на базе исходников самого PHP", а не "написанный с применением языка PHP".

Другое дело, что это в принципе плохой тон - использовать название одного продукта в названии другого.
Чисто внешне выглядит, что продукт xxxYyy имеет какое-то отношение к продукту xxx, что естественно не так.
 

magic

lancer
PHP group в FAQ почти дословно пишет, что "нам жаль, что некоторые проекты используют слово PHP, пытаясь раскрутиться за счет труда многих людей" и упоминают там нехорошими словами PHP-Nuke. В третьей версии своей лицензии они явно указали - никаких PHP в названии без письменного разрешения.

Автор проекта попросил указать на недостатки. Вот я и указываю, а то несерьезно получается :)
 

zerkms

TDD infected
Команда форума
умеет ли сабж подтягивать объекты, относящиеся к основному как 1:1 в одном запросе?

PHP:
// тут получили $order
echo $order->seller->name, $order->customer->name;
сколько запросов будет выполнено?
 

Роберт

Аналитик
И всётаки действительно интересно: форум phpBB и phpMyAdmin - нарушают правила?
С другой стороны - нарушение таких правил может ли повлечь за собой какие-то юридические последствия?
А что если я начал использовать это имя продукта (phpLightOrm) ещё до введения этих правил? Ведь они появились не с первых лет существования PHP , изначально автор языка программирования наоборот был безумно счастлив тому что кто-то использует его продукт и вносит его в название своих проектов.
 

WP

^_^
> кеширования объектов, управления памятью,
И где кеширование? Не увидел. Хотел погонять на скорость со смартдб.
 

atv

Новичок
Хороший вопрос :)

Проблема в том, как измерять? Я пока знаю один способ, сравнение с существующими аналогами, так называемый benchmark.

Результаты benchmark'а я ещё не подготовил к публикации. Сейчас могу только сказать, что данная библиотека оказалась быстрее Doctrine где-то на 30%. Propel обогнал данную ORM по селектам где-то на 50%, по остальным операциям отстал тоже где-то на 30%.

Что касается Propel, то для меня результат по селектам вполне ожидаем, так как Propel, всё-таки, генерируемая ORM.

умеет ли сабж подтягивать объекты, относящиеся к основному как 1:1 в одном запросе? $order->seller->name
Где ж тут один к одному, здесь многие к одному. Это значит, что если вытягивать одним запросом несколько заказов одного продавца, то продавец будет продублирован по количеству заказов.

По такому принципу Doctrine вытягивает связанные объекты. Потом, при создании связанных объектов, приходиться разгребать дублирующиеся записи. При бОльшем объёме выборки, больше данных передаётся в скрипт, больше памяти, больше разгребать. Т.е. объём выборки продавцов напрямую зависит от объёма выборки заказов.

Я решил попробовать по другому. Связанные объекты вытягиваются отдельным запросом. В этом случае, количество запросов равняется количеству вытягиваемых сущностей. Т.е., чтобы вытянуть заказы и продавцов, будет выполнено два запроса, независимо от объёма выборки, причём объём выборки продавцов не будет напрямую зависеть от объёма выборки заказов.

Во втором случае зависимость времени на обработку результата запроса от объёма данных более пологая, но выше начинается чем в первом случае. В каком-то месте они пересекутся, и дальше второй вариант будет выигрывать. В каком именно месте они пересекутся, я ещё не тестировал.

Отвечая на вопрос: seller и customer, в приведенном примере, будут вытянуты отдельными запросами, даже в случае использования bulkLoad, т.е., в сумме с заказом, всего три запроса. Почему сделано именно так, я объяснил выше.
 
Сверху