Yii Framework Forum: [EXTENSION] CSaveRelationsBehavior - Yii Framework Forum

Jump to content

  • (3 Pages)
  • +
  • 1
  • 2
  • 3
  • You cannot start a new topic
  • You cannot reply to this topic

[EXTENSION] CSaveRelationsBehavior CSaveRelationsBehavior Save HasMany and ManyMany relational active rec Rate Topic: ***** 1 Votes

#1 User is offline   Juban 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 12-January 09
  • Location:Grenoble France

Posted 14 May 2010 - 10:51 AM

Welcome to the CSaveRelationsBehavior discussion topic.

The CSaveRelationsBehavior enables ActiveRecord model to save HasMany and ManyMany relational active records along with the main model.
This component and its documentation are available from the official Yii Extension repository here.

I hope this extension will be helpfull for some of you.

Feel free to give your feedback or enhancements requests here.

Regards.
0

#2 User is offline   Juban 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 12-January 09
  • Location:Grenoble France

Posted 15 May 2010 - 09:56 AM

You can now see this behavior in action by downloading the demo application from the download section.
It is designed to run under Yii 1.0 but a Yii 1.1 compatible version is coming soon.
Demos for Yii 1.0 and 1.1 are now both available.

This post has been edited by Juban: 15 May 2010 - 11:44 AM

0

#3 User is offline   mech7 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 220
  • Joined: 26-March 09

Posted 30 May 2010 - 08:30 PM

:) Thanks it looks very nice..
0

#4 User is offline   Juban 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 12-January 09
  • Location:Grenoble France

Posted 31 May 2010 - 01:20 AM

View Postmech7, on 30 May 2010 - 08:30 PM, said:

:) Thanks it looks very nice..

Thank you for your feedback :)

Stay tuned for the next release coming soon including:
- possiblity to define a validation scenario for relations
- onDelete handling
0

#5 User is offline   mech7 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 220
  • Joined: 26-March 09

Posted 08 June 2010 - 04:11 AM

I am having a problem setting it up.. I tried the demo and there it works fine so I must be doing something wrong..
I keep getting:

Quote

PHP Error
Description

Undefined offset: 0
Source File

/var/www/html/flipbook/framework/db/schema/CDbCommandBuilder.php(633)


It always throws it after:
$relobj = $relModel->findByPk($criteria);

on line 151:

criteria looks like this:
array(1) {
["id"]=>
string(1) "3"
}




MY controller:

$pages = $_POST['Pages'];
            $model->setRelationRecords('pages',is_array(@$_POST['Pages']) ? $_POST['Pages'] : array());
            $model->save();


Model:

public function relations() {
        // NOTE: you may need to adjust the relation name and the related
        // class name for the relations automatically generated below.
        return array(
            'pages' => array(self::HAS_MANY, 'Pages', 'book_id',
                'order' => 'pages.page_id ASC',
            ),
            'user' => array(self::BELONGS_TO, 'User', 'user_id'),
            'comments' => array(self::HAS_MANY, 'Comments', 'book_id',
                'order' => 'comments.created DESC',
            ),
            'commentCount' => array(self::STAT, 'Comments', 'book_id',
                'condition'=> 'status='.Comments::STATUS_APPROVED),
        );
    }


and view:

<?php echo CHtml::beginForm(null, 'POST', array()); ?>
    <div class="pages clear">
        <?php foreach($model->pages as $idx => $page):?>
                <div class="page">
                    <label>Title page: <?php echo $idx + 1;?></label>
                    <?php echo CHtml::activeTextField($page, "[$idx]title"); ?><br />
                    <?php echo CHtml::error($page,"title"); ?>
                    <?php echo CHtml::image( Yii::app()->request->baseUrl.'/'.$page->image, 'page');?>
                </div>
        <?php endforeach;?>
    </div>
    <div class="clear"></div>
    <?php echo CHtml::submitButton();?>
<?php echo CHtml::endForm();?>


