Добавить CModel::clearValidators

Предварительное обсуждение найденных ошибок перед отправкой их авторам фреймворка, а также внесение новых предложений.
Ответить
mrix
Сообщения: 125
Зарегистрирован: 2010.08.30, 11:48
Откуда: Россия, Новосибирск

Добавить CModel::clearValidators

Сообщение mrix »

Всем привет! Можно добавить метод CModel::clearValidators?

Код: Выделить всё

/**
 * Очистить валидаторы
 *
 * return void
 */
public function clearValidators()
{
    $this->_validators = null;
} 
Иногда приходится использовать динамическую валидацию атрибутов, т. е. правила проверки зависят от атрибутов модели.
Валидаторы создаются перед присваиванием атрибутов модели и больше не обновляются. А писать отдельный метод или валидатор не всегда хочется.

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

Модель:

Код: Выделить всё

<?php

class User
{
    /**
     * @var integer ID страны
     */
    public $country_id;
    /**
     * @var integer ID города
     */
    public $city_id;
    
    //...
    
    /**
     * Правила проверки атрибутов
     * 
     * return array
     */
    public function rules()
    {
        return array
        (
            //страна
            array
            (
                'country_id', 'exist',
                'className' => 'Country', 'attributeName' => 'id',
                'allowEmpty' => false,
            ),
            //город
            array
            (
                'city_id', 'exist',
                'className' => 'City', 'attributeName' => 'id',
                'criteria' => array('condition' => 't.country_id = :country_id', 'params' => array('country_id' => $this->country_id)),
                'allowEmpty' => false,
            ),
        );
    }
    
    //...
} 
Контроллер:

Код: Выделить всё

class UserController extends CController
{
    /**
     * Редактирование профиля
     *
     * return void
     */
    public function actionEdit()
    {
        //загружаем из БД
        $model = User::model()->findByPk(Yii::app()->getUser()->getId());
        
        //форма отправлена
        if (isset($_POST['User']))
        {
            //устанавливаем атрибуты (здесь создаются валидаторы)
            $model->setAttributes($_POST['User']);
            
            //проверка (вот здесь хотелось бы использовать новые значения)
            if ($model->save())
            {
                Yii::app()->getUser()->setFlash('ok', true);
            }
        }
        
        //рисуем
        $this->render('edit', array('model' => $model));
    }
}
 
Я выкручиваюсь таким способом:

Код: Выделить всё

abstract class ActiveRecord extends CActiveRecord
{
    /**
     * @var boolean Использовать динамическую валидацию.
     * Можно использовать: TRUE, FALSE, array('secenario1', 'scenario2', ...), 'scenario1, scenario2, ...'
     */
    public $dynamicValidation = false;
    /**
     * @var boolean Принудительно пересоздать валидаторы
     */
    protected $_recreateValidators = false;
    
    /**
     * Выполнить проверку атрибутов модели
     *
     * @param array $attributes Список атрибутов (NULL - все атрибуты)
     * 
     * @return boolean Атрибуты прошли успешно проверку
     * 
     * @see parent::validate()
     */
    public function validate($attributes = null)
    {
        //если нужно пересоздать валидаторы
        if (is_string($this->dynamicValidation))
        {
            $this->_recreateValidators = in_array($this->getScenario(), preg_split('\s*,\s*', $this->dynamicValidation), true);
        }
        elseif (is_array($this->dynamicValidation))
        {
            $this->_recreateValidators = in_array($this->getScenario(), $this->dynamicValidation, true);
        }
        else
        {
            $this->_recreateValidators = $this->dynamicValidation ? true : false;
        }
        
        //выполянем проверку
        $result = parent::validate($attributes);
        
        //убираем флаг пересоздания валидаторов
        $this->_recreateValidators = false;
        
        //результат
        return $result;
    }
    
    /**
     * Получить все объявленные в модели валидаторы
     * 
     * @return CList Все валидаторы
     * 
     * @see parent::getValidatorList()
     */
    public function getValidatorList()
    {
        //стандартное поведение - родительский метод
        if (!$this->_recreateValidators)
        {
            return parent::getValidatorList();
        }
        
        //
        Yii::trace(get_class($this) . '::' . __FUNCTION__);
        
        //пересоздать валидаторы
        return $this->createValidators();
    }
    
    /**
     * Получить список валидаторов для текущего сценария и атрибута
     * 
     * @param string $attribute Название атрибута (NULL - все атрибуты)
     * 
     * @return array Список валидаторов
     * 
     * @see parent::getValidators()
     */
    public function getValidators($attribute = null)
    {    
        //стандартное поведение - родительский метод
        if (!$this->_recreateValidators)
        {
            return parent::getValidators($attribute);
        }
        
        //
        Yii::trace(get_class($this) . '::' . __FUNCTION__ . '::' . $attribute);
        
        //пересоздать валидаторы
        $validatorList = $this->createValidators();
        
        //копируем родительский метод, но с новыми валидаторами
        $validators = array();
        $scenario = $this->getScenario();
        foreach ($validatorList as $validator)
        {
            if ($validator->applyTo($scenario))
            {
                if ($attribute === null || in_array($attribute, $validator->attributes, true))
                {
                    $validators[] = $validator;
                }
            }
        }
        return $validators;
    }
} 
И тогда

