Phalcon: Как получить/проверить связанные объекты перед сохранением?

У меня есть модель Audio и модель AudioCategory.
Когда я сохраняю аудиообъект, я хочу проверить, что к нему прикреплена хотя бы одна аудиокатегория.

Для этого я создал собственный валидатор.
Я попытался использовать $audio->getRelated() в валидаторе, но он продолжает пытаться получить информацию из базы данных. Так как проверка происходит перед сохранением (и это здорово), то я получаю пустой список, поэтому мой валидатор всегда возвращает false.

Когда я печатаю аудиообъект без сохранения, я вижу свою аудиокатегорию в поле _related аудиообъекта (print_r($audio);):
[_related:protected] => Array
( [audiocategory] = > Массив
(
[0] => Объект GRQ\Audio\AudioCategory ([...])
[1] => Объект GRQ\Audio\AudioCategory ([...])
)
)

Если я попытаюсь напечатать $audio->audiocategory напрямую, я получу уведомление:
Доступ к неопределенному свойству GRQ\Audio\Audio::audiocategory
и ничего не будет возвращено.

Если я вызову $audio->getRelated(), я получу объект типа Phalcon\Mvc\Model\Resultset\Simple с пустым _result. (Что логично, так как он пошел и искал в базе данных...)


Поэтому мой вопрос:
Как я могу получить и проверить связанные поля перед их сохранением?


Вот мой (укороченный) тест контроллера:

    $audioCategory = new AudioCategory();
    $audioCategory->categoryId = 1;
    $arAudioCategory[0] = $audioCategory; 

    $audioCategory = new AudioCategory();
    $audioCategory->categoryId = 2;
    $arAudioCategory[1] = $audioCategory;

    $audio = new Audio();
    [...other fields initialization...]
    $audio->audiocategory = $arAudioCategory;
    $audio->save();

Вот (сокращенная) аудио модель:

namespace GRQ\Audio;
use GRQ\Validator\PresenceOfRelationValidator;
class Audio extends \Phalcon\Mvc\Model {
/**
 * @Primary
 * @Identity
 * @Column(type="integer", nullable=false)
 */
public $id = 0; 
/**
 * @Column(type="integer", nullable=false)
 */
public $createdAt = 0;

[...other fields all reflecting the database...]

public function initialize() {
    $this->setSource ( "audio" );

    // table relationships
    $this->hasMany ( "id", "GRQ\Audio\AudioCategory", "audioId", array(
            'alias' => 'audiocategory'
    ) );
}

public function validation() {      
    [...other validations...]

    $this->validate ( new PresenceOfRelationValidator ( array (
            "field" => "audiocategory" 
    ) ) );

    return $this->validationHasFailed () != true;
}
}

Вот (сокращенная) модель категории аудио:

namespace GRQ\Audio;    
class AudioCategory extends \Phalcon\Mvc\Model {
/**
 * @Primary
 * @Identity
 * @Column(type="integer", nullable=false)
 */
public $id = 0; 
/**
 * @Column(type="integer", nullable=false)
 */
public $audioId = 0;    
/**
 * @Column(type="integer", nullable=false)
 */
public $categoryId = 0;

public function initialize(){
    $this->setSource("audiocategory");
    //table relationships
    $this->belongsTo("audioId", "GRQ\Audio\Audio", "id", array(
            'alias' => 'audio'
    ));
}
}

Вот мой собственный валидатор (который не работает и всегда возвращает false):

namespace GRQ\Validator;

use Phalcon\Mvc\Model\Validator;
use Phalcon\Mvc\Model\ValidatorInterface;

class PresenceOfRelationValidator extends Validator implements ValidatorInterface {
public function validate($model){
    $field = $this->getOption('field');
    $message = $this->getOption('message');
    if (!$message) {
        $message = 'The required relation '.$field.' was not found';
    }

    $value = $model->getRelated($field);

    if (count($value) == 0) {
        $this->appendMessage(
                $message,
                $field,
                "PresenceOfRelation"
        );
        return false;
    }
    return true;
}
}

