Как запретить сохранять поле при save() модели?

Общие вопросы по использованию фреймворка. Если не знаете как что-то сделать и это про Yii, вам сюда.
MozgoEd
Сообщения: 31
Зарегистрирован: 2012.06.18, 10:51

Как запретить сохранять поле при save() модели?

Сообщение MozgoEd »

Привет всем любителям Yii!

Измучен я страданьем этим,
Я до тебя не плохо жил.
Зачем тебя я только встретил,
Скажи пожалуйста, скажи.

Это то я про этот замечательный фреймворк. Теперь по делу.

Пишу социальную сеть на Yii + MongoDB. Использую расширение YiiMongoDbSuite. Возникла проблема.
Дело в том, что в MongoDb нет жесткой структуры документа и если я запихну новое поле - оно сохраняется.
Вопрос в следующем: Как запретить Yii сохранять определенные аттрибуты? Чтобы они просто не сохранялись при вызове $model->save() ?????
Например, при авторизации пользователь вводит кроме логина и пароля еще и капчу. Чтобы код капчи проверялся, он должен быть свойством класса модели, притом публичным свойством, иначе каждый раз введенный код будет не верным. И в Mongo получается, что при сохранении модели в базу ложится и код капчи, а он там нафиг не нужен. Еще такая забавная штука происходит, когда у нас есть поле Пароль и Подтверждение пароля, пароль мы после валидации шифруем и сохраняем в базу, но поле Подтверждение пароля (оно тоже проходит валидации и значит определенно в модели) тоже ложится в базу при сохранении. Получается классная штука, мы термоядерными алгоритмами хешируем пароль, и все это напрасно, так как рядом есть поле подтверждение пароля, которое в откром виде. Как быть? Пробывал колдовать с safe/unsafe, но ничего не выходит, как только объявляю код капчи как unsafe, так сразу пропадает валидация этого поля, т.е. все время пишет, что введенный код не правильный. Гугл не дал никаких результатов. Есть идеи?

Код модели:

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

class Item extends EMongoDocument{
    public $name;
    public $soname;
    
    public $verifyCode;
    
    public static function model($className = __CLASS__) {
        return parent::model($className);
    }
    
    public function getCollectionName() {
        return 'item';
    }
    
    public function rules() {
        return array(
            array('name, soname', 'required', 'message'=>'Warning bleat!!!!!!'),
            array('verifyCode', 'captcha', 'message'=>'hueviy cod'),
        );
    }
    
    public function attributeLabels() {
        return array(
            'name'=>'Name',
            'soname'=>'Soname',
        );
    }
    
    public function attributeNames() {
        return array(
            'name',
            'soname',
        );
    }
}
Код действия:

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

    public function actionIndex()
    {
        // renders the view file 'protected/views/site/index.php'
        // using the default layout 'protected/views/layouts/main.php'
                $model = new Item;
                if(isset($_POST['Item'])) {
                    $model->attributes = $_POST['Item'];
                    if($model->validate()) {
                        $model->save();
                        Yii::app()->end();
                    }
                }
        $this->render('index', array('model'=>$model));
    }
Код представления:

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

<div class="form">
<?php $form=$this->beginWidget('CActiveForm', array(
    'id'=>'UserForm',
    'method'=>'post',
)); ?>
 
    <?php echo $form->errorSummary($model); ?>
 
    <div class="row">
        <?php echo $form->label($model,'name'); ?>
        <?php echo $form->textField($model,'name') ?>
    </div>
 
    <div class="row">
        <?php echo $form->label($model,'soname'); ?>
        <?php echo $form->textField($model,'soname') ?>
    </div>
 
    <div class="row rememberMe">
<?if(extension_loaded('gd') && Yii::app()->user->isGuest):?>
    <?=CHtml::activeLabelEx($model, 'verifyCode')?>
    <?$this->widget('CCaptcha')?>
    <?=CHtml::activeTextField($model, 'verifyCode')?>
<?endif?>
    </div>
 
    <div class="row submit">
        <?php echo CHtml::submitButton('Send'); ?>
    </div>
 
<?php $this->endWidget(); ?>
</div><!-- form -->
Аватара пользователя
anton44eg
Сообщения: 2716
Зарегистрирован: 2012.01.25, 13:37
Откуда: Киев

Re: Как запретить сохранять поле при save() модели?

Сообщение anton44eg »

unset() поля в beforeSave()?
MozgoEd
Сообщения: 31
Зарегистрирован: 2012.06.18, 10:51

Re: Как запретить сохранять поле при save() модели?

Сообщение MozgoEd »

anton44eg писал(а):unset() поля в beforeSave()?
На безрыбье, и этот вариант рыба. Был.

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

    public function rules() {
        return array(
            array('name, soname', 'required', 'message'=>'Warning bleat!!!!!!'),
            array('verifyCode', 'captcha', 'message'=>'hueviy cod'),
        );
    }
    
    public function beforeSave() {
        unset($this->verifyCode);
        return parent::beforeSave();
    }
