LightOrm и relation один к одному

john.brown

просто кулибин
LightOrm и relation один к одному

Есть таблички:

items
----------
item_id | cat_id

item_translate
-------------------
tr_id | item_id | lng | iname

В первой хранятся разные единицы, во второй их названия на разных языках.
Ну и классы, соответственно:
Item.php
PHP:
class model_Item {
	protected $map = array(
		'item_id' => 'id',
		'cat_id' => 'catId'
	);
	protected $relations = array(
		'item_translate' => 'translate'
	);
	protected $lazyProperties = array();
	
	protected $handlers = array();
}
ItemTranslate.php
PHP:
class model_ItemTranslate {
	protected $map = array(
		'tr_id' => 'id',
		'item_id' => 'itemId',
		'lng' => 'lng',
		'iname' => 'iname'
	);
	protected $relations = array();
	
	protected $lazyProperties = array();
	
	protected $handlers = array();
}
Ну вот, из всего этого хотелось бы получить итем и его название на требуемом языке. На подобие этого:
[SQL]
SELECT t1.*, t2.iname FROM items t1, item_translate t2 WHERE t1.cat_id=$catId AND t2.item_id=t1.item_id AND t2.lng=$lng
[/SQL]
Пытаюсь это сделать следующим образом:
PHP:
$items = new Collection($db, 'items', 'model_Item');
$translates = new Collection($db, 'item_translate', 'model_ItemTranslate');

$items->setOne2OneRelation($translates, 'id','itemId');
foreach($items->filter('catId = ?', $catId)->using($translates, 'lng = ?', $lng) as $item) {
        echo $item->translate->iname;
}
Увы, результат совсем не тот - он выбирает все итемы у которых есть название на нужном языке, но загружает перевод с младшим tr_id... А хотелось бы на нужном языке....

Что я не так делаю?
 

atv

Новичок
На первый взгляд всё правильно. Поэтому мне нужно время, чтобы самому попробовать этот конкретный пример.

-~{}~ 31.07.08 23:20:

Честно говоря, для меня это неожиданный аспект использования. Я имею ввиду использование связи один-к-одному в этом примере. Я полагал при разработке, что в случае использования связи один-к-одному, в таблице реально будет одна дочерняя запись на одну родительскую.

Я не знаю, насколько правомерным является такое использование связи один-к-одному как в примере, и насколько оно реализуемо в принятом мной подходе. Это потребует более глубокой оценки.

Сейчас, пока что, могу предложить такой вариант:

PHP:
$items = new Collection($db, 'items', 'model_Item');
$translates = new Collection($db, 'item_translate', 'model_ItemTranslate');
// т.е. вместо связи один-к-одному использовать один-ко-многим
$items->setOne2ManyRelation($translates, 'id', 'itemId');

foreach($items->filter('catId = ?', 1) as $item) {
        echo $item->translate->filter('lng = ?', 'ru')->current()->iname;
}
получается не так лаконично, но с ожидаемым результатом

А над вариантом, предложенным в примере я ещё подумаю.
 

john.brown

просто кулибин
Спасибо, именно то, что надо было :)

Ну да, согласен, произвольное манипулирование характером связи, наверно, не есть гут. Но было бы удобно, и, имхо, интуитивно понятно... :)

--------
Хмм, да, все же не совсем то. Он вынимает из базы и те итемы, у которых нету перевода на запрошенном языке. Чего не очень хотелось бы... Вот как то так бы если... :)
PHP:
$items->filter('catId = ?',$catId)->usingOne2One($translations, 'lng = ?',$lng)
 

atv

Новичок
Хмм, да, все же не совсем то.
Да я ошибся. Рабочий вариант будет ещё избыточнее
PHP:
foreach($items->filter('catId = ?', 1)->using($translates, 'lng = ?', 'ru') as $item) {
        echo $item->translate->filter('lng = ?', 'ru')->current()->iname;
}
Т.е. дважды указать фильтр. Сейчас долго объяснять, почему так, тем более, что я вроде нашёл возможность реализовать более удобный и лаконичный вариант, наподобие того, что был в первоначальном примере, да ещё и с bulkLoad. Думаю, сегодня-завтра реализую и оттестирую.
 

