Запрет на сохранение xPDO-объекта, если не заполнены необходимые данные

Материал для экспертов. Многие сложные моменты не будут подробно объясняться, требуется хорошее знание xPDO. Небольшое вступление. Заморочился я тут на сайте реализовать теги для топиков (для SEO отличная штука). Создал новый класс SocietyTopicTag со следующими колонками: id, topic_id, tag, active. Какие тут главные моменты, побудившие написать топик? Поле tag не может быть пустым. Поле topic_id так же не может быть пустым. И вот далее мы разберемся с тем, как это предусмотреть, и какие тут есть подводные камни... Начнем с простого - с проверки поля tag и запрета сохранения. Сразу посмотрим конечный код класса SocietyTopicTag: <php

class SocietyTopicTag extends xPDOSimpleObject {

public function save($cacheFlag= null) {

    $this->tag = trim($this->tag);

    if(!$this->tag){

        $this->xpdo->log(xPDO::LOG_LEVEL_ERROR, "[- ". __CLASS__ ." -]. Column tag can not be empty");

        return false;

    }

    if(!$this->getOne('Topic')){

        $this->xpdo->log(xPDO::LOG_LEVEL_ERROR, "[- ". __CLASS__ ." -]. Topic required");

        return false;

    }

    return parent::save($cacheFlag);

}    

} 1. Перегружаем оригинальный метод save(). Теперь, если что-то не так, мы можем вернуть false, что будет означать, что объект не был сохранен. 2. Проверяем наличие значения у поля tag. if(!$this->tag){

        $this->xpdo->log(xPDO::LOG_LEVEL_ERROR, "[- ". __CLASS__ ." -]. Column tag can not be empty");

        return false;

    } Теперь вот такой код не создаст нового объекта: <php

$tag = $modx->newObject('SocietyTopicTag', array(

"tag"   => "",

));

if(!$tag->save()){

print "Error";

}

else{

print "Saved";

} Так как tag не заполнен, то сохранение объекта прервется. Это исключит сохранение объекта с незаполненым полем. А вот с topic_id несколько сложнее... Почему вот мы не стали проверять поле topic_id так же, как и tag, просто на наличие значения? Во-первых, топик для этого тега может быть еще новый и не сохранен в БД, и соответственно у него и не будет id. А если у топика нет id, то мы просто не можем записать его в качестве topic_id. К примеру, в таком случае бы вот этот код не сработал: <php

$tag = $modx->newObject('SocietyTopicTag', [

"tag"   => "DSfdsfdsF", 

]);

$r = $modx->newObject('modResource');

$r->addMany($tag);

$r->save(); Во-вторых, можно было бы указать id несуществующего топика. В случае проверки $this->topic_id у нас просто выполняется проверка значения на наличие, но нет проверки наличия документа. А вот в случае проверки объекта $this->Topic, даже если мы просто передали значение topic_id, xPDO попытается выполнить запрос к БД и получить объект топика, и если его не получит, то тег не будет сохранен. В итоге, представленная проверка сработает во всех случаях попытки сохранения как нового тега, так и обновления существующего. P.S. Понятное дело, что в данном случае важную роль играет описание composite/aggregates-связей классов modResource и SocietyTopicTag. В SocietyTopicTag map-файл дописываем: 'aggregates' =>

array (

'Topic' => 

array (

  'class' => 'modResource',

  'local' => 'topic_id',

  'foreign' => 'id',

  'cardinality' => 'one',

  'owner' => 'foreign',

),

), А вот в modResource мы просто так не вклинимся, так как это ядро MODX-а, и мы его не можем трогать. Но есть лазейка - meta-файл пакета (metadata.mysql.php). Вот туда мы и дописываем связь для modResource: $this->map['modResource']['composites']['TopicTags'] = array(

'class' => 'SocietyTopicTag',

'local' => 'id',

'foreign' => 'topic_id',

'cardinality' => 'many',

'owner' => 'local',

); Чтобы этот момент был чуть более понятен, я специально перенес свой старый топик.