Any idea what could cause this error ? :(
0

#6 User is offline   Juban 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 12-January 09
  • Location:Grenoble France

Posted 08 June 2010 - 12:33 PM

Hi,

I'm trying to reproduce that and will come back to you as soon as possible.

Regards.

Alban.
0

#7 User is offline   Juban 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 12-January 09
  • Location:Grenoble France

Posted 08 June 2010 - 02:31 PM

OK. My guess is that the "id" field in your Pages table is not set as a Primary Key.
Could you confirm?
0

#8 User is offline   Juban 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 12-January 09
  • Location:Grenoble France

Posted 12 June 2010 - 09:44 AM

View Postmech7, on 08 June 2010 - 04:11 AM, said:

Any idea what could cause this error ? :(


Hi,

I have updated the behavior (1.0.2).
Could you try it and let me know if the issue still occurs?

Regards.

Alban.
0

#9 User is offline   Juban 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 12-January 09
  • Location:Grenoble France

Posted 19 June 2010 - 05:15 AM

Hi,

A new version of the behavior (1.0.3) is available for download that should deal better with HAS_MANY relations.
Please, download the enhanced demonstration application to see how to deal with related primary keys (look at the comments exemple in the library form).

Regards.

Alban.
0

#10 User is offline   andr2k 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 8
  • Joined: 01-July 10

Posted 01 July 2010 - 06:06 PM

Hi!

I've modified it to support more complicated models.

Please let me know whether you changed my code or not IF you accept my changes.

<?php
/**

 * http://www.yiiframework.com/extension/save-relations-ar-behavior/

 * CSaveRelationsBehavior class file.
 *
 * @author Alban Jubert <alban.jubert@trinidev.fr>
 * @link http://www.trinidev.fr/
 * @version 1.0.3
 */

/*
 * The CSaveRelationBehavior enables ActiveRecord model to save HasMany and ManyMany 
 * relational active records along with the main model
 * 
 * Requirements:
 * Yii Framework 1.0.4 or later
 * 
 * Installation:
 * Extract the release file under `protected/components`
 * 
 * Usage:
 * - Add the following code to the models you wish to add this behavior to:
 * public function behaviors(){
 * 		return array('CSaveRelationsBehavior' => array('class' => 'application.components.CSaveRelationsBehavior'));
 * }
 * In your controler, to save the relations data, simply call
 * $model->setRelationRecords('relationName',$data);
 * $model->save();
 * - For ManyMany relations, $data is either an array of foreign key values (ie. array(2,5,43)) or
 * an array of associative arrays giving the composite foreign keys values of the related model
 * (ie. array(array('pk1'=>2,'pk2'=>'fr'),array('pk1'=>5,'pk2'=>'en'))
 * You will typically get this data from some checkboxes values listing the ids of the related model
 * - For HasMany relations, $data should be set as an array of associative arrays giving the attributes values
 * of the related model (ie. array(array('id'=>123, 'name'=>'someValue', 'visible'=>true),
 * array('id'=>456, 'name'=>'someOtherValue', 'visible'=>false));
 * You can get this data by using the tabular input technique within the form of the main model 
 * (http://www.yiiframework.com/doc/guide/form.table)
 * 
 * In both cases, the foreign keys related to the main model will automatically be populated
 * with its primary key(s) value(s).
 * Most of the time, you will call the setRelationRecords that way:
 * $model->setRelationRecords('relationName',is_array(@$_POST['ModelName']) ? $_POST['ModelName'] : array());
 * 
 * By default, the behavior will handle the save operation in a transactional way
 * so that if anything goes wrong during the save of some related data
 * your relational integrity will not be affected.
 * If you prefer to handle this yourself, you can set the 'transactional' property of the behavior to false.
 * Also, if any error occurs during the save process, the hasError property will be set to true.
 * 
 * Additional features:
 * - $model->addSaveRelation('relationName'[,'customErrorMessage'])
 * You can use this method to force the save of some relation. 
 * You can also set the error message of the relation by using the second parameter (see setSaveRelationMessage below)
 * - $model->removeSaveRelation('relationName')
 * Simply do the oposite
 * - $model->setSaveRelationMessage('relationName','customErrorMessage')
 * Set the message to be shown in the error summary of the main model
 */

class CSaveRelationsBehavior extends CActiveRecordBehavior {
	
	public $relations = array();
	public $transactional = true;
	public $hasError = false;
	public $deleteRelatedRecords = true;
	private $transaction;
	
	private function initSaveRelation($relation){
		$model = $this->owner;
		if(!array_key_exists($relation,$model->relations())) 
			throw new CDbException('CSaveRelatedBehavior could not find the "'.$relation.'" relation in the model.');
		if(!array_key_exists($relation,$this->relations)) {
			Yii::trace("Init {$relation} relation",'application.components.CSaveRelatedBehavior');
			$this->relations[$relation]=array();
		}
	}
	
	public function setRelationRecords($relation,$data=null,$merge=false) {
		// TODO - Make fewer SQL requests to validate and load related models data
		$this->addSaveRelation($relation);
		$model = $this->owner;
		$activeRelation = $model->getActiveRelation($relation);
		if($activeRelation instanceOf CHasManyRelation || $activeRelation instanceOf CManyManyRelation) {
			if(!$merge) $model->{$relation} = array();
			$relationClassName = $activeRelation->className;
			$relationForeignKey = $activeRelation->foreignKey;
			$criteria = array();
			if($activeRelation instanceOf CManyManyRelation) {
				$schema = $model->getCommandBuilder()->getSchema();
				preg_match('/^\s*(.*?)\((.*)\)\s*$/',$relationForeignKey,$matches);
				$joinTable=$schema->getTable($matches[1]);
				$fks=preg_split('/[\s,]+/',$matches[2],-1,PREG_SPLIT_NO_EMPTY);
				$relModel = new $relationClassName;
				$pks = array();
				$fkDefined=true;
				foreach($fks as $i=>$fk) {
					if(isset($joinTable->foreignKeys[$fk])) {
						list($tableName,$pk)=$joinTable->foreignKeys[$fk];
						if($schema->compareTableNames($relModel->tableSchema->rawName,$tableName)) {
							$pks[] = $pk;
						}
					}
					else {
						$fkDefined=false;
						break;
					}
				}
				if(!$fkDefined) {
					$pks = array();
					foreach($fks as $i=>$fk)
					{
						if($i<count($model->tableSchema->primaryKey))
						{
							$pks[] = is_array($model->tableSchema->primaryKey) ? $model->tableSchema->primaryKey[$i] : $model->tableSchema->primaryKey;
						}
					}
				}
				if(!is_null($data)) {
					foreach($data as $key=>$value) {
						$relobj = null;
						$relModel = new $relationClassName;
						if(is_array($value)) {
							foreach($pks as $pk) {
								$criteria[$pk] = $value[$pk];
							}
						}
						else {
							$criteria[$pks[0]] = $value;
						}
						$relobj = $relModel->findByAttributes($criteria);
						if(!($relobj instanceof $relationClassName)) $relobj = new $relationClassName;
						$relobj->attributes = $value;
						$model->addRelatedRecord($relation,$relobj,$key);
					}
				}
			}
			else 
			{
				$fks=preg_split('/[\s,]+/',$relationForeignKey,-1,PREG_SPLIT_NO_EMPTY);
				if(!is_null($data)) {
					foreach($data as $key=>$value) {
						$relobj = null;
						if(!$model->isNewRecord) {
							$criteria = array();
							$relModel = new $relationClassName;
							$relationPrimaryKeys = $relModel->tableSchema->primaryKey;
							if(is_array($value)) {
								if(is_array($relationPrimaryKeys)) {
									foreach($relationPrimaryKeys as $relationPrimaryKey){
										if(!in_array($relationPrimaryKey,$fks)) {
											if(isset($value[$relationPrimaryKey])) $criteria[$relationPrimaryKey] = $value[$relationPrimaryKey];
										}
										else {
											$criteria[$relationPrimaryKey] = $model->primaryKey;
										}
									}
								}
								else{
									if(!in_array($relationPrimaryKeys,$fks)) {
										if(isset($value[$relationPrimaryKeys])) $criteria[$relationPrimaryKeys] = $value[$relationPrimaryKeys];
									}
									else {
										$criteria[$relationPrimaryKeys] = $model->primaryKey;
									}
								}
							}
							else {
								$criteria = array($relationPrimaryKeys=>$value);
							}
							if(count($criteria)) $relobj = $relModel->findByAttributes($criteria);
						}
						if(!($relobj instanceof $relationClassName)) $relobj = new $relationClassName;
						foreach($value as $prop=>$val) $relobj->{$prop} = $val;
						$model->addRelatedRecord($relation,$relobj,$key);
					}
				}
			}
		}
	}
	
	public function addSaveRelation($relation,$message=null){
		$this->initSaveRelation($relation);
		$this->relations[$relation] = CMap::mergeArray($this->relations[$relation],array('save'=>true));
		if(!is_null($message)) $this->setSaveRelationMessage($relation,$message);
	}
	
	public function removeSaveRelation($relation){
		$model = $this->owner;
		if(!array_key_exists($relation,$model->relations())) 
			throw new CDbException('CSaveRelatedBehavior could not find the "'.$relation.'" relation in the model.');
		if(array_key_exists($relation,$this->relations)) {
			Yii::trace("Removing {$relation} relation to save",'application.components.CSaveRelatedBehavior');
			$this->relations[$relation] = CMap::mergeArray($this->relations[$relation],array('save'=>false));
		}
	}
	
	public function setRelationScenario($relation,$scenario){
		$this->initSaveRelation($relation);
		$this->relations[$relation] = CMap::mergeArray($this->relations[$relation],array('scenario'=>$scenario));	
	}
	
	public function setSaveRelationMessage($relation,$message) {
		$this->initSaveRelation($relation);
		$this->relations[$relation] = CMap::mergeArray($this->relations[$relation],array('message'=>$message));
	}
	
	public function beforeValidate($event) {
		$model = $this->owner;
		foreach($this->relations as $relation=>$params) {
			if(isset($params['save']) && $params['save']==true) {
				$activeRelation = $model->getActiveRelation($relation);
				$validRelation = true;
				if(!($activeRelation instanceOf CManyManyRelation)) {
					foreach($model->{$relation} as $relatedRecord) {
						if(isset($params['scenario'])) $relatedRecord->scenario = $params['scenario'];
						$validRelation = $validRelation && $relatedRecord->validate();
					}
					if(!$validRelation) 
						$model->addError($relation,isset($params['message']) ? $params['message'] : "An error occured during the save of {$relation}");				
				}
				$this->relations[$relation]['valid'] = $validRelation;
			}
		}
	}
	
	public function beforeSave($event) {
		$model = $this->owner;
		$valid =  true;
		foreach($this->relations as $relation=>$params) {
			if(isset($params['save']) && $params['save']==true) {
				$valid = $valid && $this->relations[$relation]['valid'];
			}
		}
		if($valid && $this->transactional && !$model->dbConnection->currentTransaction) {
			Yii::trace("beforeSave start transaction",'application.components.CSaveRelatedBehavior');
			$this->transaction=$model->dbConnection->beginTransaction();
		}
		$event->isValid = $valid;
	}
	
	public function afterSave($event) {
		$model = $this->owner;
		try{
			foreach($this->relations as $relation=>$params) {
				if(isset($params['save']) && $params['save']==true) {
					Yii::trace("saving {$relation} related records.",'application.components.CSaveRelatedBehavior');
					$activeRelation = $model->getActiveRelation($relation);
					$relationClassName = $activeRelation->className;
					$relationForeignKey = $activeRelation->foreignKey;
					$keysToKeep = array();
					if($activeRelation instanceOf CManyManyRelation) {
						// ManyMany relation : save relation to the many to many relation table
						$schema = $model->getCommandBuilder()->getSchema();
						preg_match('/^\s*(.*?)\((.*)\)\s*$/',$relationForeignKey,$matches);
						$joinTable=$schema->getTable($matches[1]);
						$fks=preg_split('/[\s,]+/',$matches[2],-1,PREG_SPLIT_NO_EMPTY);
						$fksFieldNames = array();
						$fksParamNames = array();
						foreach($fks as $fk) {
							$fksFieldNames[] = $schema->quoteColumnName($fk);
							$fksParamNames[] = ':'.$fk;
						}
						$sql="INSERT IGNORE INTO ".$joinTable->rawName." (".implode(', ',$fksFieldNames).") VALUES(".implode(', ',$fksParamNames).")";
						$baseParams = array();
						$baseCriteriaCondition = array();
						reset($fks);
						foreach($fks as $i=>$fk) {
							if(isset($joinTable->foreignKeys[$fk])) {
								list($tableName,$pk)=$joinTable->foreignKeys[$fk];
								if($schema->compareTableNames($model->tableSchema->rawName,$tableName)) {
									$baseCriteriaCondition[$fk] = $baseParams[':'.$fk] = $model->{$pk};
								}
							}
						}
						$relModel = new $relationClassName;
						foreach($model->{$relation} as $idx=>$relatedRecord) {
							$relParams = array();
							reset($fks);
							foreach($fks as $i=>$fk) {
								if(isset($joinTable->foreignKeys[$fk])) {
									list($tableName,$pk)=$joinTable->foreignKeys[$fk];
									if($schema->compareTableNames($relModel->tableSchema->rawName,$tableName)) {
										$keysToKeep[$fk][] = $relParams[':'.$fk] = $relatedRecord->{$pk};
									}
								}
							}
							$model->getCommandBuilder()->createSqlCommand($sql,$baseParams+$relParams)->execute();
						}
						// Delete removed records
						$criteria = new CDbCriteria;
						$criteria->addColumnCondition($baseCriteriaCondition);
						foreach($keysToKeep as $fk=>$values)
							$criteria->addInCondition($fk,$values,'AND NOT');
						$model->getCommandBuilder()->createDeleteCommand($joinTable->name,$criteria)->execute();
					}
					else {
						// HasMany relation : save related models
						foreach($model->{$relation} as $relatedRecord) {
							if($relatedRecord->isNewRecord) {
								if(is_array($relationForeignKey)) {
									foreach($relationForeignKey as $fk) {
										$relatedRecord->{$fk} = $model->primaryKey[$fk];
									}
								}
								else {
									$relatedRecord->{$relationForeignKey} = $model->primaryKey;
								}
							}
							if($relatedRecord->save()) {
								$relationPrimaryKeys = $relatedRecord->tableSchema->primaryKey;
								if(is_array($relationPrimaryKeys)) {
									foreach($relationPrimaryKeys as $relationPrimaryKey){
										if($relationPrimaryKey!=$relationForeignKey) $keysToKeep[$relationPrimaryKey][] = $relatedRecord->{$relationPrimaryKey};
									}
								}
								else{
									$keysToKeep[$relationPrimaryKeys][] = $relatedRecord->{$relationPrimaryKeys};
								}
							}
							else {
								throw new CException("Invalid related record");
							}
						}


						

/*						foreach($keysToKeep as $key)
                                                {
                                                    $relatedRecord = new $relationClassName;
                                                    $criteria = new CDbCriteria;
                                                    $criteria->addColumnCondition(array($relationForeignKey=>$model->primaryKey));
                                                    foreach ($key as $fk=>$value)
                                                    {
                                                        $criteria->addInCondition($fk,array($value), 'AND NOT');
                                                    }
                                                    //$criteria->addColumnCondition($key, 'AND', 'AND NOT');
                                                    Yii::log('123'.print_r($criteria->toArray(), true),'error');
                                                    $relatedRecord->deleteAll($criteria);
                                                }
*/



						$relatedRecord = new $relationClassName;
						$criteria = new CDbCriteria;
						$criteria->addColumnCondition(array($relationForeignKey=>$model->primaryKey));
						if (count($keysToKeep)===1)
						{
							foreach ( $keysToKeep as $fk => $values )
								$criteria->addInCondition ( $fk, $values, 'AND NOT' );
						} else {
							$_keysToKeep = array();
							foreach ( $keysToKeep as $field=>$fValues )
							{
								foreach ( $fValues as $key=>$value )
								{
									$_keysToKeep[$key][$field] = $value;
								}								
							}
							
							foreach($_keysToKeep as $record)
							{
								$criteria->addColumnCondition ($record, 'AND', 'AND NOT' );
							}
						}
						
						$relatedRecord->deleteAll ( $criteria );


					}
				}
			}
			unset($relation);
			if($this->transactional && $this->transaction) $this->transaction->commit();
		}
		catch(Exception $e)
		{
			Yii::trace("An error occured during the save operation for related records : ".$e->getMessage(),'application.components.CSaveRelatedBehavior');
			$this->hasError = true;
			if(isset($relation)) $model->addError($relation,isset($this->relations[$relation]['message']) ? $this->relations[$relation]['message'] : "An error occured during the save of {$relation}");
			if($this->transactional && $this->transaction) $this->transaction->rollBack();
		}
	}
	
	public function beforeDelete($event) {
		$model = $this->owner;
		if($this->transactional && !$model->dbConnection->currentTransaction) {
			Yii::trace("beforeDelete start transaction",'application.components.CSaveRelatedBehavior');
			$this->transaction=$model->dbConnection->beginTransaction();
		}
	}
	
	public function afterDelete($event) {
		if($this->deleteRelatedRecords) {
			$model = $this->owner;
			try{
				foreach($model->relations() as $relation=>$params) {
					$activeRelation = $model->getActiveRelation($relation);
					if(is_object($activeRelation) && ($activeRelation instanceOf CManyManyRelation || $activeRelation instanceOf CHasManyRelation || $activeRelation instanceOf CHasOneRelation)) {
						Yii::trace("deleting {$relation} related records.",'application.components.CSaveRelatedBehavior');
						$relationClassName = $activeRelation->className;
						$relationForeignKey = $activeRelation->foreignKey;
						if($activeRelation instanceOf CManyManyRelation) {
							// ManyMany relation : delete related records from the many to many relation table
							$schema = $model->getCommandBuilder()->getSchema();
							preg_match('/^\s*(.*?)\((.*)\)\s*$/',$relationForeignKey,$matches);
							$joinTable=$schema->getTable($matches[1]);
							$fks=preg_split('/[\s,]+/',$matches[2],-1,PREG_SPLIT_NO_EMPTY);
							$baseParams = array();
							$baseCriteriaCondition = array();
							reset($fks);
							foreach($fks as $i=>$fk) {
								if(isset($joinTable->foreignKeys[$fk])) {
									list($tableName,$pk)=$joinTable->foreignKeys[$fk];
									if($schema->compareTableNames($model->tableSchema->rawName,$tableName)) {
										$baseCriteriaCondition[$fk] = $baseParams[':'.$fk] = $model->{$pk};
									}
								}
							}
							// Delete records
							$criteria = new CDbCriteria;
							$criteria->addColumnCondition($baseCriteriaCondition);
							$model->getCommandBuilder()->createDeleteCommand($joinTable->name,$criteria)->execute();
						}
						else {
							// HasMany & HasOne relation : delete related records
							$relatedRecord = new $relationClassName;
							$criteria = new CDbCriteria;
							$criteria->addColumnCondition(array($relationForeignKey=>$model->primaryKey));
							$relatedRecord->deleteAll($criteria);
						}
					}
				}
				unset($relation);
				if($this->transactional && $this->transaction) $this->transaction->commit();
			}
			catch(Exception $e)
			{
				Yii::trace("An error occured during the delete operation for related records : ".$e->getMessage(),'application.components.CSaveRelatedBehavior');
				$this->hasError = true;
				if(isset($relation)) $model->addError($relation,"An error occured during the delete operation of {$relation}");
				if($this->transactional && $this->transaction) $this->transaction->rollBack();
			}
		}
	}
}

0

#11 User is offline   mech7 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 220
  • Joined: 26-March 09

Posted 02 July 2010 - 02:16 AM

Is there anyway to just delete one relation? I could not really find it.. other then unsetting the relation array.

public function deleteBook($idx) {
		$libraryBookArray = $this->libraryBooks;
		unset($libraryBookArray[$idx]);
		$this->libraryBooks = $libraryBookArray;
}


Also how does this exactly work? :)
0

#12 User is offline   andr2k 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 8
  • Joined: 01-July 10

Posted 02 July 2010 - 09:27 AM

Modified it a liitle bit more:
-added back-quotes to column names

<?php
/**

 * http://www.yiiframework.com/extension/save-relations-ar-behavior/

 * CSaveRelationsBehavior class file.
 *
 * @author Alban Jubert <alban.jubert@trinidev.fr>
 * @link http://www.trinidev.fr/
 * @version 1.0.3
 */

/*
 * The CSaveRelationBehavior enables ActiveRecord model to save HasMany and ManyMany 
 * relational active records along with the main model
 * 
 * Requirements:
 * Yii Framework 1.0.4 or later
 * 
 * Installation:
 * Extract the release file under `protected/components`
 * 
 * Usage:
 * - Add the following code to the models you wish to add this behavior to:
 * public function behaviors(){
 * 		return array('CSaveRelationsBehavior' => array('class' => 'application.components.CSaveRelationsBehavior'));
 * }
 * In your controler, to save the relations data, simply call
 * $model->setRelationRecords('relationName',$data);
 * $model->save();
 * - For ManyMany relations, $data is either an array of foreign key values (ie. array(2,5,43)) or
 * an array of associative arrays giving the composite foreign keys values of the related model
 * (ie. array(array('pk1'=>2,'pk2'=>'fr'),array('pk1'=>5,'pk2'=>'en'))
 * You will typically get this data from some checkboxes values listing the ids of the related model
 * - For HasMany relations, $data should be set as an array of associative arrays giving the attributes values
 * of the related model (ie. array(array('id'=>123, 'name'=>'someValue', 'visible'=>true),
 * array('id'=>456, 'name'=>'someOtherValue', 'visible'=>false));
 * You can get this data by using the tabular input technique within the form of the main model 
 * (http://www.yiiframework.com/doc/guide/form.table)
 * 
 * In both cases, the foreign keys related to the main model will automatically be populated
 * with its primary key(s) value(s).
 * Most of the time, you will call the setRelationRecords that way:
 * $model->setRelationRecords('relationName',is_array(@$_POST['ModelName']) ? $_POST['ModelName'] : array());
 * 
 * By default, the behavior will handle the save operation in a transactional way
 * so that if anything goes wrong during the save of some related data
 * your relational integrity will not be affected.
 * If you prefer to handle this yourself, you can set the 'transactional' property of the behavior to false.
 * Also, if any error occurs during the save process, the hasError property will be set to true.
 * 
 * Additional features:
 * - $model->addSaveRelation('relationName'[,'customErrorMessage'])
 * You can use this method to force the save of some relation. 
 * You can also set the error message of the relation by using the second parameter (see setSaveRelationMessage below)
 * - $model->removeSaveRelation('relationName')
 * Simply do the oposite
 * - $model->setSaveRelationMessage('relationName','customErrorMessage')
 * Set the message to be shown in the error summary of the main model
 */

class CSaveRelationsBehavior extends CActiveRecordBehavior {
	
	public $relations = array();
	public $transactional = true;
	public $hasError = false;
	public $deleteRelatedRecords = true;
	private $transaction;
	
	private function initSaveRelation($relation){
		$model = $this->owner;
		if(!array_key_exists($relation,$model->relations())) 
			throw new CDbException('CSaveRelatedBehavior could not find the "'.$relation.'" relation in the model.');
		if(!array_key_exists($relation,$this->relations)) {
			Yii::trace("Init {$relation} relation",'application.components.CSaveRelatedBehavior');
			$this->relations[$relation]=array();
		}
	}
	
	public function setRelationRecords($relation,$data=null,$merge=false) {
		// TODO - Make fewer SQL requests to validate and load related models data
		$this->addSaveRelation($relation);
		$model = $this->owner;
		$activeRelation = $model->getActiveRelation($relation);
		if($activeRelation instanceOf CHasManyRelation || $activeRelation instanceOf CManyManyRelation) {
			if(!$merge) $model->{$relation} = array();
			$relationClassName = $activeRelation->className;
			$relationForeignKey = $activeRelation->foreignKey;
			$criteria = array();
			if($activeRelation instanceOf CManyManyRelation) {
				$schema = $model->getCommandBuilder()->getSchema();
				preg_match('/^\s*(.*?)\((.*)\)\s*$/',$relationForeignKey,$matches);
				$joinTable=$schema->getTable($matches[1]);
				$fks=preg_split('/[\s,]+/',$matches[2],-1,PREG_SPLIT_NO_EMPTY);
				$relModel = new $relationClassName;
				$pks = array();
				$fkDefined=true;
				foreach($fks as $i=>$fk) {
					if(isset($joinTable->foreignKeys[$fk])) {
						list($tableName,$pk)=$joinTable->foreignKeys[$fk];
						if($schema->compareTableNames($relModel->tableSchema->rawName,$tableName)) {
							$pks[] = $pk;
						}
					}
					else {
						$fkDefined=false;
						break;
					}
				}
				if(!$fkDefined) {
					$pks = array();
					foreach($fks as $i=>$fk)
					{
						if($i<count($model->tableSchema->primaryKey))
						{
							$pks[] = is_array($model->tableSchema->primaryKey) ? $model->tableSchema->primaryKey[$i] : $model->tableSchema->primaryKey;
						}
					}
				}
				if(!is_null($data)) {
					foreach($data as $key=>$value) {
						$relobj = null;
						$relModel = new $relationClassName;
						if(is_array($value)) {
							foreach($pks as $pk) {
								$criteria[$pk] = $value[$pk];
							}
						}
						else {
							$criteria[$pks[0]] = $value;
						}
						$relobj = $relModel->findByAttributes($criteria);
						if(!($relobj instanceof $relationClassName)) $relobj = new $relationClassName;
						$relobj->attributes = $value;
						$model->addRelatedRecord($relation,$relobj,$key);
					}
				}
			}
			else 
			{
				$fks=preg_split('/[\s,]+/',$relationForeignKey,-1,PREG_SPLIT_NO_EMPTY);
				if(!is_null($data)) {
					foreach($data as $key=>$value) {
						$relobj = null;
						if(!$model->isNewRecord) {
							$criteria = array();
							$relModel = new $relationClassName;
							$relationPrimaryKeys = $relModel->tableSchema->primaryKey;
							if(is_array($value)) {
								if(is_array($relationPrimaryKeys)) {
									foreach($relationPrimaryKeys as $relationPrimaryKey){
										if(!in_array($relationPrimaryKey,$fks)) {
											if(isset($value[$relationPrimaryKey])) $criteria[$relationPrimaryKey] = $value[$relationPrimaryKey];
										}
										else {
											$criteria[$relationPrimaryKey] = $model->primaryKey;
										}
									}
								}
								else{
									if(!in_array($relationPrimaryKeys,$fks)) {
										if(isset($value[$relationPrimaryKeys])) $criteria[$relationPrimaryKeys] = $value[$relationPrimaryKeys];
									}
									else {
										$criteria[$relationPrimaryKeys] = $model->primaryKey;
									}
								}
							}
							else {
								$criteria = array($relationPrimaryKeys=>$value);
							}
							if(count($criteria)) $relobj = $relModel->findByAttributes($criteria);
						}
						if(!($relobj instanceof $relationClassName)) $relobj = new $relationClassName;
						foreach($value as $prop=>$val) $relobj->{$prop} = $val;
						$model->addRelatedRecord($relation,$relobj,$key);
					}
				}
			}
		}
	}
	
	public function addSaveRelation($relation,$message=null){
		$this->initSaveRelation($relation);
		$this->relations[$relation] = CMap::mergeArray($this->relations[$relation],array('save'=>true));
		if(!is_null($message)) $this->setSaveRelationMessage($relation,$message);
	}
	
	public function removeSaveRelation($relation){
		$model = $this->owner;
		if(!array_key_exists($relation,$model->relations())) 
			throw new CDbException('CSaveRelatedBehavior could not find the "'.$relation.'" relation in the model.');
		if(array_key_exists($relation,$this->relations)) {
			Yii::trace("Removing {$relation} relation to save",'application.components.CSaveRelatedBehavior');
			$this->relations[$relation] = CMap::mergeArray($this->relations[$relation],array('save'=>false));
		}
	}
	
	public function setRelationScenario($relation,$scenario){
		$this->initSaveRelation($relation);
		$this->relations[$relation] = CMap::mergeArray($this->relations[$relation],array('scenario'=>$scenario));	
	}
	
	public function setSaveRelationMessage($relation,$message) {
		$this->initSaveRelation($relation);
		$this->relations[$relation] = CMap::mergeArray($this->relations[$relation],array('message'=>$message));
	}
	
	public function beforeValidate($event) {
		$model = $this->owner;
		foreach($this->relations as $relation=>$params) {
			if(isset($params['save']) && $params['save']==true) {
				$activeRelation = $model->getActiveRelation($relation);
				$validRelation = true;
				if(!($activeRelation instanceOf CManyManyRelation)) {
					foreach($model->{$relation} as $relatedRecord) {
						if(isset($params['scenario'])) $relatedRecord->scenario = $params['scenario'];
						$validRelation = $validRelation && $relatedRecord->validate();
					}
					if(!$validRelation) 
						$model->addError($relation,isset($params['message']) ? $params['message'] : "An error occured during the save of {$relation}");				
				}
				$this->relations[$relation]['valid'] = $validRelation;
			}
		}
	}
	
	public function beforeSave($event) {
		$model = $this->owner;
		$valid =  true;
		foreach($this->relations as $relation=>$params) {
			if(isset($params['save']) && $params['save']==true) {
				$valid = $valid && $this->relations[$relation]['valid'];
			}
		}
		if($valid && $this->transactional && !$model->dbConnection->currentTransaction) {
			Yii::trace("beforeSave start transaction",'application.components.CSaveRelatedBehavior');
			$this->transaction=$model->dbConnection->beginTransaction();
		}
		$event->isValid = $valid;
	}
	
	public function afterSave($event) {
		$model = $this->owner;
		try{
			foreach($this->relations as $relation=>$params) {
				if(isset($params['save']) && $params['save']==true) {
					Yii::trace("saving {$relation} related records.",'application.components.CSaveRelatedBehavior');
					$activeRelation = $model->getActiveRelation($relation);
					$relationClassName = $activeRelation->className;
					$relationForeignKey = $activeRelation->foreignKey;
					$keysToKeep = array();
					if($activeRelation instanceOf CManyManyRelation) {
						// ManyMany relation : save relation to the many to many relation table
						$schema = $model->getCommandBuilder()->getSchema();
						preg_match('/^\s*(.*?)\((.*)\)\s*$/',$relationForeignKey,$matches);
						$joinTable=$schema->getTable($matches[1]);
						$fks=preg_split('/[\s,]+/',$matches[2],-1,PREG_SPLIT_NO_EMPTY);
						$fksFieldNames = array();
						$fksParamNames = array();
						foreach($fks as $fk) {
							$fksFieldNames[] = $schema->quoteColumnName($fk);
							$fksParamNames[] = ':'.$fk;
						}
						$sql="INSERT IGNORE INTO ".$joinTable->rawName." (".implode(', ',$fksFieldNames).") VALUES(".implode(', ',$fksParamNames).")";
						$baseParams = array();
						$baseCriteriaCondition = array();
						reset($fks);
						foreach($fks as $i=>$fk) {
							if(isset($joinTable->foreignKeys[$fk])) {
								list($tableName,$pk)=$joinTable->foreignKeys[$fk];
								if($schema->compareTableNames($model->tableSchema->rawName,$tableName)) {
									$baseCriteriaCondition["`$fk`"] = $baseParams[':'.$fk] = $model->{$pk};
								}
							}
						}
						$relModel = new $relationClassName;
						foreach($model->{$relation} as $idx=>$relatedRecord) {
							$relParams = array();
							reset($fks);
							foreach($fks as $i=>$fk) {
								if(isset($joinTable->foreignKeys[$fk])) {
									list($tableName,$pk)=$joinTable->foreignKeys[$fk];
									if($schema->compareTableNames($relModel->tableSchema->rawName,$tableName)) {
										$keysToKeep[$fk][] = $relParams[':'.$fk] = $relatedRecord->{$pk};
									}
								}
							}
							$model->getCommandBuilder()->createSqlCommand($sql,$baseParams+$relParams)->execute();
						}
						// Delete removed records
						$criteria = new CDbCriteria;
						$criteria->addColumnCondition($baseCriteriaCondition);
						foreach($keysToKeep as $fk=>$values)
							$criteria->addInCondition("`$fk`",$values,'AND NOT');
						$model->getCommandBuilder()->createDeleteCommand($joinTable->name,$criteria)->execute();
					}
					else {
						// HasMany relation : save related models
						foreach($model->{$relation} as $relatedRecord) {
							if($relatedRecord->isNewRecord) {
								if(is_array($relationForeignKey)) {
									foreach($relationForeignKey as $fk) {
										$relatedRecord->{$fk} = $model->primaryKey[$fk];
									}
								}
								else {
									$relatedRecord->{$relationForeignKey} = $model->primaryKey;
								}
							}
							if($relatedRecord->save()) {
								$relationPrimaryKeys = $relatedRecord->tableSchema->primaryKey;
								if(is_array($relationPrimaryKeys)) {
									foreach($relationPrimaryKeys as $relationPrimaryKey){
										if($relationPrimaryKey!=$relationForeignKey) $keysToKeep[$relationPrimaryKey][] = $relatedRecord->{$relationPrimaryKey};
									}
								}
								else{
									$keysToKeep[$relationPrimaryKeys][] = $relatedRecord->{$relationPrimaryKeys};
								}
							}
							else {
								throw new CException("Invalid related record");
							}
						}


						$relatedRecord = new $relationClassName;
						$criteria = new CDbCriteria;
						$criteria->addColumnCondition(array("`$relationForeignKey`"=>$model->primaryKey));
						if (count($keysToKeep)===1)
						{
							foreach ( $keysToKeep as $fk => $values )
								$criteria->addInCondition ( "`$fk`", $values, 'AND NOT' );
						} else {
							$_keysToKeep = array();
							foreach ( $keysToKeep as $field=>$fValues )
							{
								foreach ( $fValues as $key=>$value )
								{
									$_keysToKeep[$key]["`$field`"] = $value;
								}								
							}
							
							foreach($_keysToKeep as $record)
							{
								$criteria->addColumnCondition ($record, 'AND', 'AND NOT' );
							}
						}
						
						$relatedRecord->deleteAll ( $criteria );


					}
				}
			}
			unset($relation);
			if($this->transactional && $this->transaction) $this->transaction->commit();
		}
		catch(Exception $e)
		{
			Yii::trace("An error occured during the save operation for related records : ".$e->getMessage(),'application.components.CSaveRelatedBehavior');
			$this->hasError = true;
			if(isset($relation)) $model->addError($relation,isset($this->relations[$relation]['message']) ? $this->relations[$relation]['message'] : "An error occured during the save of {$relation}");
			if($this->transactional && $this->transaction) $this->transaction->rollBack();
		}
	}
	
	public function beforeDelete($event) {
		$model = $this->owner;
		if($this->transactional && !$model->dbConnection->currentTransaction) {
			Yii::trace("beforeDelete start transaction",'application.components.CSaveRelatedBehavior');
			$this->transaction=$model->dbConnection->beginTransaction();
		}
	}
	
	public function afterDelete($event) {
		if($this->deleteRelatedRecords) {
			$model = $this->owner;
			try{
				foreach($model->relations() as $relation=>$params) {
					$activeRelation = $model->getActiveRelation($relation);
					if(is_object($activeRelation) && ($activeRelation instanceOf CManyManyRelation || $activeRelation instanceOf CHasManyRelation || $activeRelation instanceOf CHasOneRelation)) {
						Yii::trace("deleting {$relation} related records.",'application.components.CSaveRelatedBehavior');
						$relationClassName = $activeRelation->className;
						$relationForeignKey = $activeRelation->foreignKey;
						if($activeRelation instanceOf CManyManyRelation) {
							// ManyMany relation : delete related records from the many to many relation table
							$schema = $model->getCommandBuilder()->getSchema();
							preg_match('/^\s*(.*?)\((.*)\)\s*$/',$relationForeignKey,$matches);
							$joinTable=$schema->getTable($matches[1]);
							$fks=preg_split('/[\s,]+/',$matches[2],-1,PREG_SPLIT_NO_EMPTY);
							$baseParams = array();
							$baseCriteriaCondition = array();
							reset($fks);
							foreach($fks as $i=>$fk) {
								if(isset($joinTable->foreignKeys[$fk])) {
									list($tableName,$pk)=$joinTable->foreignKeys[$fk];
									if($schema->compareTableNames($model->tableSchema->rawName,$tableName)) {
										$baseCriteriaCondition[$fk] = $baseParams[':'.$fk] = $model->{$pk};
									}
								}
							}
							// Delete records
							$criteria = new CDbCriteria;
							$criteria->addColumnCondition($baseCriteriaCondition);
							$model->getCommandBuilder()->createDeleteCommand($joinTable->name,$criteria)->execute();
						}
						else {
							// HasMany & HasOne relation : delete related records
							$relatedRecord = new $relationClassName;
							$criteria = new CDbCriteria;
							$criteria->addColumnCondition(array($relationForeignKey=>$model->primaryKey));
							$relatedRecord->deleteAll($criteria);
						}
					}
				}
				unset($relation);
				if($this->transactional && $this->transaction) $this->transaction->commit();
			}
			catch(Exception $e)
			{
				Yii::trace("An error occured during the delete operation for related records : ".$e->getMessage(),'application.components.CSaveRelatedBehavior');
				$this->hasError = true;
				if(isset($relation)) $model->addError($relation,"An error occured during the delete operation of {$relation}");
				if($this->transactional && $this->transaction) $this->transaction->rollBack();
			}
		}
	}
}

0

#13 User is offline   Juban 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 12-January 09
  • Location:Grenoble France

Posted 02 July 2010 - 11:10 AM

Hi, and thank you for your contribution.
Could you explain a bit more what the "more complicated model" is for you?

Anyway, I'm going to check the modifications you made and figure out what it does and come back to you as soon as possible.

Regards.

Alban.
0

#14 User is offline   Juban 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 12-January 09
  • Location:Grenoble France

Posted 02 July 2010 - 11:18 AM

View Postmech7, on 02 July 2010 - 02:16 AM, said:

Is there anyway to just delete one relation? I could not really find it.. other then unsetting the relation array.

public function deleteBook($idx) {
		$libraryBookArray = $this->libraryBooks;
		unset($libraryBookArray[$idx]);
		$this->libraryBooks = $libraryBookArray;
}


Also how does this exactly work? :)


