Yii Framework Forum: please test my AR Enhancement: automatically sync MANY_MANY table when calling save() - Yii Framework Forum

Jump to content

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

please test my AR Enhancement: automatically sync MANY_MANY table when calling save() Rate Topic: -----

#41 User is offline   bonnie 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 62
  • Joined: 08-February 11

Posted 02 August 2011 - 02:20 PM

View Postthyseus, on 26 January 2010 - 08:32 AM, said:

Hi everyone,

let´s think about pimping up our relation ActiveRecord up even further.

Assuming the following MANY-to-MANY 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_d, post_id)'),



Now we can do this:
$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 System.

We can also do this:

$category = new Category();
$category->posts = array(5, 6, 7, 10);
$caregory->save();


This saves our new Category and adds the Post with the primary key of 5, 6, 7 and 10 in the Databases. 5 Queries will be performed here, one for the Category-Model and four for the N:M-Table tbl_post_category.

We can also pass a _single_ object:

$category = new Category();
$category->posts = Post::model()->findByPk(12);
$category->save();


or a single integer:

$post = new Post();
$post->categories = 7;
$post->save();


This is my basic implementation of this behavior. It would like to hear what you low-level Yii Cracks think about it :)

Place this anywhere in your Model file:
 public function afterSave() {
    parent::afterSave();

    Yii::trace('writing MANY_MANY data for '.get_class($this),'system.db.ar.CActiveRecord');

    foreach($this->relations() as $key => $relation)
    {
      if($relation['0'] == self::MANY_MANY) // relationType
      {
        if(isset($this->$key))
        {
          if(is_object($this->$key) || is_numeric($this->$key))
          {
            $query = $this->makeManyManyCommand(
              $relation[2],
              $this->{$this->tableSchema->primaryKey},
              (is_object($this->$key))
              ?  $this->$key->{$this->$key->tableSchema->primaryKey}
              : $this->{$key});
            $this->insertManyManyEntry($query);
          }
          else if (is_array($this->$key) && $this->$key != array())
          {
            foreach($this->$key as $foreignobject)
            {
              $query = $this->makeManyManyCommand(
                $relation[2],
                $this->{$this->tableSchema->primaryKey},
                (is_object($foreignobject))
                ? $foreignobject->{$foreignobject->tableSchema->primaryKey}
                : $foreignobject);
                $this->insertManyManyEntry($query);
            }
          }
        }
      }
    }
  }

  public function insertManyManyEntry($query) {
    if(!Yii::app()->db->createCommand($query)->execute())
      throw new CException(Yii::t('yii','an Error occured while trying to update MANY_MANY relation table'));

  }
  public function makeManyManyCommand($model, $rel, $foreignrel) {
    return sprintf("insert into %s values ('%s', '%s')", $model, $rel, $foreignrel);
  }



0

#42 User is offline   afterlastangel 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 1
  • Joined: 10-August 11

Posted 28 August 2011 - 10:10 PM

The extension is very useful but, I think you must apply mrbig's patch. Because the problem when saving. (If you don't specify any Many Many relation to before save, there will be problem).
0

#43 User is offline   tremity 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 4
  • Joined: 27-August 11

Posted 06 September 2011 - 04:07 AM

Hi all,
nice extension.

I got it to work quite easily, the only problem I had to fix by my own is about the kind of db connection it uses. In my project I have to connect to different databases according to the specific activerecord class I'm working with.
To do this I prepared some customized activerecord master classes (overriding the getDbConnection() method) from which I let my classes inherit accordingly.

However, the main point here is that I cannot always use Yii::app()->db connection, that is exactly the connection used in CAdvancedArBehavior extension.

Thus I simply replaced this:
        public function execute($query) {
		return Yii::app()->db->createCommand($query)->execute();
	}


with this:
        public function execute($query) {
		return $this->owner->dbConnection->createCommand($query)->execute();
	}


This should work in any case, either your class extends the standard CActiveRecord base class or a modified version of it (as in my case).

And of course I had to use mrbig's patch as well to get everything work:

View Postmrbig, on 02 June 2010 - 04:29 AM, said:

I did the following modification to the original code, this is even shorter
diff --git a/protected/extensions/CAdvancedArBehavior.php b/protected/extensions/CAdvancedArBehavior.php
index 803b75f..2a00aeb 100644
--- a/protected/extensions/CAdvancedArBehavior.php
+++ b/protected/extensions/CAdvancedArBehavior.php
@@ -156,7 +156,7 @@ class CAdvancedArbehavior extends CActiveRecordBehavior
                {
                        if(!is_numeric($foreignobject))
                        {
-                               $foreignobject = $foreignobject->{$foreignobject->$relation['m2mForeignField']};
+                               $foreignobject = $foreignobject->getPrimaryKey();
                        }
                        $this->execute($this->makeManyManyInsertCommand($relation, $foreignobject));
                }




hope this can help someone else.
0

#44 User is offline   shark0der 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 2
  • Joined: 11-October 11

Posted 11 October 2011 - 06:08 PM

Hi everyone,

I noticed that this very usefull extension has absolutely no protection againts mysql injections.
I used this extension in conjuction with CHtml::checkBoxList(). One can easily inspect the page, and edit the value of a checkbox adding his sql code along with the value.

