Вот здесь Илья Уткин уже писал про связи aggregates и composites. aggregates — не жесткие связи, а composites — жесткие. Что это значит? Это значит, что если есть два xPDO-класса, между которыми описаны жесткие composites-связи, то при удалении главного объекта будет удален и дочерний объект. Давайте рассмотрим простой и понятный пример. Вот есть у нас класс modUser (пользователь), и есть modUserProfile (профиль пользователя). Ведь абсолютно логично, что профиль пользователя не может существовать без самого пользователя. Потому логично, что если мы удаляем пользователя, то и профиль его должен тоже удаляться. При этом если вы выполните $modx->getObject('modUser', $id)->remove(), то удалится не только пользователь (modUser), но и его профиль (modUserProfile) (а так же настройки пользователя (UserSettings), и записи членства в группах (UserGroupMembers)). То есть, когда у нас правильно настроены все связи, то нам не приходится каждый раз писать лишний код, плюс исключается человеческий фактор. Давайте посмотрим на описание composites-связей класса modUser: 'composites' =>
array (
'Profile' =>
array (
'class' => 'modUserProfile',
'local' => 'id',
'foreign' => 'internalKey',
'cardinality' => 'one',
'owner' => 'local',
),
'UserSettings' =>
array (
'class' => 'modUserSetting',
'local' => 'id',
'foreign' => 'user',
'cardinality' => 'many',
'owner' => 'local',
),
'UserGroupMembers' =>
array (
'class' => 'modUserGroupMember',
'local' => 'id',
'foreign' => 'member',
'cardinality' => 'many',
'owner' => 'local',
),
), Довольно подробно все это Илья описывал здесь, а я дальше двинусь к сути топика. В общем, если кто-то уже создавал свои кастомные классы, наверняка пробовали расширять другие классы и в описаниях. То есть, если вы к примеру, расширяете modResource, вам нет нужды опять описывать это огромное количество колонок. Достаточно просто в описании класса указать 'extends' => 'modResource', и описания всех колонок будут унаследованы от modResource. Так же будут унаследованы и связи composites и aggregates. То есть вы создали свой класс myResource extends modResource, и легко можете делать так: $modx->getObject('myResource', $id)->getOne('Parent'), так как описание связи с Parent описано в modResource. Удобно? А то. Но рассмотрим вот такой пример: мы расширили modResource, и для своего кастомного класса создали связанный объект, и связь жесткая. То есть если удалять объект вашего класса, то xPDO по связям поймет, что и связанный объект тоже надо удалять. Но здесь есть большое НО… А вот мы после того, как для этого документа были созданы связанные объекты взяли, и переключили класс документа опять в modDocument… А modDocument не расширяет наш класс, он не знает о связях нашего кастомного класса с другими пользовательскими классами… И все, связь нарушилась. После этого кто-то взял, удалил этот документ, а мусор после него остался. И вот все это печально. Но как оказалось, и на это решение есть! — Метод xPDO::setPackageMeta(). Давайте посмотрим его код: /**
Adds metadata information about a package and loads the xPDO::$classMap.
@param string $pkg A package name to use when looking up classes/maps in xPDO.
@param string $path The root path for looking up classes in this package.
@return bool
*/
public function setPackageMeta($pkg, $path = '') {
$set = false;
if (is_string($pkg) && !empty($pkg)) {
$pkgPath = str_replace('.', '/', $pkg);
$mapFile = $path . $pkgPath . '/metadata.' . $this->config['dbtype'] . '.php';
if (file_exists($mapFile)) {
$xpdo_meta_map = '';
include $mapFile;
if (!empty($xpdo_meta_map)) {
foreach ($xpdo_meta_map as $className => $extends) {
if (!isset($this->classMap[$className])) {
$this->classMap[$className] = array();
}
$this->classMap[$className] = array_unique(array_merge($this->classMap[$className],$extends));
}
$set = true;
}
} else {
$this->log(xPDO::LOG_LEVEL_WARN, "Could not load package metadata for package {$pkg}.");
}
} else {
$this->log(xPDO::LOG_LEVEL_ERROR, 'setPackageMeta called with an invalid package name.');
}
return $set;
} В общем, когда выполняется метод xPDO::addPackage() (многие с ним наверняка сталкивались), то выполняется и этот метод, и он пытается найти в подключаемом пакете файл с мета-описанием пакета. $mapFile = $path . $pkgPath . '/metadata.' . $this->config['dbtype'] . '.php'; То есть для MySQL в папке модели вашего пакета должен лежать файл metadata.mysql.php Вот там мы можем описать наследование других классов нашими собственными. К примеру так: <php
$xpdo_meta_map = array (
'xPDOSimpleObject' =>
'modResource' =>
array (
'myResource',
),
); И вот когда вот эти связи объектов указаны, тогда при инициализации конечного объекта он будет выполнять $this->_composites= $xpdo->getComposites($this->_class), и получать связи и дочерних классов. Но здесь, как оказалось, есть очень паршивая штука… myResource наследует modResource, modDocument наследует modResource, но myResource не наследует modDocument. То есть здесь получается треугольник наследуемости и нет единой вертикали. По этой причине $modx->newObject('modResource'); будет иметь информацию о связях myResource-а (их связь описана в meta-файле), но $modx->newObject('modDocument'); не будет знать зависимостей myResource-а. Чтобы знал, придется указывать еще и 'modDocument' =>
array (
'myResource',
), Но ведь есть еще и modWebLink, modStaticResource и прочие, плюс еще могут и CRC появиться. Не перечислять же их все вручную… В общем я такую хитрость придумал: в meta-файле написать так: <php
$xpdo_meta_map = array ();
foreach((array)$this->getDescendants('modResource') as $class){
$xpdo_meta_map[$class] = array('myResource');
} Тогда xPDO получит все связанные классы modResource-а, и для всех их пропишет связь с моим классом. И тогда все эти связанные классы будут знать о зависимостях моего класса. Но, во-первых, ни в коем случае не злоупотребляйте этим, и тем более моим хаком. Дело в том. что очень многое на этом завязано. И у наследуемых объектов могут быть переопределены колонки и т.п. Потому такие вещи лучше делать только для тех классов, которые не сильно отличаются от базовых. И в этом ключе вообще имеет смысл разработать какие-то дополнительные инструментарии для контроля таких связей. А то получается, что несколько классов лежат в одной таблице, у всех ID уникальные, появился CRC, появились новые связи, на всех база данных единая, но стоит поменять класс объекта, и все, целостность базы рушится… Удалил запись с другим class_key, и в базе осталось куча мусора. Но механизмы вторичных ключей на уровне БД в MODX игнорируются напрочь, и этот механизм барахлит… В общем, хотелось бы улучшений ядра.
Мне обижаться совершенно не на что. Каждый выбирает то, что ему удобней. У меня нет цели всех посадить на MODX. Так что могу только пожелать удачи!
http://framework.joomla.org/ :-) Видимо в какой-то момент они сделали серьезный шаг вперед, но я это пропустил... Недавно я переносил сайт с Джумла. Бешеная система менюшек формирует различные УРЛы на один и тот же документ, в зависимости от того, где и какая менюшка вызывается. Конечно, это может косяк разработчика, который тот сайт делал, но все же... Пожалуйста, дайте хоть зацепку, может быть я сам справлюсь! Многое зависит от того, сколько этих пользователей-авторов будет. Если в пределах нескольких сотен, то еще нормально. Если тысячи, то уже сложнее гораздо. В любом случае, надо ставить MigxDB, создавать таблицу для связки Документ-автор, и туда уже фигачить эти связки через TV-поле. Почему нельзя использовать базовый TV-мультивыбор? Потому что тогда авторы будут храниться на каждый документ по одной только строчке в JSON-строке или с разделителями ||, что значительно усложнит выборки таких документов с учетом автора. С типом доставки, как я и говорил выше, все проще - простой выпадающий список в TV спасает. А далее формируете менюшки динамически. Как именно (структурно), это уже вам решать. В любом случае, сначала надо реализовать одноуровневые выборки товаров/категорий с учетом автора и типа доставки. Вот этот топик в помощь. То есть на конечном каталоге в простейшем варианте это будет адрес с GET-параметрами, типа catalog/?chocotype=247&product_type=188. Но если нужно именно ЧПУ реализовать, тогда свйо плагин-роутер надо писать. К примеру на этом сайте используется такой роутер, без этого просто никак (или много лишних документов пришлось бы плодить). К примеру, http://modxclub.ru/profile/nekto - такого документа фактически нет в site_content, но как видите, страница открывается. В помощь этот топик будет.
К сожалению за помощью сейчас у меня нету возможности обратиться, т.к. это займет явно не один час. Долго и упорно я люблю ковыряться, но в данном случае я даже не знаю в какую сторону ковырять. Да и время не так много тоже. Поэтому остается одно - сделать на это другом движке, не в обиду...
http://framework.joomla.org/ :-) Но я имел в виду саму систему. Пожалуйста, дайте хоть зацепку, может быть я сам справлюсь!
И если например в joomla-ле меню создается отдельно от документов, то в modx нет. Джумла теперь фрейм? Это так, к слову... Здесь надо динамические меню делать. Подробного рецепта не дам, так как вы замахнулись на слишком объемную задачу, и или вам самостоятельно долго и упорно это делать, или искать специалиста для помощи за денежку. А реализовать можно. Элементарно, гляньте http://unichoc.ru/, там товар по многим категориям найти можно.
Одна категория может принадлежать нескольким авторам. Дело в том, что структура должна быть: "по стране" -> категории -> подкатегории->категория1->товары "самовывоз" -> категории -> подкатегории->категория1->товары "самовывоз" -> категории -> подкатегории->категория2->товары "по стране" -> авторы-> автор1->товар2 "самовывоз" -> авторы-> автор1->товар1 И если например в joomla-ле меню создается отдельно от документов, то в modx нет. Можно сделать tv, но структура будет уже не та. Между авторами и типом связь many_many И между категориями и типом связь many_many Как быть?
Подскажите пожалуйста. Или может быть легче на framework-ке реализовать это, чем ковырять modx? Вообще-то, MODX - это фреймворк, если вы не в курсе. После этой фразы сразу захотелось отправить вас пытаться это реализовывать на каком-нибудь "framework-ке"... Проблема не в MODX-е (да-да, не про ShopModxBox надо спрашивать, а про MODX), а в том, что вы не можете просто продумать структуру так, чтобы правильно сущности и их связи организовать. Автор - он всегда один. Их дублировать никак нельзя. Если документы-товары и документы-категории создаются этими конечными авторами, то можно вообще оттолкнуться от колонки создателя документа. А типы доставки заводятся TV-полями и значение выбирается из списка. Потом простой поиск по TV и автору.