Hi,

This code simply removes one related model object (at a specific index) from a HAS_MANY relation array.
As you can't directly delete the objects from the property, it first transfer the content of the property (an array of objects) to a temporary array, remove a entry from that array then reset the value of the property with the resulting array.
It does not actually delete it from the database. This will be achieve during the save operation.

Regards.

Alban.
0

#15 User is offline   andr2k 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 8
  • Joined: 01-July 10

Posted 02 July 2010 - 06:44 PM

View PostJuban, on 02 July 2010 - 11:10 AM, said:

Hi, and thank you for your contribution.
Could you explain a bit more what the "more complicated model" is for you?

Anyway, I'm going to check the modifications you made and figure out what it does and come back to you as soon as possible.

Regards.

Alban.



It generated wrong DELETE queries for related models having several fields (I found it on a model having 3 fields (composite PK) + Foreign Key). Now it generates them better.
Just try to use previous version with such a model and you'll understand.

The second modification adds back-quotes, so now it's possible to create columns even with names from 'reserved words' list (like FROM, TO etc).

Anyway, you understand your code better so I would be happy if you review my changes and adapt them to your style.
0

#16 User is offline   Sander 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 169
  • Joined: 03-November 09
  • Location:Amsterdam, Netherlands

Posted 07 July 2010 - 05:28 AM

