Я новичок в Propel ORM (и ORM в целом) и пытаюсь немного разобраться в этом вопросе о правильном и четком разрешении отношений внешнего ключа в базе данных MySQL.
Начиная с 3 простых таблиц MySQL:
герой:
умение:
hero_skill:
Где hero_skill имеет ожидаемые свойства внешнего ключа и генерирует все классы Propel.
Как в Propel полностью «гидрировать» объект «Герой» с соответствующими ссылками на все их навыки в общем виде ?
Простой пример того, как это можно сделать с помощью определенной функции:
function getAllTheHeroes(){
$query = PropelQuery::from("Hero")
$heroes = $query->find(); //hero model objects
foreach($heroes as $hero){
$heroSkills = $hero->getHeroSkills(); //hero_skill model objects
foreach($heroSkills as $heroSkill){
$skill = $heroSkill->getSkill(); //skill model object.
}
echo $hero->exportTo('JSON'); //export to JSON string (new in Propel 1.6)
}
}
Таким образом, приведенный выше код получает всех героев, выполняет итерации и получает коллекцию всех записей hero_skill для hero_id, затем выполняет итерацию и получает записи навыков для skill_id. Важно то, что он упаковывает все эти разрешенные отношения как ссылки на объект Hero, поэтому вызовы getWHATEVER () «гидрируют» объект с этими данными, и все это так, что при экспорте каждого героя в JSON у него есть список из всех записей навыков для этого героя, а не только идентификаторы. Это работает, как и ожидалось, и хорошо, так как вы пишете обычные функции для обработки каждого типа объекта. JSON от этого - что-то вроде:
[{
"Id":1,
"Name":"Hero McHeroington",
"Description":"Awesome.",
"HeroSkills":
{
"HeroSkill_0":
{
"Id":1,
"HeroId":1,
"SkillId":2,
"Skill":
{
"Id":2,
"Name":"Interpretive Dance",
"Rank":5
}
},
"HeroSkill_1":
{
"Id":2,
"HeroId":1,
"SkillId":4,
"Skill":
{
"Id":4,
"Name":"Pottery",
"Rank":2
}
},
"HeroSkill_2":
{
"Id":3,
"HeroId":1,
"SkillId":5,
"Skill":
{
"Id":5,
"Name":"Walking",
"Rank":1
}
}
}
},{
"Id":2,
"Name":"Squire McTinyPants",
"Description":"Chipper.",
"HeroSkills":
{
"HeroSkill_0":
{
"Id":4,
"HeroId":2,
"SkillId":2,
"Skill":
{
"Id":2,
"Name":"Interpretive Dance",
"Rank":10
}
},
"HeroSkill_1":
{
"Id":5,
"HeroId":2,
"SkillId":3,
"Skill":
{
"Id":3,
"Name":"Peeping",
"Rank":6
}
},
"HeroSkill_2":
{
"Id":6,
"HeroId":2,
"SkillId":6,
"Skill":
{
"Id":6,
"Name":"Skipping",
"Rank":4
}
}
}
}]
Бла-бла.
Я хотел бы сделать что-то более общее:
function getModel($byClassName){
$pq = PropelQuery::from($byClassName)
$recs = $pq->find(); // <- collection of records of unknown type
foreach ($recs as $rec){
//Somehow retrieve all of the 'getFOREIGNKEY' Propel generated helper functions and call them all in sequence,
// then iterate down into any of these logical children objects and repeat the process to fully resolve all Foreign Key relationships within this Object
// and all this Objects 'children'
// AND not get stuck in infinite loops due to Many-To-Many relationships from the 'getWHATEVER' calls running in circles.
//Or some other method entirely to accomplish the same thing that i am just not getting...
echo $rec->exportTo('JSON'); //export to JSON string (new in Propel 1.6)
}
}
Я копаюсь в Propel Docs и Source, и я до сих пор не обернулся вокруг всей модели Peer / TableMap / RelationMap / etc, и я уже близко к решению из коробки, но продолжать бить в тупики.
НО это не похоже на ужасно уникальную концепцию, которую я пытаюсь воплотить в жизнь, поэтому я надеюсь, что здесь есть гуру Propel, который может быстро указать, где я отсталый по этому поводу.
Спасибо!
РЕДАКТИРОВАТЬ: (В ответ на комментарий wimvds)
Цель универсального подхода - дать мне возможность настроить отношения на уровне базы данных, затем продвинуть классы PHP и использовать их как объекты, не беспокоясь о базовом хранилище данных.
При особом подходе мне нужно было бы определить эти отношения в БД, а затем написать собственную функцию «гидратации» (как указано выше) для правильного извлечения объекта из хранилища данных. Философская проблема с этим подходом заключается в репликации, так как если я изменяю отношение в базе данных, то повторно запускаю propel-gen, ТОГДА мне также необходимо обновить мои функции гидратации, чтобы отразить это изменение в структуре. Похоже, что все части должны быть там, чтобы пропустить этот последний шаг и удалить репликацию.
Например, с приведенной выше небольшой функцией гидратации для конкретного случая все, что я делаю, это вызываю вспомогательные функции getWHATEVER (), основываясь на знании моей головы, что hero_skill относится как к герою, так и к навыку, и когда я получаю герой Я хочу проверить hero_skill и получить соответствующий список навыков. Propel также знает об этих отношениях благодаря внешним ключам, и при использовании joinWith () он даже знает о рекурсии (устанавливает для рекурсивных ссылок на объекты строковое значение '* RECURSION'), так что все части на месте для Propel чтобы разумно автоматически гидратировать эти объекты, в то же время корректно разрешая / игнорируя рекурсию, я просто не могу найти правильный синтаксис, чтобы это произошло.
т.е.: получить объект, разрешить его отношения FK, гидрировать все «дочерние» объекты (строки в связанных таблицах по FK) и исключить рекурсивные объекты.
Полагаю, что я ищу способ полностью абстрагировать хранилище данных, использовать возможности генерации классов Propel на основе определений базы данных schema.xml и, таким образом, не нужно настраивать пользовательские группы вспомогательных функций, которые могут быть предметом изменить, если структура данных меняется. Таким образом, позволяя мне кодировать в терминах объектов, а не записей базы данных, разве не в этом смысл ORM?
Надеюсь, это прояснит немного, спасибо!