Наследуемость полей у расширяемых xPDO-классов
Всем привет!
Смотрите, когда в своих компонентах мы наследуемся от, допустим, modResource, то в схеме мы пропишем следущее:
Теперь то, что хочу сделать я.
У меня есть пара десятков сущностей (и, соответственно, таблиц), у каждой из которых должен быть определённый набор полей, вроде «кем создан», «когда создан», «активно», «удалено» и т.д.
Так вот по аналогии с расширением класса modResource подумалось мне сделать вот так:
Так вот. При генерации все таблицы создаются абсолютно правильно, со всеми нужными полями — и в my_object_one, и в my_object_two есть столбцы active, deleted, createdby, createdon, etc… Т.е. такой механизм наследования как бы предумотрен и работает. Отлично.
Получилось у нас 3 файла с классами, соответствующие им map-файлы и metadata.mysql.php.
Содержимое metadata.mysql.php:
Содержимое map-файла класса-«шаблона» (жалко нету спойлеров, такие портянки нужно прятать):
Т.е. это просто описание столбцов и их свойств для myObjectTemplate. Код этот привожу для того, чтобы показать, что всё правильно. Ведь правильно же?
Вот аналогичное описание для класса myObjectOne:
Напоминаю, что в примере с расширением класса modResource в map-файле унаследованного класса была бы точно такая же строчка
И класс Ticket спокойно наследует все поля, свойства и методы класса modResource. Все мы это знаем.
Т.е. в моём случае пока всё сгенерировано корректно.
Так вот к чему всё это? Развязка :-)
А вся проблема в том, что унаследованные поля в таблицу не записываются. От слова «совсем».
Т.е. выполнив этот код:
Вот такой код:
Этот же xPDO сперва физически создаёт эти столбцы в таблицах, унаследовав их как надо, потом как надо генерирует map-файлы, а в конечном счёте чихать он на них хотел.
К слову сказать. В файлах самих объектов myobjectone.class.php и myobjecttwo.class.php подключается файл-«шаблон»:
Первой же мыслью было — «надо, чтобы для myObjectTemplate создавалась таблица!». Прописал в xml-схеме table=«table_blabla», всё перегенировал и нифига. Не работает.
Да и в конце концов, чем способ унаследования класса с несуществующей таблицей отличается от унаследования xPDOSimpleObject, который так же физически (в виде таблицы) не существует? Да ни чем, в сущности. Однако, с моими классами этот фокус не работает.
И что интересно — столбец id-то унаследован! Т.е. в цепочке myObjectOne -> myObjectTemplate -> xPDOSimpleObject, myObjectOne наследует столбец от xPDOSimpleObject, но не наследует столбцы от myObjectTemplate, как бы пропуская его.
И вот тут я, честно говоря, не знаю что делать. Документация на эту тему молчит, гугление плодов не принесло, а в исходниках я могу закопаться на неделю :-(
Конечно, остался вариант прописывать все эти поля для каждой таблицы в отдельности (а таких полей 12-15 штук + плюс индексы к ним). Но это просто нереально раздует xml-схему и уже после 5-6 таблицы ориентироваться в этом xml-аду будет невозможно (а таблиц будет 20-25). Это не просто вопрос удобства, а реально во время разработки будет невозможно скролить по 5-10-15 экранов вверх-вниз, чтобы прописывать связи и что-то там редактировать.
Хотелось бы универсального подхода. Да и в конце концов, xPDO, какого хрена??
В общем, дорогой читатель, такие дела. Надеюсь не сильно тебя утомил своими размышлениями и также надеюсь на твою помощь ибо в какую сторону копать — не представляю.
Смотрите, когда в своих компонентах мы наследуемся от, допустим, modResource, то в схеме мы пропишем следущее:
<model package="myPackage" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" phpdoc-package="myPackage" phpdoc-subpackage="" version="1.1">
<object class="myObject" extends="modResource">
</object>
</model>
И, собственно, всё — объект myObject унаследует все поля, их свойства и методы от класса modResource.Теперь то, что хочу сделать я.
У меня есть пара десятков сущностей (и, соответственно, таблиц), у каждой из которых должен быть определённый набор полей, вроде «кем создан», «когда создан», «активно», «удалено» и т.д.
Так вот по аналогии с расширением класса modResource подумалось мне сделать вот так:
<model package="myPackage" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" phpdoc-package="myPackage" phpdoc-subpackage="" version="1.1">
<object class="myObjectTemplate" extends="xPDOSimpleObject">
<field key="active" dbtype="tinyint" precision="1" attributes="unsigned" phptype="integer" null="false" default="0" />
<field key="deleted" dbtype="tinyint" precision="1" attributes="unsigned" phptype="integer" null="false" default="0" />
<field key="createdby" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0" />
<field key="createdon" dbtype="int" precision="20" attributes="unsigned" phptype="timestamp" null="false" default="0" />
<field key="deletedby" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0" />
<field key="deletedon" dbtype="int" precision="20" attributes="unsigned" phptype="timestamp" null="false" default="0" />
<!-- в действительности будет ещё десяток таких полей + индексы к ним -->
</object>
<object class="myObjectOne" table="my_object_one" extends="myObjectTemplate">
<field key="my_object_one_field" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0" />
</object>
<object class="myObjectTwo" table="my_object_two" extends="myObjectTemplate">
<field key="my_object_two_field" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0" />
</object>
</model>
Таким образом, имеем объект-«шаблон», у которого прописаны определённые поля и не прописана таблица. И остальные классы, которые расширяют этот класс-«шаблон» и добавляют в себя собственные столбцы (коряво выразился, знаю).Так вот. При генерации все таблицы создаются абсолютно правильно, со всеми нужными полями — и в my_object_one, и в my_object_two есть столбцы active, deleted, createdby, createdon, etc… Т.е. такой механизм наследования как бы предумотрен и работает. Отлично.
Получилось у нас 3 файла с классами, соответствующие им map-файлы и metadata.mysql.php.
Содержимое metadata.mysql.php:
$xpdo_meta_map = array (
'xPDOSimpleObject' => array (
0 => 'myObjectTemplate',
),
'myObjectTemplate' => array (
0 => 'myObjectOne',
1 => 'myObjectTwo',
),
);
Как видим, наследование установлено верно.Содержимое map-файла класса-«шаблона» (жалко нету спойлеров, такие портянки нужно прятать):
$xpdo_meta_map['myObjectTemplate']= array (
'package' => 'myPackage',
'version' => '1.1',
'extends' => 'xPDOSimpleObject',
'fields' => array (
'active' => 0,
'deleted' => 0,
'createdby' => 0,
'createdon' => 0,
'deletedby' => 0,
'deletedon' => 0,
),
'fieldMeta' => array (
'active' => array (
'dbtype' => 'tinyint',
'precision' => '1',
'attributes' => 'unsigned',
'phptype' => 'integer',
'null' => false,
'default' => 0,
),
'deleted' => array (
'dbtype' => 'tinyint',
'precision' => '1',
'attributes' => 'unsigned',
'phptype' => 'integer',
'null' => false,
'default' => 0,
),
// ну и так далее остальные поля
),
);
Т.е. это просто описание столбцов и их свойств для myObjectTemplate. Код этот привожу для того, чтобы показать, что всё правильно. Ведь правильно же?
Вот аналогичное описание для класса myObjectOne:
$xpdo_meta_map['myObjectOne']= array (
'package' => 'myPackage',
'version' => '1.1',
'table' => 'my_object_one',
'extends' => 'myObjectTemplate',
'fields' => array (
'my_object_one_field' => 0,
),
'fieldMeta' => array (
'my_object_one_field' => array (
'dbtype' => 'int',
'precision' => '10',
'attributes' => 'unsigned',
'phptype' => 'integer',
'null' => false,
'default' => 0,
),
),
);
Здесь видим описание того единственного столбца, который был указан в xml-схеме для этого класса, а так же строчку'extends' => 'myObjectTemplate',
, которая говорит о том, что класс myObjectOne расширяется от myObjectTemplate. И снова всё верно. Ведь верно же?Напоминаю, что в примере с расширением класса modResource в map-файле унаследованного класса была бы точно такая же строчка
'extends' => 'modResource',
Вот доказательства. Мукышщт :-)И класс Ticket спокойно наследует все поля, свойства и методы класса modResource. Все мы это знаем.
Т.е. в моём случае пока всё сгенерировано корректно.
Так вот к чему всё это? Развязка :-)
А вся проблема в том, что унаследованные поля в таблицу не записываются. От слова «совсем».
Т.е. выполнив этот код:
$o = $modx->newObject('myObjectOne');
$o->fromArray(array(
'active' => 1,
'createdon' => time(),
'my_object_one_field' => 100500
));
// или так:
/*
$o->set('active', 1);
$o->set('createdon', time());
$o->set('my_object_one_field', 100500);
*/
$result = $o->save();
в базу запишется только родное my_object_one_field поле и поле id с автоинкриментом. Унаследованные поля проигнорируются полностью!Вот такой код:
$o = $modx->newObject('myObjectOne');
print_r($o->toArray());
print_r($modx->getFields('myObjectOne'));
выведет вот это:Array
(
[id] =>
[my_object_one_field] => 0
)
Array
(
[id] =>
[my_object_one_field] => 0
)
И тут мы понимаем, что xPDO чихать хотел на поля, унаследованные от myObjectTemplate класса.Этот же xPDO сперва физически создаёт эти столбцы в таблицах, унаследовав их как надо, потом как надо генерирует map-файлы, а в конечном счёте чихать он на них хотел.
К слову сказать. В файлах самих объектов myobjectone.class.php и myobjecttwo.class.php подключается файл-«шаблон»:
<?php
require_once('myobjecttemplate.class.php');
class myObjectOne extends myObjectTemplate {}
Т.е. проблема точно не в этом.Первой же мыслью было — «надо, чтобы для myObjectTemplate создавалась таблица!». Прописал в xml-схеме table=«table_blabla», всё перегенировал и нифига. Не работает.
Да и в конце концов, чем способ унаследования класса с несуществующей таблицей отличается от унаследования xPDOSimpleObject, который так же физически (в виде таблицы) не существует? Да ни чем, в сущности. Однако, с моими классами этот фокус не работает.
И что интересно — столбец id-то унаследован! Т.е. в цепочке myObjectOne -> myObjectTemplate -> xPDOSimpleObject, myObjectOne наследует столбец от xPDOSimpleObject, но не наследует столбцы от myObjectTemplate, как бы пропуская его.
И вот тут я, честно говоря, не знаю что делать. Документация на эту тему молчит, гугление плодов не принесло, а в исходниках я могу закопаться на неделю :-(
Конечно, остался вариант прописывать все эти поля для каждой таблицы в отдельности (а таких полей 12-15 штук + плюс индексы к ним). Но это просто нереально раздует xml-схему и уже после 5-6 таблицы ориентироваться в этом xml-аду будет невозможно (а таблиц будет 20-25). Это не просто вопрос удобства, а реально во время разработки будет невозможно скролить по 5-10-15 экранов вверх-вниз, чтобы прописывать связи и что-то там редактировать.
Хотелось бы универсального подхода. Да и в конце концов, xPDO, какого хрена??
В общем, дорогой читатель, такие дела. Надеюсь не сильно тебя утомил своими размышлениями и также надеюсь на твою помощь ибо в какую сторону копать — не представляю.
Комментарии: 6
.
// см. $modx->map['myObjectTemplate'];
$modx->loadClass('myObjectTemplate');
// а сейчас смотри см. $modx->map['myObjectTemplate'];
$o = $modx->newObject('myObjectOne');
$o->fromArray(array(
'active' => 1,
'createdon' => time(),
'my_object_one_field' => 100500
));
$result = $o->save();
или в myobjectone.class.php дописать:
<?php
require_once('myobjecttemplate.class.php');
class myObjectOne extends myObjectTemplate {
public function __construct(xPDO & $xpdo) {
$xpdo->loadClass('myObjectTemplate',dirname(__FILE__).'/');
parent::__construct($xpdo);
}
}
тогда не надо будет каждый раз при создании самому загружать класс:$o = $modx->newObject('myObjectOne');
$o->fromArray(array(
'active' => 1,
'createdon' => time(),
'my_object_one_field' => 100500
));
$result = $o->save();
Вот спасибо тебе, добрый человек!
Ведь чувствовал же, что что-то где-то упускаю.
Жирный плюс тебе в карму :-)
Огромное количество времени и нервов съэкономлено)
Ведь чувствовал же, что что-то где-то упускаю.
Жирный плюс тебе в карму :-)
Огромное количество времени и нервов съэкономлено)
Блин, так все просто… Спасибо!)))
Т.к. не мне одному интересна данная тема и кто-то даже добавил этот тикет себе в избранное, то расскажу-ка я об ещё одной возникшей проблеме дабы сэкономить потомкам часы гугления и нервы.
Вот такой вот нехитрый код:
Дело в том, что такой вот унаследованный объект полученный методом $modx->getObject будет lazy (а вот через newObject всё хорошо). Не буду расписывать что это такое и почему. Факт в том, что сохраняться ничего не будет.
Чтобы всё работало как ожидается, надо в унаследованных классах переопределять метод set:
Лучше переопределить метод set и не заморачиваться. В этом случае $o->fromArray() тоже будет работать адекватно.
Вот такой вот нехитрый код:
$id = 1;
$o = $modx->getObject('myObjectOne', $id);
$o->fromArray(array(
'active' => 0
));
// или так:
// $o->set('active', 0);
var_dump($o->save());
будет приводить к вот такой вот ошибке (в modx-логе):Attempt to save lazy object: Array( <массив объекта> )
Дело в том, что такой вот унаследованный объект полученный методом $modx->getObject будет lazy (а вот через newObject всё хорошо). Не буду расписывать что это такое и почему. Факт в том, что сохраняться ничего не будет.
Чтобы всё работало как ожидается, надо в унаследованных классах переопределять метод set:
function set ($k, $v= null, $vType= '') {
if ($this->isLazy()) $this->_loadFieldData($this->_lazy);
return parent::set($k, $v, $vType);
}
Либо перед изменением данных объекта делать $o->toArray();
// а потом что-то изменяем
$o->set('active', 0);
Тогда тоже будет хорошо, но это не удобно.Лучше переопределить метод set и не заморачиваться. В этом случае $o->fromArray() тоже будет работать адекватно.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.