Но ошибка происходит.
Property "Item.verifyCode" is not defined. Не видит он это свойство, грохнули мы его, грохнули.
Аватара пользователя
anton44eg
Сообщения: 2716
Зарегистрирован: 2012.01.25, 13:37
Откуда: Киев

Re: Как запретить сохранять поле при save() модели?

Сообщение anton44eg »

или делайте validate(), потом обнуляйте аттрибут, потом save(false)
MozgoEd
Сообщения: 31
Зарегистрирован: 2012.06.18, 10:51

Re: Как запретить сохранять поле при save() модели?

Сообщение MozgoEd »

anton44eg писал(а):или делайте validate(), потом обнуляйте аттрибут, потом save(false)
А что? Это тоже вариант, правда костыль конечно, но так как валидация уже прошла, можно и сохранить с save(false). Ушел пробывать
MozgoEd
Сообщения: 31
Зарегистрирован: 2012.06.18, 10:51

Re: Как запретить сохранять поле при save() модели?

Сообщение MozgoEd »

MozgoEd писал(а):
anton44eg писал(а):или делайте validate(), потом обнуляйте аттрибут, потом save(false)
А что? Это тоже вариант, правда костыль конечно, но так как валидация уже прошла, можно и сохранить с save(false). Ушел пробывать
Фиг бы там. Yii не сдается. Славный фреймворк, душевный.

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

    public function actionIndex() {
                $model = new Item;
                if(isset($_POST['Item'])) {
                    $model->attributes = $_POST['Item'];
                    if($model->validate()) {
                        unset($model->verifyCode);
                        $model->save(false);
                        Yii::app()->end();
                    }
                }
        $this->render('index', array('model'=>$model));
    } 
Один фиг: Property "Item.verifyCode" is not defined. А счастье было так близко :cry:
MozgoEd
Сообщения: 31
Зарегистрирован: 2012.06.18, 10:51

Re: Как запретить сохранять поле при save() модели?

Сообщение MozgoEd »

Я конечно понимаю, что вопрос совсем не детский, да и способы решения в голове рисуются (например переопределять метод присваивания, но это же жесть), но хотелось бы услышать мнение официальных лиц, продвигающих фреймворк. Не хочу костыли городить. Как например, связаться с Sam Dark? Кто-нибудь знает?
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Как запретить сохранять поле при save() модели?

Сообщение samdark »

Я не работал с YiiMongoDbSuite, именно по нему подсказать не могу.
MozgoEd
Сообщения: 31
Зарегистрирован: 2012.06.18, 10:51

Re: Как запретить сохранять поле при save() модели?

Сообщение MozgoEd »

Sam Dark писал(а):Я не работал с YiiMongoDbSuite, именно по нему подсказать не могу.
На самом деле, тут, скорее всего, дело даже не в YiiMongoDbSuite, а в том, что когда мы строим через Gii модель из бд MySQL, там читаются мета-данные и создаются поля класса модели. А при сохранении такой проблемы не возникает (я проделывал аналогичные операции в MySQL) по той причине,что в таблице нет такого поля, например verifyCode или ConfirmPass. Но структура документов MongoDB, даже при наличии в ней записей, позволяет сохранить несуществующее до этого момента поле.
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Как запретить сохранять поле при save() модели?

Сообщение slavcodev »

Тут как раз скорее всего дело в YiiMongoDbSuite. Потому что yii считает атрибутами то что вернет функция:

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

public function attributeNames() {
        return array(
            'name',
            'soname',
        );
    } 
Жду Yii 3!
MozgoEd
Сообщения: 31
Зарегистрирован: 2012.06.18, 10:51

Re: Как запретить сохранять поле при save() модели?

Сообщение MozgoEd »

mc-bear писал(а):Тут как раз скорее всего дело в YiiMongoDbSuite. Потому что yii считает атрибутами то что вернет функция:

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

public function attributeNames() {
        return array(
            'name',
            'soname',
        );
    }
Я думал об этом, дело в том, что даже если вообще не переопределять метод attributeNames(), т.е его вообще тупо нет в коде модели, проблема остается.
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Как запретить сохранять поле при save() модели?

Сообщение slavcodev »

так если его нет то фреймоворк сгенерирует из базы, но если он есть, должен рабоатьт он
Жду Yii 3!
MozgoEd
Сообщения: 31
Зарегистрирован: 2012.06.18, 10:51

Re: Как запретить сохранять поле при save() модели?

Сообщение MozgoEd »

mc-bear писал(а):так если его нет то фреймоворк сгенерирует из базы, но если он есть, должен рабоатьт он
Сгенерирует из базы? Так поля verifyCode нет в БД. И это Mongo, а не MySQL или Postgresql какой-нибудь.
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Как запретить сохранять поле при save() модели?

Сообщение slavcodev »

Вот поэтому не известно что там делает YiiMongoDbSuite и как он генерирует запрос на сохранение. Нужно смотреть API EMongoDocument::save()
Жду Yii 3!
MozgoEd
Сообщения: 31
Зарегистрирован: 2012.06.18, 10:51