person ether    schedule 11.04.2014    source источник
comment
Вы пытались получить доступ с помощью audioCategory и audio_category?   -  person kbtz    schedule 14.04.2014
comment
Я только что попробовал, но результата больше нет. Хотя теперь я установил псевдоним при инициализации как audio, так и audioCategory. Но больше не повезло... Я обновил приведенный выше код, чтобы отразить это.   -  person ether    schedule 15.04.2014
comment
По сути, перед сохранением аудио (вместе с его отношениями) использование $audio-›audiocategory или $audio-›getaudiocategory() не работает. Когда я пытаюсь напечатать их в шаблоне, я получаю пустую страницу...: echo '‹br/›‹br/›audio: ‹pre›'; print_r($audio-›аудиокатегория); эхо '‹/pre›'; // пустая страница echo '‹br/›‹br/›audio: ‹pre›'; print_r($audio-›getaudiocategory()); echo '‹/pre›';//пустая страница   -  person ether    schedule 15.04.2014
comment
Честно говоря, у меня тоже не было успешной истории с родственными записями. Будем надеяться, что кто-то знает, что происходит...   -  person kbtz    schedule 15.04.2014


Ответы (1)


Итак, я нашел способ добиться этого. Не уверен, что это лучший способ, но он работает:
Поскольку значения защищены, мне пришлось выставить их из моего объекта.
Поэтому я создал базовую модель, из которой можно расширить себя:

Базовая модель:

namespace GRQ;
class BaseModel extends \Phalcon\Mvc\Model {

/**
 * This function should be used to get the data in the _related field directly.
 * It is very useful if you need to validate the presence of a relation BEFORE saving in the database.
 * To initialize the field with the database content, use $this->getRelated().
 */
public function getInternalRelated(){
    return $this->_related;
}   
}

Затем я изменил свой аудиокласс, чтобы он расширялся от моей базовой модели:

Аудио модель (упрощенная):

namespace GRQ\Audio;

use Phalcon\Mvc\Model\Validator\Numericality;
use GRQ\Validator\MinValueValidator;
use GRQ\Validator\PresenceOfRelationValidator;

class Audio extends \GRQ\BaseModel {
/**
 * @Primary
 * @Identity
 * @Column(type="integer", nullable=false)
 */
public $id = 0;

/**
 * @Column(type="string", length=255, nullable=false)
 */
public $title = '';

public function initialize() {
    $this->setSource ( "audio" );

    // table relationships
    $this->hasMany ( "id", "GRQ\Audio\AudioCategory", "audioId", array(
            'alias' => 'audiocategory'
    ) );
}

public function validation() {              
    $this->validate ( new PresenceOfRelationValidator ( array (
            "field" => "audiocategory" 
    ) ) );

    return $this->validationHasFailed () != true;
}
}

Моя модель AudioCategory (упрощенная) осталась почти такой же:

namespace GRQ\Audio;

use Phalcon\Mvc\Model\Message;

class AudioCategory extends \GRQ\BaseModel {
/**
 * @Primary
 * @Identity
 * @Column(type="integer", nullable=false)
 */
public $id = 0;

/**
 * @Column(type="integer", nullable=false)
 */
public $audioId = 0;

/**
 * @Column(type="integer", nullable=false)
 */
public $categoryId = 0;

public function initialize()
{
    $this->setSource("audiocategory");
    //table relationships
    $this->belongsTo("audioId", "GRQ\Audio\Audio", "id", array(
            'alias' => 'audio'
    ));
}
}

И мой валидатор теперь использует метод getInternalRelated для проверки:

namespace GRQ\Validator;

use Phalcon\Mvc\Model\Validator;
use Phalcon\Mvc\Model\ValidatorInterface;

class PresenceOfRelationValidator extends Validator implements ValidatorInterface {
public function validate($model){
    $field = $this->getOption('field');
    $message = $this->getOption('message');
    if (!$message) {
        $message = 'The required relation '.$field.' was not found';
    }

    $value = $model->getInternalRelated();

    if (count($value[$field]) == 0) {
        $this->appendMessage(
                $message,
                $field,
                "PresenceOfRelation"
        );
        return false;
    }
    return true;
}
}
person ether    schedule 15.04.2014