To stay safe, please update the code as follows:

	public function makeManyManyInsertCommand($relation, $value) {
		return sprintf("insert into %s (%s, %s) values (%s, %s)",
				$relation['m2mTable'],
				$relation['m2mThisField'],
				$relation['m2mForeignField'],
				Yii::app()->db->quoteValue(
                        $this->owner->{$this->owner->tableSchema->primaryKey}
                ),
				Yii::app()->db->quoteValue($value)
        );
	}

	public function makeManyManyDeleteCommand($relation) {
		return sprintf("delete ignore from %s where %s = %s",
                    $relation['m2mTable'],
                    $relation['m2mThisField'],
                    Yii::app()->db->quoteValue(
                        $this->owner->{$this->owner->tableSchema->primaryKey}
                    )
				);
	}


I recommend copy pasting this code over the functions you have because I edited the sql too, not only added the quoteValue() function.
Please don't make other mistakes like this.

Thanks
0

#45 User is offline   ditchx 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 1
  • Joined: 19-November 11

Posted 18 December 2011 - 10:43 AM

Hi,

I used to have problems when saving an array of Model objects until I found mrbig's patch and applied it. Just a heads up since the latest version (version 0.3) still doesn't contain the patch.


@shark0der:
I believe that the enhancement assumes valid input - so there is no need to sanitize at the "behavior" level. It is up to the user to ensure the integrity of the data that will passed to and used by the behaviors. Let's take the way you used the extension as an example. You use it in conjunction with a checkbox list - I assume you are not checking if the ids posted indeed correspond to actual/existing record ids. Otherwise, you won't be needing to apply data sanitation in the behavior.
0

#46 User is offline   shark0der 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 2
  • Joined: 11-October 11

Posted 12 February 2012 - 09:09 AM

You forgot the golden rule of web development: NEVER TRUST USER INPUT!.
It's not about checking if the ids exist in the database, it's about peeps that insert malicious code instead.
0

#47 User is offline   Th3N3rD 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 6
  • Joined: 19-October 12
  • Location:Palermo

Posted 25 October 2012 - 01:10 PM

I've a problem using this ext.
I've 2 Entity ParkingSlot and Street and the Relation ParkingSlot2Street.
I've defined 2 Models with MANY_MANY relation and i followerd the examples, and I did care about even the order of the fields in the relations definition.

Howerver If I try to add streets to ParkingSlot (inside the Controller) with:

$parking_slot = $this->loadModel($id);
$parking_slot->street = 1;
// or
$parking_slot->street = Street::model()->findByPk(1);
// or
$parking_slot->street = array(1);


I get the following error:

Property "ParkinSlot.Array" is not defined. (......\framework\base\CComponent.php:131) ....


I also add a $street property into the ParkingSlot Model but still the same!!

Any Hints??
0

#48 User is offline   Andres Felipe Diaz 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 14
  • Joined: 13-August 12

Posted 29 March 2013 - 02:48 PM

Thanks guys for the work. Looks amazing.

For those of you like me just getting here, this behavior is part of a bigger project yii user management.

The initial repository was an svn one in Google Code. However, the latest one got moved to github.

You may find it here:

CAdvancedArBehavior.php
Andrés Felipe Díaz
Andrés Felipe Díaz Digital Geek Blog Trickortip.com
My LinkedIn Profile
0

#49 User is offline   MrReboot 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 1
  • Joined: 25-November 13

Posted 25 November 2013 - 08:42 AM

I'm having some problems with this extensions.
I think it's all well defined. When i save new objects, the relation registers are created and everything seems ok.
The problem is when i use Table::model()->findByPk($pk) and i try to modify any field of the retrieved register in the main table.

When I save it, I get an error here in CAdvancedArBehavior.writeRelation() at the line:
$foreignobject = $foreignobject->{$foreignobject->$relation['m2mForeignField']};


It says Property "GCategorias.3" is not defined. It seems that the retrieved object by the MANY_MANY relation is not the same as the expected object.

If I print the object before save, this is what i get:
Array
(
    [0] => GCategorias Object
        (
            [_new:CActiveRecord:private] => 
            [_attributes:CActiveRecord:private] => Array
                (
                    [id_g_categoria] => 3
                    [descripcion] => voluntariado
                    [grupo] => 1
                )

            [_related:CActiveRecord:private] => Array
                (
                )
........
)


I have solved this bug changing the write relation method like this:

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(!is_numeric($foreignobject) && is_object($foreignobject)){
                            
                            if (is_numeric($foreignobject->$relation['m2mForeignField'])){
                                $foreignobject=$foreignobject->$relation['m2mForeignField'];
                            } else {
				$foreignobject = $foreignobject->{$foreignobject->$relation['m2mForeignField']};
                            }
                        }
			$this->execute(
					$this->makeManyManyInsertCommand($relation, $foreignobject));
		}
	}



Is it a bug or am I doing something wrong?
0

#50 User is offline   Sumar 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 2
  • Joined: 18-August 14

Posted 18 August 2014 - 03:59 PM

Do this extension supersede advancedrelationsbehavior?
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