john.brown

просто кулибин
Тут еще вопрос возник. А как из нутри объекта добраться до связанных объектов? Т. е. на подобии этого:
PHP:
class model_Item {
        ....
        
        function render($lng) {
                echo $this->translate->filter('lng = ?',$lng)->current()->iname;
        }
}
 

atv

Новичок
А как из нутри объекта добраться до связанных объектов?
Так как весь функционал по работе с БД помещён в декораторе, то добраться до связанных объектов можно через него. В объекте модели для декоратора зарезервировано свойство, которое называется __shell. Например
PHP:
class model_Item {
        ....
        
        function render($lng) {
                echo $this->__shell->translate->filter('lng = ?',$lng)->current()->iname;
        }
}
К сожалению, так и не дошли руки до описания всех этих моментов в документации.

Скоро планирую залить в SVN последнюю версию.

-~{}~ 01.08.08 16:13:

Залил в SVN последнюю версию.
Теперь можно сделать следующее
PHP:
$items = new Collection($db, 'items', 'model_Item');
$translates = new Collection($db, 'item_translate', 'model_ItemTranslate');
// т.е. используется связь один-к-одному
$items->setOne2OneRelation($translates, 'id','itemId');

// в этом случае каждый translate будет загружаться отдельным запросом
foreach($items->filter('catId = ?', 1)->using($translates, 'lng = ?', 'ru') as $item) {
    echo $item->translate->iname;
}

// в этом случае все связанные и отфильтрованные translate загружаются отдельным запросом
foreach($items->filter('catId = ?', 1)->bulkLoad($translates, 'lng = ?', 'ru') as $item) {
    echo $item->translate->iname;
}
 

john.brown

просто кулибин
Спасибо, круто :D
Да, более полная дока с туториалом каким не то, имхо, пошла бы на пользу... :)
 

MiksIr

miksir@home:~$
Все ж не могу понять, где тут нашли один к одному связь?
итем имеет много названий... один ко многим
 

john.brown

просто кулибин
формально да. но при определенном условии (на конкретном языке) она один к одному. И удобно работать, когда можно ею манипулировать - при выводе списка итемов на каком то языке пользовать один к одному, а при редактировании итема на всех языкак - один ко многим :)
 

MiksIr

miksir@home:~$
ну из любого один ко многим путем соответствующих where можно получить один к одному ;)
В идеале должен быть задан тип один ко многим и два критерия - на левую и правую часть, а после этого - уже запрос.
Я просто конкретно с LightOrm не разбирался еще, хотя у нас были кое-какие идеи насчет нее... возможно там архитектура мешает что-то такое делать...
 

atv

Новичок
В идеале должен быть задан тип один ко многим и два критерия - на левую и правую часть, а после этого - уже запрос.
Так изначально и задумывалось, так и работает. Весь прикол в том, что при указании типа связи один-к-одному $item->translate возвращает объект, а один-ко-многим - итератор по коллекции, и тогда нужно будет писать $item->translate->current()->iname, вместо $item->translate->iname.

-~{}~ 01.08.08 18:00:

Хочу ещё добавить, что LightOrm запрашивает мета информацию из БД, для контроля ключей, связей и т.д. Так вот недавно появилась возможность кешировать эту метаинформацию (на уровне драйвера БД). Актуально при большом количестве таблиц в проекте, если интересует, расскажу подробнее.
 

john.brown

просто кулибин
Да, конечно интерестно.

-~{}~ 02.08.08 15:31:

