Yii Framework Forum: An idea for handling MANY_MANY relationships - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • This topic is locked

An idea for handling MANY_MANY relationships Rate Topic: -----

#1 User is offline   MarcS 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 97
  • Joined: 05-July 09

Posted 28 July 2009 - 04:07 PM

I have a project that has quite a few many to many relationships and dialogs where they can be set.
I found it a bit sad that Yii doesn't have a built in way to automatically save them.
It's not very pretty since I wasn't able to encapsulate everything in a behavior.
I had to overwrite the __get __set and __isset methods in the Active Record class

By doing it this way you can easily create checkbox lists like this:
CHtml::activeCheckBoxList($model, 'group_ids',CHtml::listData(Group::model()->findAll(), 'id', 'name') );

without having to make any other modifications

HabtmBehavior:
class HabtmBehavior extends CActiveRecordBehavior{
  function relationshipIds($relation) {
    if(empty($this->owner->primaryKey)) {
      return array();
    }
    $relations = $this->owner->relations();
    list($table,$fk1,$fk2) = $this->parseTableDefinition($relations[$relation][2]);
    $command=$this->owner->dbConnection->createCommand("SELECT $fk2 FROM $table WHERE $fk1 = {$this->owner->primaryKey}");
    $reader=$command->query();
    $ids = array();
    foreach($reader as $row) {
    $ids[] = $row[$fk2];
    }
    return $ids;
  }

  function afterSave() {
    foreach($this->owner->relations() as $rel_name=>$relation) {
      if($relation[0]!==CActiveRecord::MANY_MANY) {
        continue;
      }
      $check_name = strtolower($relation[1]).'_ids';
      if(!isset($this->owner->$check_name)) {
        continue;
      }
      $data = $this->owner->$check_name;
      if(empty($data)) {
        $data = array();
      }
      list($table,$fk1,$fk2) = $this->parseTableDefinition($relation[2]);

      if(!$this->owner->isNewRecord) {
        $command=$this->owner->dbConnection->createCommand("DELETE FROM $table WHERE $fk1 = {$this->owner->primaryKey}");
        $command->execute();
      }
      foreach($data as $id) {
         $command=$this->owner->dbConnection->createCommand("INSERT INTO $table($fk1,$fk2) VALUES({$this->owner->primaryKey},$id) ");
         $command->execute();
      }
      
    }     
  }

  private function parseTableDefinition($table_definition) {
    preg_match('/([^(]+)\(([^,*]+), ([^)]+)\)/',$table_definition,$matches);
    if(count($matches) !== 4) {
      throw new CHttpException('404', "unable to parse $table_definition");
    }
    return array_slice($matches,1);
  }
}


Methods in Active Record Class
  private $_rel_attribs;

  function __isset($name) {
    if(isset($this->_rel_attribs[$name])) {
      return true;
    }
    return parent::__isset($name);
  }

  function __set($name,$value) {
    if($name === 'attributes' && is_array($value)) {
      $all_habtm = array();
      foreach($this->relations() as $rel_name=>$relation) {
        if($relation[0] === self::MANY_MANY) {
          $all_habtm[strtolower($relation[1]).'_ids'] = $rel_name;
        }
      }
      foreach($value as $key=>$val) {
        if(isset($all_habtm[$key])) {
          $this->_rel_attribs[$key] = $val;
        }
      }
    }
    return parent::__set($name, $value);
  }

  function __get($name) {
    if(isset($this->_rel_attribs[$name])) {
      return $this->_rel_attribs[$name];
    }
    $rel = $this->relations();
    foreach($rel as $r=>$data) {
      if($data[0]===self::MANY_MANY && $name==strtolower($data[1]).'_ids') {
        $result = $this->relationshipIds($r);
        $this->_rel_attribs[$name] = $result;
        return $result;
      }
    }
    return parent::__get($name);
  }


What do you guys think?
0

#2 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,902
  • Joined: 04-October 08
  • Location:DC, USA

Posted 29 July 2009 - 06:14 AM

Nice work. The only thing not pretty is that it relies on some hardcoded naming conventions. In 1.1.x, we will enhance the support for MM relationships.
0

#3 User is offline   MarcS 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 97
  • Joined: 05-July 09

Posted 29 July 2009 - 06:38 AM

true
it would be good if the naming conventions could be customized

I might include that and release it as an extension

Can't wait to see the MM support in 1.1
0

#4 User is offline   mavs 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 4
  • Joined: 17-November 11

Posted 19 November 2011 - 01:58 PM

Hi,

I'm interested in many to many relationships. Is there something in the current release that would be useful?

Regards,
mavs
0

#5 User is offline   andy_s 

  • Random Member Title
  • Yii
  • Group: Moderators
  • Posts: 1,526
  • Joined: 22-June 09
  • Location:Russia, Kostroma

Posted 24 November 2011 - 12:53 PM

View Postmavs, on 19 November 2011 - 01:58 PM, said:

Hi,

I'm interested in many to many relationships. Is there something in the current release that would be useful?

Regards,
mavs

This extension can help you, but only in the case if you will switch to Yii 1.1 ;)
0

#6 User is offline   mavs 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 4
  • Joined: 17-November 11

Posted 29 November 2011 - 05:40 PM

Thanks! Interesting. I don't have to switch. I'm already on it ;)
I found other way to get to related data. It's easy to get it, but hard to save it. Anyways, I'll be waiting for some Yii native solutions. Maybe in 2.0?
0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • This topic is locked

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