Файл: silawar.ru/protected/extensions/CAdvancedArBehavior/CAdvancedArBehavior.php
Строк: 295
<?php
/**
* CAdvancedArBehavior class file.
*
* @author Herbert Maschke <thyseus@gmail.com>
* @link http://www.yiiframework.com/
* @version 0.3
*/
/* The CAdvancedArBehavior extension adds up some functionality to the default
* possibilites of yii´s ActiveRecord implementation.
*
* To use this extension, just copy this file to your extensions/ directory,
* add 'import' => 'application.extensions.CAdvancedArBehavior', [...] to your
* config/main.php and add this behavior to each model you would like to
* inherit the new possibilities:
*
* public function behaviors(){
* return array( 'CAdvancedArBehavior' => array(
* 'class' => 'application.extensions.CAdvancedArBehavior'));
* }
*
*
* Automatically sync your Database Schema when setting new fields by
* activating $syncdb
*
* Better support of MANY_TO_MANY relations:
*
* When we have defined a MANY_MANY relation in our relations() function, we
* are now able to add up instances of the foreign Model on the fly while
* saving our Model to the Database. Let´s assume the following Relation:
*
* Post has:
* 'categories'=>array(self::MANY_MANY, 'Category',
* 'tbl_post_category(post_id, category_id)')
*
* Category has:
* 'posts'=>array(self::MANY_MANY, 'Post',
* 'tbl_post_category(category_id, post_id)')
*
* Now we can use the attribute 'categories' of our Post model to add up new
* rows to our MANY_MANY connection Table:
*
* $post = new Post();
* $post->categories = Category::model()->findAll();
* $post->save();
*
* This will save our new Post in the table Post, and in addition to this it
* updates our N:M-Table with every Category available in the Database.
*
* We can further limit the Objects given to the attribute, and can also go
* the other Way around:
*
* $category = new Category();
* $category->posts = array(5, 6, 7, 10);
* $caregory->save();
*
* We can pass Object instances like in the first example, or a list of
* integers that representates the Primary key of the Foreign Table, so that
* the Posts with the id 5, 6, 7 and 10 get´s added up to our new Category.
*
* 5 Queries will be performed here, one for the Category-Model and four for
* the N:M-Table tbl_post_category. Note that this behavior could be tuned
* further in the future, so only one query get´s executed for the MANY_MANY
* Table.
*
* We can also pass a _single_ object or an single integer:
*
* $category = new Category();
* $category->posts = Post::model()->findByPk(12);
* $category->posts = 12;
* $category->save();
*
* Assign -1 to a attribute to let it be untouched by the behavior.
*/
class CAdvancedArbehavior extends CActiveRecordBehavior
{
// Set this to false to disable tracing of changes
public $trace = true;
// If you want to ignore some relations, set them here.
public $ignoreRelations = array();
// After the save process of the model this behavior is attached to
// is finished, we begin saving our MANY_MANY related data
public function afterSave($event)
{
if(!is_array($this->ignoreRelations))
throw new CException('ignoreRelations of CAdvancedArBehavior needs to be an array');
$this->writeManyManyTables();
return parent::afterSave($event);
}
protected function writeManyManyTables()
{
if($this->trace)
Yii::trace('writing MANY_MANY data for '.get_class($this->owner),
'system.db.ar.CActiveRecord');
foreach($this->getRelations() as $relation)
{
$this->cleanRelation($relation);
$this->writeRelation($relation);
}
}
/* A relation will have the following format:
$relation['m2mTable'] = the tablename of the foreign object
$relation['m2mThisField'] = the column in the many2many table that represents the primary Key of the object that this behavior is attached to
$relation['m2mForeignField'] = the column in the many2many table that represents the foreign object.
Written in Yii relation syntax, it would be like this
'relationname' => array('foreignobject', 'column', 'm2mTable(m2mThisField, m2mForeignField) */
protected function getRelations()
{
$relations = array();
foreach ($this->owner->relations() as $key => $relation)
{
if ($relation[0] == CActiveRecord::MANY_MANY &&
!in_array($key, $this->ignoreRelations) &&
$this->owner->hasRelated($key) &&
$this->owner->$key != -1)
{
$info = array();
$info['key'] = $key;
$info['foreignTable'] = $relation[1];
if (preg_match('/^(.+)((.+)s*,s*(.+))$/s', $relation[2], $pocks))
{
$info['m2mTable'] = $pocks[1];
$info['m2mThisField'] = $pocks[2];
$info['m2mForeignField'] = $pocks[3];
}
else
{
$info['m2mTable'] = $relation[2];
$info['m2mThisField'] = $this->owner->tableSchema->PrimaryKey;
$info['m2mForeignField'] = CActiveRecord::model($relation[1])->tableSchema->primaryKey;
}
$relations[$key] = $info;
}
}
return $relations;
}
/** writeRelation's job is to check if the user has given an array or an
* single Object, and executes the needed query */
protected function writeRelation($relation)
{
$key = $relation['key'];
// Only an object or primary key id is given
if(!is_array($this->owner->$key) && $this->owner->$key != array())
$this->owner->$key = array($this->owner->$key);
// An array of objects is given
foreach((array)$this->owner->$key as $foreignobject)
{
if(empty($foreignobject)) continue; // если выбрали пустое поле - не пишем
if(!is_numeric($foreignobject) && is_object($foreignobject))
$foreignobject = $foreignobject->{$foreignobject->$relation['m2mForeignField']};
$this->execute(
$this->makeManyManyInsertCommand($relation, $foreignobject));
}
}
/* before saving our relation data, we need to clean up exsting relations so
* they are synchronized */
protected function cleanRelation($relation)
{
$this->execute($this->makeManyManyDeleteCommand($relation));
}
// A wrapper function for execution of SQL queries
public function execute($query) {
return Yii::app()->db->createCommand($query)->execute();
}
public function makeManyManyInsertCommand($relation, $value) {
return sprintf("insert into %s (%s, %s) values ('%s', '%s')",
$relation['m2mTable'],
$relation['m2mThisField'],
$relation['m2mForeignField'],
$this->owner->{$this->owner->tableSchema->primaryKey},
$value);
}
public function makeManyManyDeleteCommand($relation) {
return sprintf("delete ignore from %s where %s = '%s'",
$relation['m2mTable'],
$relation['m2mThisField'],
$this->owner->{$this->owner->tableSchema->primaryKey}
);
}
}