Код: Выделить всё

class User extends ActiveRecord
{
    /**
     * @var array Динамическая валидация при обновлении профиля
     */
    public $dynamicValidation = array('update');
} 
Вообще хотелось бы много в каких классах нужно заменить private на protected.
Это ведь фрэймворк. Он должен быть полностью расширяем.

Весь текст набирал в браузере. Где-то мог ошибиться, но суть, думаю, донёс.
Ekstazi
Сообщения: 1428
Зарегистрирован: 2009.08.20, 22:54
Откуда: Молдова, Бельцы
Контактная информация:

Re: Добавить CModel::clearValidators

Сообщение Ekstazi »

Код: Выделить всё

$model->getValidatorList() ->clear(); 
mrix
Сообщения: 125
Зарегистрирован: 2010.08.30, 11:48
Откуда: Россия, Новосибирск

Re: Добавить CModel::clearValidators

Сообщение mrix »

спасибо :) не заметил, что так CList.

но вопрос про protected остаётся. сейчас примеров других не вспомню, но помню, что спотыкался обо что-то.
mrix
Сообщения: 125
Зарегистрирован: 2010.08.30, 11:48
Откуда: Россия, Новосибирск

Re: Добавить CModel::clearValidators

Сообщение mrix »

Ekstazi писал(а):

Код: Выделить всё

$model->getValidatorList() ->clear();  
а вообще это же не поможет. это только очистит список валидаторов. и проверяться не будет ни один атрибут.
Ekstazi
Сообщения: 1428
Зарегистрирован: 2009.08.20, 22:54
Откуда: Молдова, Бельцы
Контактная информация:

Re: Добавить CModel::clearValidators

Сообщение Ekstazi »

Так для проверки города надо inline валидатор использовать.
mrix
Сообщения: 125
Зарегистрирован: 2010.08.30, 11:48
Откуда: Россия, Новосибирск

Re: Добавить CModel::clearValidators

Сообщение mrix »

Я знаю, что есть inline-валидаторы. Для таких простых проверок предпочитаю использовать стандартные.

Просто таких атрибутов может быть много. Это относится не только к CExistValidator'у. У меня в модели сейчас 9 таких атрибутов, которых нужно проверять в зависимости от значения остальных. Мне проще было бы пересоздать все валидаторы.

Мне кажется, что с такой проблемой сталкивалось большое количество разработчиков, а добавление такого метода не изменит остальной логики.
mrix
Сообщения: 125
Зарегистрирован: 2010.08.30, 11:48
Откуда: Россия, Новосибирск

Re: Добавить CModel::clearValidators

Сообщение mrix »

От разработчиков ответа не будет?
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Добавить CModel::clearValidators

Сообщение slavcodev »

Я не разработчик, но могу сказать с увереностью 99% для введения новых методов, нужны обоснование.
Вот это не совсем обоснованный аргумент
Иногда приходится
А писать отдельный метод или валидатор не всегда хочется.
ИМХО существующие возможности вполне достаточны
Чтоб удалить валидаторы есть $model->getValidatorList() ->clear();
А чтоб валидацию проводить выборочно есть сценарии.
Жду Yii 3!
mrix
Сообщения: 125
Зарегистрирован: 2010.08.30, 11:48
Откуда: Россия, Новосибирск

Re: Добавить CModel::clearValidators

Сообщение mrix »

Выборочная валидация - это не валидация в зависимости от параметров.
Inline-валидаторами можно обойтись, но просто добавлять по методу на каждый атрибут - это лишнее.

Это изменение не затронет остального. Кому нужно может выполнить такую инструкцию.

Код: Выделить всё

protected function beforeValidate()
{
    $this->recreateValidators(); //$this->_validators = null;
    return parent::beforeValidate();
} 
Это даже не вопрос этого метода. Хочется полную расширяемость фрэймворка, а пока этого нет.
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Добавить CModel::clearValidators

Сообщение slavcodev »

mrix писал(а):Это даже не вопрос этого метода. Хочется полную расширяемость фрэймворка, а пока этого нет.
Разве ActiveRecord extends CActiveRecord не достаточно расширяемо?
Жду Yii 3!
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Добавить CModel::clearValidators

Сообщение slavcodev »