Вот еще и по работе со связю многие ко многим какой не то пример бы не помешал. Как то шас заморачиваюсь, и что то не оч очевидно :(

На примере хотя бы классического авторы-книги...
 

atv

Новичок
Прошу прощения за задержку с ответом.

По поводу кеширования. Из проекта PHP_Application добавилась ещё одна директория, в которой есть класс FileRegistry - обычный Registry, только его содержимое сериализуется в файл.
PHP:
require_once('misc/FileRegistry.php');

FileRegistry::setFilePath(dirname(__FILE__).'/DBMetaInfo.txt');

$db = new PDODB;
$db->dsn = DB_DSN;
$db->username = DB_USERNAME;
$db->password = DB_PASSWORD;
// подсовываем в драйвер ссылку на Registry
$db->registry = FileRegistry::instance();
$db->init();
По поводу многие-ко-многим. Такая связь пока не реализована, поэтому и примера на неё нет. Мне казалось, что я где-то указывал об этом в проекте, хотя может и ошибаюсь.
 

john.brown

просто кулибин
Жаль, что многие к многим не реализовано :( Это сильно сужает область применения. Но сам то както выходиш из ситуации, Или в ручную пишеш работу с бд в таком варианте?
 

atv

Новичок
Или в ручную пишеш работу с бд в таком варианте?
Ну, собственно, многие-ко-многим это две связи один-ко-многим.

Но сам то както выходиш из ситуации
Да вот, наполовину написал поддержку связи многие-ко-многим, но всё нет времени закончить.

-~{}~ 15.08.08 15:02:

Наконец нашёл время чтобы закончить реализацию связи многие-ко-многим. Последняя версия в SVN.

Кроме этого, убрал неявные зависимости между последовательными выборками из коллекции с использованием фильтров (применяемые фильтры неявно сказывались на последующих выборках), что затрудняло получение желаемого результата.

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

Пример настройки связи многие ко многим:
PHP:
$sellers = new Collection($db, 'sellers', 'Seller');
$groups = new Collection($db, 'groups', 'Group');
$sellerGroups = new Collection($db, 'seller_groups', 'SellerGroup');

// т.е. объявляем две связи один-ко-многим
$sellers->setOne2ManyRelation($sellerGroups, 'seller_id', 'seller_id');
$groups->setOne2ManyRelation($sellerGroups, 'group_id', 'group_id');
// и затем уже связь многие-ко-многим с указанием
// связующей коллекции
$sellers->setMany2ManyRelation($groups, $sellerGroups);
 

john.brown

просто кулибин
atv, ох спасибо, обрадовал :) А то уже думал, что придется все же более тяжеловесный какой не то пропел пользовать.

-~{}~ 18.08.08 21:12:

А ты бы не мог набросать еще и классы как выглядят в таком варианте? А то я, наверно, тупой, но как то не получается. Ругается на неизвестный ключ таблицы зависимостей... Ша у меня так оно выглядит:
PHP:
class model_Author {
	protected $map = array(
		'auth_id' => 'auth_id',
		'fname' => 'fname',
		'lname' => 'lname'
	);
	protected $relations = array(
		'authors_books' => 'authors_books'
	);
}
class model_Book {
	protected $map = array(
		'book_id' => 'book_id',
		'title' => 'title'
	);
	protected $relations = array(
		'authors_books' => 'authors_books'
	);
}
class model_AuthorBook {
	protected $map = array(
		'id' => 'id',
		'auth_id' => 'auth_id',
		'book_id' => 'book_id'
	);
	protected $relations = array(
		'authors' => 'authors',
		'books' => 'books'
	);
}
 

atv

Новичок
Прошу прощения за задержку с ответом, отсутствовал в пределах цивилизации.

Как выглядят классы можно увидеть в тестах. В relations указывается отображение связей БД на свойства объекта. Вот и добавь в relations для автора и книги отображение связи многие-ко-многим друг с другом. Т.е. укажи свойство объекта, через которое будет доступна эта связь.

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

john.brown

просто кулибин
Да, уже выудил из тестов классы.

В пропеле нету явной поддержки многие-ко-многим, но есть простой и понятный мануал, как ее реализовать.
 
Сверху