Вопрос по ORM

Господа, я запутался, прошу разъясните.
Пользуюсь последней версией ЛС, ставлю все обновления с трака, хочется пользоваться всеми предоставляемыми возможностями движка.

Имеется три таблицы в базе данных:
  • таблица команд (Team), содержит Id команды, её название, лого и так далее
  • таблица пользователей, собственно таблица как у всех
  • таблица турнира (Teamsintournament), содержит id турнира, id команды и id пользователя

Задача вывести страницу участников турнира (Название команды с лого, Имя игрока ссылкой + аська + ещё набор полей из таблицы пользователя)

Как бы я сделал в обычном проекте, один запрос, с left join, так как у команды может и отсутствовать игрок и передал бы массив в шаблон,

Но ведь хочется по умному, но я не представляю как это сделать грамотно, есть вариант
$oTeamsInTournament = LS::E()->PluginVs_Stat_GetTeamsintournamentItemsByTournamentId($oBlog->GetTournamentId());
--получаем все записи команд в турнире
$aUserId=array();
$aTeamId=array();		
foreach ($oTeamsInTournament as $oTeamInTournament) {
 if($oTeamInTournament->getPlayerId()!=0)$aUserId[]=$oTeamInTournament->getPlayerId();	
 $aTeamId[]=$oTeamInTournament->getTeamId();						
}
--создаем и заполняем массивы содержащие id пользователей и команд
$aUsers= $this->User_GetUsersAdditionalData($aUserId);
$aTeams = LS::E()->PluginVs_Stat_GetTeamItemsByTeamIdIn($aTeamId);
--получаем все команды, кстати я не совсем понял зачем убрали ByArray, теперь к объектам не обратиться через $aTeams[$TeamId], теперь $aTeams[просто номер объекта]
foreach ($oTeamsInTournament as $oTeamInTournament) {
 if($oTeamInTournament->getPlayerId()!=0)$aTeams[$oTeamInTournament->getTeamId()]->setUser($aUsers[$oTeamInTournament->getPlayerId()]); 
}
--в один объект записываем через setUser другой объект
$this->Viewer_Assign('aTeams',$aTeams);
-- и передаем в шаблон
 

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

Наверняка я где-то не дочитал, может просто не нашел, помогите.

53 комментария

avatar
да, своеобразная реализация :)
сейчас подумаю, как лучше поступить в этой ситуации.
avatar
на самом деле в данной ситуации нужно просто добавить к сущности Teamsintournament
protected $aRelations=array(		
	'user' => array(self::RELATION_TYPE_BELONGS_TO, 'ModuleUser_EntityUser', 'user_id'),
	'team' => array(self::RELATION_TYPE_BELONGS_TO, 'PluginVs_ModuleStat_EntityTeam', 'team_id')
);