Объясни пжл что такое динамическая валидация?
Жду Yii 3!
mrix
Сообщения: 125
Зарегистрирован: 2010.08.30, 11:48
Откуда: Россия, Новосибирск

Re: Добавить CModel::clearValidators

Сообщение mrix »

mc-bear писал(а):Объясни пжл что такое динамическая валидация?
Это я так выразился. Это валидация в зависимости от параметров модели (см. первый пост).
mc-bear писал(а):
mrix писал(а):Это даже не вопрос этого метода. Хочется полную расширяемость фрэймворка, а пока этого нет.
Разве ActiveRecord extends CActiveRecord не достаточно расширяемо?
Нет, не достаточно. Вот я и привожу пример.
Так, как выкручиваюсь я, не очень правильно. Я копирую текст родительского метода. Мне нужно смотреть и сравнивать его со своим. Если что-то изменилось, то мне тоже надо вносить эти изменения, чтобы вдруг не было каких-то сбоев.
mrix
Сообщения: 125
Зарегистрирован: 2010.08.30, 11:48
Откуда: Россия, Новосибирск

Re: Добавить CModel::clearValidators

Сообщение mrix »

Такие же проблемы были у меня с event'ами. Точно не помню, но пришлось переписывать все attachEventHandler, hasEvent почти на такие же, только потому что не мог обратиться к переменной _e.
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Добавить CModel::clearValidators

Сообщение slavcodev »

Я не понял первого поста, покажи пример контроллера, где идет манипуляция с моделью.
Жду Yii 3!
mrix
Сообщения: 125
Зарегистрирован: 2010.08.30, 11:48
Откуда: Россия, Новосибирск

Re: Добавить CModel::clearValidators

Сообщение mrix »

Код: Выделить всё

class UserController extends CController
{
    /**
     * Редактирование профиля
     *
     * return void
     */
    public function actionEdit()
    {
        //загружаем из БД
        $model = User::model()->findByPk(Yii::app()->getUser()->getId());
        
        //форма отправлена
        if (isset($_POST['User']))
        {
            //устанавливаем атрибуты (здесь создаются валидаторы)
            $model->setAttributes($_POST['User']);
            
            //проверка (вот здесь хотелось бы использовать новые значения)
            if ($model->save())
            {
                Yii::app()->getUser()->setFlash('ok', true);
            }
        }
        
        //рисуем
        $this->render('edit', array('model' => $model));
    }
} 
Валидаторы создаются при $model->setAttributes($_POST['User']). Проверка атрибута city_id (он зависит от country_id) идёт по значениям, которые я загрузил из БД.

Например, старое значение (из БД получено при $model->findByPk) country_id = 1. Новое значение country_id = 2.
Теперь я хочу проверить атрибут city_id как Country::model()->exists('country_id = 2'). А проверяю по старому значению country_id = 1, потому что валидаторы уже созданы и больше не обновляются, а создаются они до валидации.

Таких примеров много. Нажал на флажок - поле стало обязательным и т.п.
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Добавить CModel::clearValidators

Сообщение slavcodev »

А добавить правило в beforeValidate или обновить существующее не вариант?
Жду Yii 3!
mrix
Сообщения: 125
Зарегистрирован: 2010.08.30, 11:48
Откуда: Россия, Новосибирск

Re: Добавить CModel::clearValidators

Сообщение mrix »

Да вариантов много: inline-валидатор, getValidatorsList()->add(), как я делал и т.д. Это неудобно.
mrix писал(а):Это даже не вопрос этого метода. Хочется полную расширяемость фрэймворка, а пока этого нет.
Причём расширяемость должна быть простой. Так можно обойтись вообще без rules, а создавать все валидаторы, где нужно. Просто так было бы удобнее.
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Добавить CModel::clearValidators

Сообщение slavcodev »

У нас разные понятия о расширяемости. inline-валидаторы, свои собственные валидаторы extends CValidator, события в сочетании с поведениями именно то что нужно для таких специфичных задач как у тебя.
Ну не возможно предусмотреть все задачи. Так же как нельзя сделать фреймворк быстрым и легким и одновременно с кучей лишних методов, которые используются один раз на миллион.
Жду Yii 3!
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Добавить CModel::clearValidators

Сообщение slavcodev »

Тебе вот лень написать свой инлайн-валидатор, а разрабам потом этот метод нужно поддерживать.
Каждый лишний метод это проблема как для производительности, но главное для последующих расширений функционала.
Жду Yii 3!
mrix
Сообщения: 125
Зарегистрирован: 2010.08.30, 11:48
Откуда: Россия, Новосибирск

Re: Добавить CModel::clearValidators

Сообщение mrix »

Я как раз про расширяемость и говорю. Привел вот такой пример.
Я думаю, что фрэймворк не должен (или почти не должен) содержать private переменных. Потому что иногда методов не хватает, чтобы их обработать.
Ответить