I'm wondering why my code won't work:

I have two model, 'Bedrijf' and 'Perceel' with a MANY_MANY relation. Working from the 'Bedrijf' model it looks like this:

	public function relations() {
		return array(
			...
			'percelen' => array(self::MANY_MANY, 'Perceel', 'BedrijfPerceel(bedrijf_id, perceel_id)',
				'index' => 'perceel_id',
			),
		);
	}


My form generates a checkboxlist of Perceel models and saves the selected elements to a public variable called $perceel, which is validated by this function. All validated ids are assigned to the 'percelen' relation afterwards:

	public function validatePerceel() {
		$clean = array();
		foreach($this->perceel as $perceel) {
			$criteria = new CDbCriteria;
			$criteria->compare('perceel_id', $perceel);
			if(Perceel::model()->exists($criteria)) {
				$clean[] = $perceel;
			} else {
				return false;
			}
		}
		$this->setRelationRecords('percelen', $clean);
		return true;
	}


This all works, $clean contains something like array(2, 3).

However, if I try to save my model afterwards I get the following CDbException:

Quote

Table "Perceel" does not have a column named "bedrijf_id".

#0 /Users/sander/Sites/Tools/yii-1.1.3.r2247/framework/db/ar/CActiveRecord.php(1327): CDbCommandBuilder->createColumnCriteria(Object(CDbTableSchema), Array, '', Array, ''t'.')
#1 /Users/sander/Sites/VROM/protected/components/CSaveRelationsBehavior.php(129): CActiveRecord->findByAttributes(Array)
#2 [internal function]: CSaveRelationsBehavior->setRelationRecords('percelen', Array)
#3 /Users/sander/Sites/Tools/yii-1.1.3.r2247/framework/base/CComponent.php(261): call_user_func_array(Array, Array)
#4 /Users/sander/Sites/Tools/yii-1.1.3.r2247/framework/db/ar/CActiveRecord.php(195): CComponent->__call('setRelationReco...', Array)
#5 [internal function]: CActiveRecord->__call('setRelationReco...', Array)
#6 /Users/sander/Sites/VROM/protected/models/Bedrijf.php(118): Bedrijf->setRelationRecords('percelen', Array)
#7 /Users/sander/Sites/Tools/yii-1.1.3.r2247/framework/validators/CInlineValidator.php(39): Bedrijf->validatePerceel('perceel', Array)
[...]