Re: Как запретить сохранять поле при save() модели?

Сообщение MozgoEd »

mc-bear писал(а):Вот поэтому не известно что там делает YiiMongoDbSuite и как он генерирует запрос на сохранение. Нужно смотреть API EMongoDocument::save()
Ну так вот:

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

    public function save($runValidation=true,$attributes=null)
    {
        if(!$runValidation || $this->validate($attributes))
            return $this->getIsNewRecord() ? $this->insert($attributes) : $this->update($attributes);
        else
            return false;
    }
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Как запретить сохранять поле при save() модели?

Сообщение slavcodev »

Кстати метод save имеет второй параметр $attributes, может хакнуть удасться

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

class Item extends EMongoDocument {
  public function save($runValidators = true, $attributes = null) {
     if($attributes == null) {
         $attributes = $this->attributeNames()
         unset($attributes['verifyCode']);
     }
     parent::save($runValidators, $attributes);
  }
}
Жду Yii 3!
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Как запретить сохранять поле при save() модели?

Сообщение slavcodev »

Еще как вариант создать одельный модель extends CFormModel, который будет проверять verifyCode и сохранять уже модель Item
ИМХО более правильный вариант, чем путать в одной моделе и запись БД и элементы формы
Жду Yii 3!
MozgoEd
Сообщения: 31
Зарегистрирован: 2012.06.18, 10:51

Re: Как запретить сохранять поле при save() модели?

Сообщение MozgoEd »

mc-bear писал(а):Кстати метод save имеет второй параметр $attributes, может хакнуть удасться

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

class Item extends EMongoDocument {
  public function save($runValidators = true, $attributes = null) {
     if($attributes == null) {
         $attributes = $this->attributeNames()
         unset($attributes['verifyCode']);
     }
     parent::save($runValidators, $attributes);
  }
} 
Вы не поверите. Сработало. Поле verifyCode в базу не записалось.
MozgoEd
Сообщения: 31
Зарегистрирован: 2012.06.18, 10:51

Re: Как запретить сохранять поле при save() модели?

Сообщение MozgoEd »

mc-bear писал(а):Еще как вариант создать одельный модель extends CFormModel, который будет проверять verifyCode и сохранять уже модель Item
ИМХО более правильный вариант, чем путать в одной моделе и запись БД и элементы формы
А как такое можно провернуть. Ведь у CFormModel будут свои поля, тогда придется создавать от CFormModel такую же модель как и Item (в данном случае), только без таких полей в модели, как verifyCode и ConfirmPass. Получается не очень хорошо, т.к. на каждую такую модель для Mongo придется городить две модели, одну от CFormModel, другую от EMongoDocument. Много классов, очень много.
MozgoEd
Сообщения: 31
Зарегистрирован: 2012.06.18, 10:51

Re: Как запретить сохранять поле при save() модели?

Сообщение MozgoEd »

Запилил решение свой проблемы. Выкладываю здесь, чтобы хотя бы другие любители Yii не сошли с ума, как сегодня сошел я.

Шаги к решению:

1) Создаем модифицированную модель, которая наследуется от EMongoDocument (метод getCollectionName - абстрактный и должен быть реализован)

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

class EModMongoDocument extends EMongoDocument {
    
    public function save($runValidators = true, $attributes = null) {
        if($attributes === null) {
            $attributes = $this->attributeNames();
        } 
        return parent::save($runValidators, $attributes);
    }

    public function getCollectionName() {

    }
} 
2. И все модели наследуем теперь уже от нашей EModMongoDocument (обратите внимание, что в нашей модели ОБЯЗАТЕЛЬНО должен быть определен метод attributeNames, иначе ничего не получится. Очень важно, не один час убил.)

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

<?php

class Item extends EModMongoDocument {
    public $name;
    public $soname;

    public $verifyCode;

    public static function model($className = __CLASS__) {
        return parent::model($className);
    }

    public function getCollectionName() {
        return 'item';
    }

    public function rules() {
        return array(
            array('name, soname', 'required', 'message'=>'Warning bleat!!!!!!'),
            array('verifyCode', 'captcha', 'message'=>'hueviy cod'),
        );
    }

    public function attributeLabels() {
        return array(
            'name'=>'Name',
            'soname'=>'Soname',
        );
    }

    public function attributeNames() {
      return array(
          'name',
          'soname',
      );
    }

}

?>
3. Теперь делаем вызов из контроллера (обратите внимание, что не важно как вы будете вызывать сохранение, так $model->save() или так $model->save(false), код будет работать одинаково).

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

    public function actionIndex() {
            $model = new Item;
            if(isset($_POST['Item'])) {
                $model->attributes = $_POST['Item'];
                if($model->validate()) {
                    $model->save();
                    Yii::app()->end();
                }
            }
            $this->render('index', array('model'=>$model));
    } 
Результат: поле с кодом капчи (verifyCode) проверяется, но в БД не сохраняется. Админ!
Всем спасибо! Удачи! I love this game...
Ответить