Расширить CActiveRecord

Общие вопросы по использованию фреймворка. Если не знаете как что-то сделать и это про Yii, вам сюда.
Ответить
ustisha
Сообщения: 2
Зарегистрирован: 2011.01.05, 14:00

Расширить CActiveRecord

Сообщение ustisha »

Хочется добавить функционала в ActiveRecord, что то вроде insertUpdate, т.е. INSERT INTO .... ON DUPLICATE KEY UPDATE ..., еще не плохо было бы SELECT .... LOCK IN SHARE MODE.
Примерно так:

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

$a = new A();
$a->b = $b;
$a->insertUpdate(array(
   'update' => new CDbExpression('NOW()')
)); 
и

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

$criteria = new CDbCriteria();
$criteria->order = 'column';
$criteria->addCondition('field1 is NULL');
$criteria->addLock('LOCK IN SHARE MODE');
$res = $model->find($criteria); 
Хорошо бы расширить CDbCommandBuilder и дописать свои методы, от CDbCriteria тоже можно унаследовать свой класс, главный вопрос как заставить использовать фреймворк эти классы?
С новым годом :D , заранее спасибо за ответы.
Ekstazi
Сообщения: 1428
Зарегистрирован: 2009.08.20, 22:54
Откуда: Молдова, Бельцы
Контактная информация:

Re: Расширить CActiveRecord

Сообщение Ekstazi »

А зачем ? CDbCommandBuilder возвращает CDbCommand, от этого и плясать можно...
zibert02
Сообщения: 203
Зарегистрирован: 2010.11.24, 17:29

Re: Расширить CActiveRecord

Сообщение zibert02 »

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

Re: Расширить CActiveRecord

Сообщение slavcodev »

ustisha писал(а):главный вопрос как заставить использовать фреймворк эти классы?
вот тут можно подменить билдер
CActiveRecord::getCommandBuilder()
но зачем? можно перегрузить CActiveRecord::insert() изменив sql который должен быть выполнен.
Жду Yii 3!
ustisha
Сообщения: 2
Зарегистрирован: 2011.01.05, 14:00

Re: Расширить CActiveRecord

Сообщение ustisha »

1. Сделаем свой CDbExtCommandBuilder, в случае update добавляем параметры, а с ignore вообще все просто, переопределяем весь метод, могут быть проблемы при обновлении фреймворка но не такие болезненные как при изменеии непосредственно кода фреймвокра.
Заметьте: $this->_connection пришлось заменить на $this->getDbConnection(), скорее всего где то не досмотрел, но времени в обрез было.

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

class CDbExtCommandBuilder extends CDbCommandBuilder
{
  public function createInsertUpdateCommand($table,$data,$update)
  {
    $this->ensureTable($table);
    $fields=array();
    $values=array();
    $updates=array();
    $placeholders=array();
    $i=0;
    foreach($data as $name=>$value)
    {
      if(($column=$table->getColumn($name))!==null && ($value!==null || $column->allowNull))
      {
        $fields[]=$column->rawName;
        if($value instanceof CDbExpression)
        {
          $placeholders[]=$value->expression;
          foreach($value->params as $n=>$v)
            $values[$n]=$v;
        }
        else
        {
          $placeholders[]=self::PARAM_PREFIX.$i;
          $values[self::PARAM_PREFIX.$i]=$column->typecast($value);
          $i++;
        }
      }
    }
        foreach($update as $name=>$value)
    {
      if(($column=$table->getColumn($name))!==null && ($value!==null || $column->allowNull))
      {
        if($value instanceof CDbExpression)
        {
          $updates[]=$column->rawName.'='.$value->expression;
        }
        else
        {
          $updates[]=$column->rawName.'='.self::PARAM_PREFIX.$i;
          $values[self::PARAM_PREFIX.$i]=$column->typecast($value);
          $i++;
        }
      }
    }
    if($fields===array())
    {
      $pks=is_array($table->primaryKey) ? $table->primaryKey : array($table->primaryKey);
      foreach($pks as $pk)
      {
        $fields[]=$table->getColumn($pk)->rawName;
        $placeholders[]='NULL';
      }
    }
    $sql="INSERT INTO {$table->rawName} (".implode(', ',$fields).') VALUES ('.implode(', ',$placeholders).') ';
    $sql.='ON DUPLICATE KEY UPDATE '.implode(', ', $updates);
    $command=$this->getDbConnection()->createCommand($sql);

    foreach($values as $name=>$value)
      $command->bindValue($name,$value);

    return $command;
  }