Looks like it's selecting the wrong primary key for the joined table. I tried figuring out what exactly was going on, but the code looks pretty complicated so I could use some help with this!
0

#17 User is offline   Juban 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 12-January 09
  • Location:Grenoble France

Posted 08 July 2010 - 07:53 AM

View PostSander, on 07 July 2010 - 05:28 AM, said:

Looks like it's selecting the wrong primary key for the joined table. I tried figuring out what exactly was going on, but the code looks pretty complicated so I could use some help with this!


Hi,

Sorry for the late answer.
For what I can guess, I think that you didn't set any foreign key constrains in your MANY_MANY table definition.
If this information is missing from the table schema, the behavior will consider the latest keys provided in the relation keys property as the primary keys of the joined table (the same way Yii Framework does).
So, you can try by setting the relation like that:

       public function relations() {
                return array(
                        ...
                        'percelen' => array(self::MANY_MANY, 'Perceel', 'BedrijfPerceel(perceel_id, bedrijf_id)',
                                'index' => 'perceel_id',
                        ),
                );
        }


Let me know if it works for you.

Regards.

Alban.
0

#18 User is offline   Sander 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 169
  • Joined: 03-November 09
  • Location:Amsterdam, Netherlands

Posted 08 July 2010 - 08:05 AM

The way I posted it was working fine for Yii when I manually added some records to the BedrijfPerceel table in my DB. Just to be clear, this code is from the Bedrijf model, so bedrijf_id its own primary key and perceel_id is the primary key of the joined table (the Perceel model). Switching the keys around results in incorrect behavior when I request related Perceel models, and it still gives me the same error when saving. You are correct in that there are no foreign keys defined explicitly, my app uses SQLite, which didn't support foreign keys until recently and still has FK functionality disabled by default..

