Commits

Anonymous committed 9df254d

* Added suport to MANY_MANY relations (even if no 3rd party extension is provided).
* Fixed i18n bug in "create" view. Thanks to @ben_rivero

  • Participants
  • Parent commits 2d3dc93

Comments (0)

Files changed (3)

components/AweActiveRecord.php

  */
 abstract class AweActiveRecord extends CActiveRecord
 {
+    public $oldValues = array();
 
     /**
      * @var string The separator (delimiter) used to separate the primary keys values in a
     }
 
     /**
+     * PHP getter magic method.
+     * This method is overridden so that AR attributes can be accessed like properties.
+     * @param string $name property name
+     * @return mixed property value
+     * @see getAttribute
+     */
+    public function __get($name) {
+        $getter = 'get' . ucfirst($name);
+        if (method_exists($this, $getter))
+            return $this->$getter();
+        return parent::__get($name);
+    }
+
+    /**
+     * PHP setter magic method.
+     * This method is overridden so that AR attributes can be accessed like properties.
+     * @param string $name property name
+     * @param mixed $value property value
+     */
+    public function __set($name, $value) {
+        $setter = 'set' . ucfirst($name);
+        if (method_exists($this, $setter))
+            return $this->$setter($value);
+        parent::__set($name, $value);
+    }
+
+    public function afterFind() {
+        parent::afterFind();
+        $this->oldValues = $this->getAttributes();
+    }
+
+    /**
      * Returns the text label for the specified active record relation, attribute or class property.
      * The labels are the user friendly names displayed in the views.
      * If defined in the model, the label for its attribute, property or relation is returned.
     }
 
     /**
-     * Fills the provided array of PK values with the composite PK column names.
-     * Warning: the order of the values in the array must match the order of
-     * the columns in the composite PK.
-     * The returned array has the format required by {@link CActiveRecord::findByPk}
-     * for composite keys.
-     * The method supports single PK also.
-     * @param mixed $pk The PK value or array of PK values.
-     * @return array The array of PK values, indexed by column name.
-     * @see CActiveRecord::findByPk
-     * @throws InvalidArgumentException If the count of values doesn't match the
-     * count of columns in the composite PK.
-     */
-    public function fillPkColumnNames($pk)
-    {
-        // Get the table PK column names.
-        $columnNames = $this->getTableSchema()->primaryKey;
-
-        // Check if the count of values and columns match.
-        $columnCount = count($columnNames);
-        if (count($pk) !== $columnCount) {
-            throw new InvalidArgumentException(Yii::t(
-                'AweCrud.messages',
-                'The count of values in the argument "pk" ({countPk}) does not match the count of columns in the composite PK ({countColumns}).'
-            ), array(
-                '{countPk}' => count($pk),
-                '{countColumns}' => $columnCount,
-            ));
-        }
-
-        // Build the array indexed by the column names.
-        if ($columnCount === 1) {
-            if (is_array($pk)) {
-                $pk = $pk[0];
-            }
-            return array($columnNames => $pk);
-        } else {
-            $result = array();
-            for ($columnIndex = 0; $columnIndex < $columnCount; $columnIndex++) {
-                $result[$columnNames[$columnIndex]] = $pk[$columnIndex];
-            }
-            return $result;
-        }
-    }
-
-    /**
      * Finds the relation of the specified column.
      * @param string|AweActiveRecord $modelClass The model class name or a model instance.
      * @param string|CDbColumnSchema $column The column.
         return null;
     }
 
+    /**
+     * @param $relationName
+     * @param $data PKs of the currently related records
+     * @throws CDbException
+     */
+    public function saveManyMany($relationName, $data)
+    {
+        if($this->getIsNewRecord())
+            throw new CDbException(Yii::t('yii','The active record cannot be updated because it is new.'));
+
+        /** @var CDbCommandBuilder $commandBuilder */
+        $commandBuilder=$this->dbConnection->commandBuilder;
+        $relations = $this->relations();
+        $relation = $relations[$relationName];
+
+        if($relation[0]!==self::MANY_MANY){
+            throw new CDbException(Yii::t('AweCrud.app', 'This is NOT a MANY_MANY relation'));
+        }
+
+        Yii::trace('Updating MANY_MANY table for relation ' . get_class($this) . '.' . $relationName, 'system.db.ar.CActiveRecord');
+
+        // get table and fk information
+        list($relationTable, $fks) = $this->parseManyManyFk($relationName, $relation);
+
+        // 1. Delete relation table entries for records that have been removed from relation
+        // @warning WE DON'T SUPPORT COMPOSITE PKs (yet)
+        $criteria = new CDbCriteria();
+        //var_dump($fks,$currentPKs, $this->primaryKey, $data);die();
+        $criteria->addNotInCondition($fks[1], $data/*$currentPKs*/)
+            ->addColumnCondition(array($fks[0] => $this->getPrimaryKey()));
+        $deleted = $commandBuilder->createDeleteCommand($relationTable, $criteria)->execute();
+
+        // 2. Gather the current records
+        $newCriteria = new CDbCriteria(array(
+            'select' => $fks[1], // $fks?
+        ));
+        $newCriteria->addColumnCondition(array($fks[0] => $this->primaryKey));
+        $currentPKs = $commandBuilder->createFindCommand($relationTable, $newCriteria)->queryColumn();
+        if($currentPKs===false){
+            $currentPKs = array();
+        }
+
+        // 3. add new entries to relation table
+        // @warning WE DON'T SUPPORT COMPOSITE PK's (yet)
+        foreach ($data as $fk) {
+            if (!in_array($fk, $currentPKs)) {
+                $commandBuilder->createInsertCommand(
+                    $relationTable,
+                    array(
+                        $fks[0] => $this->getPrimaryKey(),
+                        $fks[1] => $fk,
+                    )
+                )->execute();
+                $currentPKs[] = $fk;
+            }
+        }
+
+        // refresh relation data
+        //$this->getRelated($name, true); // will come back with github issue #4
+    }
+
+    /**
+     * Parses the foreign key definition of a MANY_MANY relation
+     *
+     * the first 7 lines are copied from CActiveFinder:561-568
+     * https://github.com/yiisoft/yii/blob/2353e0adf98c8a912f0faf29cc2558c0ccd6fec7/framework/db/ar/CActiveFinder.php#L561
+     *
+     * @todo this method should be removed and using code should implement solution of https://github.com/yiisoft/yii/issues/508 when it is fixed
+     *
+     * @throws CDbException
+     * @param string $name name of the relation
+     * @param array $relation relation definition
+     * @return array ($joinTable, $fks)
+     *               joinTable is the many-many-relation-table
+     *               fks are primary key of that table defining the relation
+     */
+    protected function parseManyManyFk($name, $relation)
+    {
+        if(!preg_match('/^\s*(.*?)\((.*)\)\s*$/',$relation[2],$matches))
+            throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".',
+                array('{class}'=>get_class($this),'{relation}'=>$name)));
+        if(($joinTable=$this->dbConnection->schema->getTable($matches[1]))===null)
+            throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.',
+                array('{class}'=>get_class($this), '{relation}'=>$name, '{joinTable}'=>$matches[1])));
+        $fks=preg_split('/\s*,\s*/',$matches[2],-1,PREG_SPLIT_NO_EMPTY);
+
+        return array($joinTable, $fks);
+    }
+
+    /**
+     * Returns all primary keys of the currently assigned records
+     *
+     * @throws CDbException
+     * @param string $relationName name of the relation
+     * @return array
+     */
+    private function getNewManyManyPks($relationName)
+    {
+
+        $newRelatedRecords=$this->getRelated($relationName, false);
+        if (!is_array($newRelatedRecords)) {
+            throw new CDbException('A MANY_MANY relation needs to be an array of records or primary keys!');
+        }
+        // get new related records primary keys
+        return $this->objectsToPrimaryKeys($newRelatedRecords);
+    }
+
+    /**
+     * converts an array of AR objects or primary keys to only primary keys
+     *
+     * @throws CDbException
+     * @param CActiveRecord[] $records
+     * @return array
+     */
+    private function objectsToPrimaryKeys($records)
+    {
+        $pks=array();
+        foreach($records as $record) {
+            if (is_object($record) && $record->isNewRecord)
+                throw new CDbException('You can not save a record that has new related records!');
+
+            $pks[]=is_object($record) ? $record->getPrimaryKey() : $record;
+        }
+        return $pks;
+    }
+
 }