и вызывать
$aTeamInTournament = LS::E()->GetTeamsintournamentItemsByFilter(array(
	'tournament_id' => $oBlog->GetTournamentId(),
	'#with'		=> array('user', 'team')
));
foreach($aTeamInTournament as $oTeamInTournament) {
	echo "{$oTeamInTournament->getUser()->getLogin()} / {$oTeamInTournament->getTeam()->getLogo()}";
}
avatar
поправка к первой строчке:
$aTeamsInTournament = LS::E()->PluginVs_Stat_GetTeamsintournamentItemsByFilter(array(
avatar
суть понял, за это огромное спасибо, а вот с реализацией возникли странные проблемы (даже с 1069)
'Undefined method module: getUserItemsByArrayId'
avatar
Тут проблема в том, что стандартный модуль ModuleUser не является ORM сушностью.

Чтобы это исправить, измените файл /classes/modules/user/User.class.php, строку #22:
class ModuleUser extends Module {
замените на
class ModuleUser extends ModuleORM {
а также после строки #40 добавьте код:
parent::Init();

а также в файле /classes/modules/user/entity/User.entity.class.php нужно заменить строку #18:
class ModuleUser_EntityUser extends Entity {
на
class ModuleUser_EntityUser extends EntityORM {
avatar
все проделал
Undefined method module: getUserItemsByArrayUserId
avatar
Странно, после этого должно было заработать, User точно наследует класс ModuleORM?
В каком файле и строчке показывает ошибку?
avatar
почитал внимательнее ошибку, её вызывал другой плагин, sitemap, после его отключения ошибка пропала, НО
foreach($aTeamInTournament as $oTeamInTournament) {
        echo "{$oTeamInTournament->getUser()->getUserLogin()} ";
}

Call to a member function getUserLogin() on a non-object
avatar
$aTeamInTournament = LS::E()->PluginVs_Stat_GetTeamsintournamentItemsByFilter(array(
        'tournament_id' => $oBlog->GetTournamentId(),
        '#with'         => array('user')
));

foreach($aTeamInTournament as $oTeamInTournament) {
        echo "{$oTeamInTournament->getUser()->getUserLogin()} ";
}

и
class PluginVs_ModuleStat_EntityTeamsintournament extends EntityORM {
protected $aRelations=array(            
        'user' => array(self::RELATION_TYPE_BELONGS_TO, 'ModuleUser_EntityUser', 'user_id'),
        'team' => array(self::RELATION_TYPE_BELONGS_TO, 'PluginVs_ModuleStat_EntityTeam', 'team_id')
);

}

на мой взгляд все правильно
avatar
В плагине sitemap переопределяется модуль User, соответственно там тоже необходимо переделать на использование ORM
т.е. в plugins/sitemap/classes/modules/user/User.class.php
заменяем
class PluginSitemap_ModuleUser extends Module {

на
class PluginSitemap_ModuleUser extends ModuleORM {

и после
public function Init() {

добавляем
parent::Init();
avatar
без этого какая проблема возникает?
avatar
Undefined method module: getUserItemsByArrayUserId
avatar
Fatal error: Uncaught exception 'Exception' with message 'Undefined method module: getUserItemsByArrayUserId' in /home/vs/www/site1/public_html/engine/classes/Engine.class.php:526
Stack trace:
#0 /home/vs/www/site1/public_html/engine/classes/Engine.class.php(458): Engine->GetModule('getUserItemsByA...')
#1 /home/vs/www/site1/public_html/engine/classes/Module.class.php(49): Engine->_CallModule('getUserItemsByA...', Array)
#2 [internal function]: Module->__call('getUserItemsByA...', Array)
#3 [internal function]: PluginSitemap_ModuleUser->getUserItemsByArrayUserId(Array)
#4 /home/vs/www/site1/public_html/engine/classes/Engine.class.php(481): call_user_func_array(Array, Array)
#5 /home/vs/www/site1/public_html/engine/classes/ModuleORM.class.php(296): Engine->_CallModule('PluginSitemap_U...', Array)
#6 /home/vs/www/site1/public_html/engine/classes/ModuleORM.class.php(439): ModuleORM->GetItemsByFilter(Array, 'Teamsintourname...')
#7 [internal function]: ModuleORM->__call('GetTeamsintourn...', Array)
#8 [internal function]: Plu in /home/vs/www/site1/public_html/engine/classes/Engine.class.php on line 526
avatar
вообще должен вызываться метод getUserItemsByArrayId, а не getUserItemsByArrayUserId, если мы получаем список юзеров по примари
avatar
значит вчерашние обновления затронули это (используемая версия Changeset [1071] by ajaxy )
avatar
эксперимента ради
вернул /classes/modules/user/User.class.php и /classes/modules/user/entity/User.entity.class.php и sitemap плагин в изначальное состояние, без ORM
стал падать правильно
Fatal error: Uncaught exception 'Exception' with message 'Undefined method module: getUserItemsByArrayId'
avatar
метод getUserItemsByArrayId есть только в транковой версии, в 0.4.2 его нет
avatar
так у меня транковая, вплоть до Changeset [1071]
avatar
проблема в последней ревизии не решилась?
без плагина sitemap работает нормально?
avatar
версия 1072
1.Вернул класс User и его сущность в исходное состояние без ORM и Parent Init
2.Вернул класс плагина sitemap в исходное состояние
Undefined method module: getUserItemsByArrayId


1.Вернул только sitemap в исходное состояние, а класс user оставил с ORM
Undefined method module: getUserItemsByArrayUserId

1.Плагин sitemap выключен, класс user вернул в исходный вид
Все работает
avatar
предлагаю перейти в личку, а потом здесь поделиться результатом
опиши все начальные условия, т.к. я не пойму то ли здесь есть плагин, который использует ORM, то ли нет. Весь порядок действий приводящий к ошибке.
avatar
$aTeamInTournament = LS::E()->PluginVs_Stat_GetTeamsintournamentItemsByFilter(array(
        'tournament_id' => $oBlog->GetTournamentId(),
        '#with'         => array('team')
));


и
protected $aRelations=array(            
        'team' => array(self::RELATION_TYPE_BELONGS_TO, 'PluginVs_ModuleStat_EntityTeam', 'team_id')
);

приводят к
Fatal error: Uncaught exception 'Exception' with message 'Undefined method module: PluginVs_Stat_getTeamItemsByArrayTeam_id' in /usr/home/vs/www/site1/public_html/engine/classes/Engine.class.php:526
Stack trace:
#0 /usr/home/vs/www/site1/public_html/engine/classes/Engine.class.php(458): Engine->GetModule('PluginVs_Stat_g...')
#1 /usr/home/vs/www/site1/public_html/engine/classes/ModuleORM.class.php(298): Engine->_CallModule('PluginVs_Stat_g...', Array)
#2 /usr/home/vs/www/site1/public_html/engine/classes/ModuleORM.class.php(440): ModuleORM->GetItemsByFilter(Array, 'Teamsintourname...')
#3 [internal function]: ModuleORM->__call('GetTeamsintourn...', Array)
#4 [internal function]: PluginVs_ModuleStat->GetTeamsintournamentItemsByFilter(Array)
#5 /usr/home/vs/www/site1/public_html/engine/classes/Engine.class.php(481): call_user_func_array(Array, Array)
#6 /usr/home/vs/www/site1/public_html/engine/classes/Engine.class.php(560): Engine->_CallModule('PluginVs_Stat_G...', Array)
#7 /usr/home/vs/www/site1/public_html/plugins/vs/cla in /usr/home/vs/www/site1/public_html/engine/classes/Engine.class.php on line 526
avatar
заметил ещё одну странность, независом от того какое указано поле
protected $aRelations=array(            
        'team' => array(self::RELATION_TYPE_BELONGS_TO, 'PluginVs_ModuleStat_EntityTeam', '<b>bla-bla-bla</b>')
);

всеравно продолжает ругаться на
'Undefined method module: PluginVs_Stat_getTeamItemsByArrayTeam_id'
avatar
попробуйте в /engine/ModuleORM.class.php на строке 296 заменить вызов функции ucfirst на func_camelize:
$sPrimaryKey=func_camelize($sPrimaryKey);					
avatar
'#with'         => array('team')
заработало
'#with'         => array('user')
все таже ошибкаUndefined method module: getUserItemsByArrayId
avatar
рано обрадовался, с team ошибка исчезла, НО
foreach($aTeamInTournament as $oTeamInTournament) {
        echo " {$oTeamInTournament->getTeam()->getName()}";
}

приводит к
Call to a member function getName() on a non-object 

я представлял что через данную конструкцию смогу обратиться к полям другой таблицы
avatar
посмотрите, что содержится в getTeam() для каждого элемента в массиве $aTeamInTournament, возможно у вас где-то не догружаются данные из связанной таблицы
avatar
foreach($aTeamInTournament as $oTeamInTournament) {
        echo "{$oTeamInTournament->getTeam()} ";
}

возвращает (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30)т.е. те 30 команд что участвую в турнире (они же первые 30 заведенные, проверил, все присутствую в таблице команд с такими team_id)
avatar
тоже очень странно, попробуйте
echo "{$oTeamInTournament->getTeam()}
заменить на
var_dump($oTeamInTournament->getTeam());

только что проверил аналогичный пример, — у меня работает.
avatar
string(1) "1"
string(1) "2"
string(1) "3"
string(1) "4"
string(1) "5"
string(1) "6"
string(1) "7"
string(1) "8"
string(1) "9"
string(2) "10"
string(2) "11"
string(2) "12"
string(2) "13"
string(2) "14"
string(2) "15"
string(2) "16"
string(2) "17"
string(2) "18"
string(2) "19"
string(2) "20"
string(2) "21"
string(2) "22"
string(2) "23"
string(2) "24"
string(2) "25"
string(2) "26"
string(2) "27"
string(2) "28"
string(2) "29"
string(2) "30"
avatar
[2011-03-27 21:35:29][38061][DEBUG][SELECT * FROM tis_stat_teamsintournament WHERE 1=1  and tournament_id  =  '5'    ]
[2011-03-27 21:35:29][38061][DEBUG][  -- 0 ms; returned 30 row(s)]
[2011-03-27 21:35:29][38061][DEBUG][SHOW COLUMNS FROM tis_user]
[2011-03-27 21:35:29][38061][DEBUG][  -- 8 ms; returned 31 row(s)]
[2011-03-27 21:35:29][38061][DEBUG][SELECT * FROM tis_user WHERE 1=1  and user_id in ( NULL )  and 0  =  '#index-from-primary'    ]
[2011-03-27 21:35:29][38061][DEBUG][  -- 0 ms; returned 0 row(s)]
[2011-03-27 21:35:29][38061][DEBUG][SHOW COLUMNS FROM tis_stat_team]
[2011-03-27 21:35:29][38061][DEBUG][  -- 2 ms; returned 7 row(s)]
[2011-03-27 21:35:29][38061][DEBUG][SELECT * FROM tis_stat_team WHERE 1=1  and team_id in ( NULL )  and 0  =  '#index-from-primary'    ]
[2011-03-27 21:35:29][38061][DEBUG][  -- 0 ms; returned 0 row(s)]

посмотрел лог запросов, удивлен
avatar
все починили
avatar
у вас две ошибки было:
1) в aRelations третьим параметром для 'belongs_to' указывается Foreign Key таблицы, я вам написал `user_id` и `team_id`, а в таблице у вас было `user` и `team`.
2) ошибка заключается в том, что имена релейшенов ('user' и 'team') не могут совпадать с именами настоящих полей в базе (`user` и `team`).

В связи с этим я переименовал релейшены в my_user и my_team, а третий параметр — 'user' и 'team'. После этого ->getMyUser()->getLogin() и ->getMyTeam()->getLogo() заработали, но я вам советую оставить все как было, просто изменив в таблице ключи relation key на user_id и team_id.
avatar
да, спасибо вам, я несколько неправильно понял приведенный вами пример.
Тогда получается что в релейшене нельзя указать с каким полем устанавливать связь, т.е. только с primary, правильно?
avatar
брр, т.е.
'my_user' => array(self::RELATION_TYPE_BELONGS_TO, 'ModuleUser_EntityUser', 'поле моей таблицы'),
а допустим указать что я хочу связать не по айди, а по логину, который тоже уникален я не смогу?
и второй вопрос, могу ли я сделать два релейшена с одной таблицей, ну к примеру за одну команду два игрока?
avatar
Да, все верно. релейшены можно и нужно делать только по primary key.

За одну команду может играть два игрока, но тогда вам нужно сделать другой тип отношений — HAS_MANY вместо BELONGS_TO, и тогда foreign key вы указываете уже не в таблице команды, а в таблице пользователя. например:

prefix_team: id | name | logo
class PluginVs_ModuleStat_EntityTeam extends EntityORM
{
	protected $aRelations=array(		
		'users' => array(self::RELATION_TYPE_HAS_MANY,'ModuleUser_EntityUser','team_id'),
	);
	...


prefix_user: user_id | user_login | team_id

class ModuleUser_EntityUser extends EntityORM
{
	protected $aRelations=array(		
		'team' => array(self::RELATION_TYPE_BELONGS_TO,'PluginVs_ModuleStat_EntityTeam','team_id')
	);
	...


И таким образом у вас будет работать:
$aUsers = $oTeam->getUsers();
foreach($aUsers as $oUser) {
    echo $oUser->getLogin();
    echo $oUser->getTeam()->getName() == $oTeam->getName(); // => true
}
avatar
все понял, спасибо, не знаю как ещё вас отблагодарить

З.Ы.
надеюсь что ответы на вопросы в этом топике помогут кому-нибудь ещё
avatar
на здоровье :) тоже на это надеюсь :)
avatar
заметил ещё одну странность, независом от того какое указано поле
тут вы указываете Foreign Key, а ошибка возникает при автоматическом определении Primary Key привязываемой таблицы. Если мой совет вам поможет, пофиксю в svn.
avatar
пофиксил в svn
avatar
что касается метода GetItemsByArray*() — мы вернули его для совместимости, однако для индексирования результирующих коллекций, лично я рекомендую использовать новый модификатор #index-from-primary (его можно использовать в любых запросах):
LS::User_GetItemsByFilter('id IN' => $a, '#index-from-primary');
этот код равносилен
LS::User_GetItemsByArrayId($a);
и вернет ассоциативный массив сущностей User с ключами, взятыми из поля `user_id`.
avatar
А можно ли заставить ORM полноценно работать в плагинах?
Тестирую функции ORM на плагине Page, постепенно изменяя его.
Пытаюсь вызвать
$oPage = Engine::GetInstance()->Page_GetPageByUrl('aaaaa');

Пишет
Fatal error: Class 'ModulePage' not found

А тот самы класс, который он хочет найти, объявляется как
class PluginPage_ModulePage extends ModuleORM

вместо
class ModulePage extends ModuleORM

если бы это не был плагин.
Получается, из-за приставки «PluginPage_» невозможно найти нужный класс.
Есть ли решение?
avatar
не уверен конечно…
avatar
… а не пробовали так?
$oPage = Engine::GetInstance()->PluginPage_Page_GetPageByUrl('aaaaa');
avatar
Спасибо! кажется уже ближе к разгадке, но возникла другая ошибка
Fatal error: Call to a member function GetByFilter() on a non-object in /var/www/livestreet/engine/classes/ModuleORM.class.php on line 265 

Поискал по сайту, почистил /var/www/livestreet/templates/compiled.new
Но не помогло
avatar
добавьте первой строчкой
parent::Init();

в методе Init() вашего модуля PluginPage_ModulePage
avatar
avatar
большре спасибо! теперь все работает :)
avatar
У меня очень странно работает метод setПоле, где Поле — название сущности, к которой относится данная (аналогично $oTopic->setAuthor() ).
$oProgram=Engine::GetInstance()->PluginDiary_ModuleProgram_GetProgramById(13);
$oProgram->setName("Firstprogram");
$oDay = Engine::GetInstance()->PluginDiary_ModuleProgram_GetDayById(101);
$oDay->setProgram($oProgram);
echo $oDay->getProgram()->getId();
$oDay->setName('Firstday1');
$oDay->Save();

Странно, что данный код печатает правильный Id, но почему-то не сохраняет его в нужное поле таблицы. Причем читает из этого поля тоже правильно: при закоментированной строке
$oDay->setProgram($oProgram);
выводит program_id из таблицы. Сущности Program и Day связаны как has_many
В чем может быть загвоздка? Может это баг?
avatar
поэтому приходится писать строку
$oDay->setProgramId($oProgram->getId());

вместо
$oDay->setProgram($oProgram);
avatar
пока не поддерживается прямое задание сущности через set, нужно действительно использовать
$oDay->setProgramId($oProgram->getId());

есть сомнение насчет необходимости более короткой записи…
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.