Thanks for any help you can give :)
0

#19 User is offline   Juban 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 12-January 09
  • Location:Grenoble France

Posted 08 July 2010 - 09:33 AM

View PostSander, on 08 July 2010 - 08:05 AM, said:

The way I posted it was working fine for Yii when I manually added some records to the BedrijfPerceel table in my DB. Just to be clear, this code is from the Bedrijf model, so bedrijf_id its own primary key and perceel_id is the primary key of the joined table (the Perceel model). Switching the keys around results in incorrect behavior when I request related Perceel models, and it still gives me the same error when saving. You are correct in that there are no foreign keys defined explicitly, my app uses SQLite, which didn't support foreign keys until recently and still has FK functionality disabled by default..

Thanks for any help you can give :)


OK. I understand.
I will try to reproduce this and come back to you with a fix as soon as possible.
0

#20 User is offline   Juban 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 12-January 09
  • Location:Grenoble France

Posted 08 July 2010 - 10:51 AM

View PostSander, on 08 July 2010 - 08:05 AM, said:

The way I posted it was working fine for Yii when I manually added some records to the BedrijfPerceel table in my DB. Just to be clear, this code is from the Bedrijf model, so bedrijf_id its own primary key and perceel_id is the primary key of the joined table (the Perceel model). Switching the keys around results in incorrect behavior when I request related Perceel models, and it still gives me the same error when saving. You are correct in that there are no foreign keys defined explicitly, my app uses SQLite, which didn't support foreign keys until recently and still has FK functionality disabled by default..

Thanks for any help you can give :)


I just did a few tests using a MySQL table with no foreign keys constrains and a "flipped" keys definition for the relation and I confirm that Yii does not use the right foreign key value for the joined table either.
So. To get it work for you, I think you should define an extra relation in your model using the right definition keys order and use that relation to save the related data with the behavior.
What do you think about it ?

Regards.

Alban.
0

Share this topic:


  • (3 Pages)
  • +
  • 1
  • 2
  • 3
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users