generators/AweCrud/templates/default/controller.php

         $this->performAjaxValidation($model, '<?php echo $this->class2id($this->modelClass)?>-form');
 <?php endif; ?>
 
-		if(isset($_POST['<?php echo $this->modelClass; ?>']))
+        if(isset($_POST['<?php echo $this->modelClass; ?>']))
 		{
-<?php foreach(CActiveRecord::model($this->modelClass)->relations() as $key => $relation):?>
-<?php if($this->getUseRelatedRecordBehavior() and ($relation[0] == CActiveRecord::BELONGS_TO || $relation[0] == CActiveRecord::MANY_MANY)):?>
-                <?php echo "if (isset(\$_POST['$this->modelClass']['$key'])) \$model->$key = \$_POST['$this->modelClass']['$key'];\n"?>
+<?php if($this->getUseRelatedRecordBehavior()): ?>
+<?php   foreach(CActiveRecord::model($this->modelClass)->relations() as $key => $relation):?>
+<?php       if($relation[0] == CActiveRecord::BELONGS_TO || $relation[0] == CActiveRecord::MANY_MANY): ?>
+                <?php echo "if (isset(\$_POST['$this->modelClass']['$key'])) \$model->$key = \$_POST['$this->modelClass']['$key'];" . PHP_EOL; ?>
+<?php       endif; ?>
+<?php   endforeach; ?>
 <?php endif; ?>
-<?php endforeach; ?>
 			$model->attributes = $_POST['<?php echo $this->modelClass; ?>'];
-			if($model->save())
-				$this->redirect(array('view', 'id' => $model-><?php echo $this->tableSchema->primaryKey; ?>));
+			if($model->save()) {
+<?php if(!$this->getUseRelatedRecordBehavior()): ?>
+<?php   foreach(CActiveRecord::model($this->modelClass)->relations() as $key => $relation):?>
+<?php       if($relation[0] == CActiveRecord::MANY_MANY): ?>
+                <?php echo "if (isset(\$_POST['$this->modelClass']['$key'])) \$model->saveManyMany('$key', \$_POST['$this->modelClass']['$key']);" . PHP_EOL; ?>
+<?php       endif; ?>
+<?php   endforeach; ?>
+<?php endif; ?>
+                $this->redirect(array('view', 'id' => $model-><?php echo $this->tableSchema->primaryKey; ?>));
+            }
 		}
 
 		$this->render('create',array(
 
 		if(isset($_POST['<?php echo $this->modelClass; ?>']))
 		{
-<?php foreach(CActiveRecord::model($this->modelClass)->relations() as $key => $relation):?>
-<?php if($this->getUseRelatedRecordBehavior() and ($relation[0] == CActiveRecord::BELONGS_TO || $relation[0] == CActiveRecord::MANY_MANY)):?>
-                if (isset($_POST['<?php echo $this->modelClass?>']['<?php echo $key?>'])) $model-><?php echo $key?> = $_POST['<?php echo $this->modelClass?>']['<?php echo $key?>'];
-                else $model-><?php echo $key?> = array();
-    <?php endif; ?>
-<?php endforeach; ?>
+<?php if($this->getUseRelatedRecordBehavior()): ?>
+<?php   foreach(CActiveRecord::model($this->modelClass)->relations() as $key => $relation):?>
+<?php       if($relation[0] == CActiveRecord::BELONGS_TO || $relation[0] == CActiveRecord::MANY_MANY): ?>
+                <?php echo "if (isset(\$_POST['$this->modelClass']['$key'])) \$model->$key = \$_POST['$this->modelClass']['$key'];" . PHP_EOL; ?>
+                <?php echo "\$model->$key = array();" . PHP_EOL; ?>
+<?php       endif; ?>
+<?php   endforeach; ?>
+<?php endif; ?>
 			$model->attributes = $_POST['<?php echo $this->modelClass; ?>'];
-			if($model->save())
+			if($model->save()) {
+<?php if(!$this->getUseRelatedRecordBehavior()): ?>
+<?php   foreach(CActiveRecord::model($this->modelClass)->relations() as $key => $relation):?>
+<?php       if($relation[0] == CActiveRecord::MANY_MANY): ?>
+                <?php echo "if (isset(\$_POST['$this->modelClass']['$key'])) \$model->saveManyMany('$key', \$_POST['$this->modelClass']['$key']);" . PHP_EOL; ?>
+                <?php echo "else \$model->saveManyMany('$key', array());" . PHP_EOL; ?>
+<?php       endif; ?>
+<?php   endforeach; ?>
+<?php endif; ?>
 				$this->redirect(array('view','id' => $model-><?php echo $this->tableSchema->primaryKey; ?>));
+            }
 		}
 
 		$this->render('update',array(

generators/AweCrud/templates/default/create.php

 $label = $this->pluralize($this->class2name($this->modelClass));
 echo "\$this->breadcrumbs=array(
 	\$model->label(2) => array('index'),
-	'Create',
+	Yii::t('AweCrud.app', 'Create'),
 );\n";
 ?>