  public function createInsertIgnoreCommand($table,$data)
  {
    $this->ensureTable($table);
    $fields=array();
    $values=array();
    $placeholders=array();
    $i=0;
    foreach($data as $name=>$value)
    {
      if(($column=$table->getColumn($name))!==null && ($value!==null || $column->allowNull))
      {
        $fields[]=$column->rawName;
        if($value instanceof CDbExpression)
        {
          $placeholders[]=$value->expression;
          foreach($value->params as $n=>$v)
            $values[$n]=$v;
        }
        else
        {
          $placeholders[]=self::PARAM_PREFIX.$i;
          $values[self::PARAM_PREFIX.$i]=$column->typecast($value);
          $i++;
        }
      }
    }
    if($fields===array())
    {
      $pks=is_array($table->primaryKey) ? $table->primaryKey : array($table->primaryKey);
      foreach($pks as $pk)
      {
        $fields[]=$table->getColumn($pk)->rawName;
        $placeholders[]='NULL';
      }
    }
    $sql="INSERT IGNORE INTO {$table->rawName} (".implode(', ',$fields).') VALUES ('.implode(', ',$placeholders).')';
    $command=$this->getDbConnection()->createCommand($sql);

    foreach($values as $name=>$value)
      $command->bindValue($name,$value);

    return $command;
  }
} 

2. Унаследуем наш класс CExtActiveRecord от ActiveRecord

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

class CExtActiveRecord extends CActiveRecord
{

  /**
   * Returns the command builder used by this AR.
   * @return CDbCommandBuilder the command builder used by this AR
   */
  public function getCommandBuilder()
  {
    return new CDbExtCommandBuilder(Yii::app()->db->getSchema());
  }

  private function insertExt($builder, $table, $command)
  {
    if(!$this->getIsNewRecord())
      throw new CDbException(Yii::t('yii','The active record cannot be inserted to database because it is not new.'));
    if($this->beforeSave())
    {
      Yii::trace(get_class($this).'.insert()','system.db.ar.CActiveRecord');
      if($command->execute())
      {
        $primaryKey=$table->primaryKey;
        if($table->sequenceName!==null)
        {
          if(is_string($primaryKey) && $this->$primaryKey===null)
            $this->$primaryKey=$builder->getLastInsertID($table);
          else if(is_array($primaryKey))
          {
            foreach($primaryKey as $pk)
            {
              if($this->$pk===null)
              {
                $this->$pk=$builder->getLastInsertID($table);
                break;
              }
            }
          }
        }
        $this->_pk=$this->getPrimaryKey();
        $this->afterSave();
        $this->setIsNewRecord(false);
        $this->setScenario('update');
        return true;
      }
    }
    return false;
  }

  public function insertUpdate($update, $runValidation=true, $attributes=null)
  {
    if(!$runValidation || $this->validate($attributes))
        if ($this->getIsNewRecord())
        {
              $builder=$this->getCommandBuilder();
                $table=$this->getMetaData()->tableSchema;
                $command=$builder->createInsertUpdateCommand($table,$this->getAttributes($attributes), $update);
              return $this->insertExt($builder, $table, $command);
        }
        else
            return $this->update($attributes);
  }

    public function insertIgnore($runValidation=true, $attributes=null)
  {
    if(!$runValidation || $this->validate($attributes))
        if ($this->getIsNewRecord())
        {
              $builder=$this->getCommandBuilder();
            $table=$this->getMetaData()->tableSchema;
            $command=$builder->createInsertIgnoreCommand($table,$this->getAttributes($attributes));
            return $this->insertExt($builder, $table, $command);
        }
        else
            return $this->update($attributes);
  }
} 
3. Ну и после генерации модели заменяем класс родителя CActiveRecod на CExtActiveRecord.
4. Использование:

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

$queue = new Queue();
$queue->uid = $uid;
$queue->adapter = get_class($this);
$queue->insertIgnore();

$a = new Address();
$a->address = $address;
$a->postcode = $post;
$a->city = $city;
$a->insertUpdate(array(
      'update' => new CDbExpression('NOW()')
)); 

Т.к. это мой первый опыт с Yii, приветствуются замечания и дополнения.
Ответить