Skip to content

Instantly share code, notes, and snippets.

@hom3chuk
Last active August 29, 2015 14:27
Show Gist options
  • Save hom3chuk/1f0e9e9076c5f7c4480a to your computer and use it in GitHub Desktop.
Save hom3chuk/1f0e9e9076c5f7c4480a to your computer and use it in GitHub Desktop.
<?php
class ActiveRecord extends CActiveRecord
{
/**
* Updates cross-table for MANY_MANY relations, no AR class for cross-table needed.
* This method will delete all existing cross-table records not present in relatedIdList, then populate cross-table with new relations from relatedIdList in one transaction.
* @param string $relationName relation name to be updated (as given in CActiveRecord::relations())
* @param array $relatedIdList list of related ids OR list of related ARs
* @param string $idKey PK accessor for related entity (used only if list of ARs passed in relatedIdList). Defaults to `id`, which is ok for almost every AR.
* @throws CDbException
*/
public function setRelated($relationName, array $relatedIdList, $idKey = 'id'){
// getting relation options
$crossTableTempInfo = explode('(', $this->relations()[$relationName][2]);
$crossTableMeta['table_name'] = trim($crossTableTempInfo[0], "() \t\n\r\0\x0B");
$crossTableMeta['key_1'] = trim(explode(',', $crossTableTempInfo[1])[0], "() \t\n\r\0\x0B");
$crossTableMeta['key_2'] = trim(explode(',', $crossTableTempInfo[1])[1], "() \t\n\r\0\x0B");
// filtering out ids
$flat = [];
foreach ( $relatedIdList as $currentRelated ){
if ( is_object($currentRelated) ){
$flat[] = $currentRelated->$idKey;
} else {
$flat[] = $currentRelated;
}
}
$related = [];
foreach ( $this->$relationName as $currentRelated ){
$related[] = $currentRelated->$idKey;
}
// relations to connect
$connect = array_diff($flat, $related);
// relations to disconnect
$disconnect = array_diff($related, $flat);
$transaction = $this->dbConnection->beginTransaction();
// connecting
if ( !empty($connect) ){
$params = [];
foreach ( $connect as $key => $value ){
$params[':rel_con_v_'.(string)$key] = $value;
}
$query = 'INSERT INTO '
. $crossTableMeta['table_name']
. ' ('
. $crossTableMeta['key_2']
. ', '
. $crossTableMeta['key_1']
. ') VALUES (' . implode(', :rel_pk), (', array_keys($params))
. ', :rel_pk)';
/** @var CDbCommand $connectRelatedCommand */
$connectRelatedCommand = Yii::app()->db->createCommand($query);
$connectRelatedCommand->bindValues($params + [':rel_pk' => $this->primaryKey]);
$connectRelatedCommand->execute();
}
// disconnecting
if ( !empty($disconnect) ){
$criteria = new CDbCriteria();
$criteria->addInCondition($crossTableMeta['key_2'], $disconnect);
$criteria->addCondition($crossTableMeta['key_1'] . '=:rel_pk');
/** @var CDbCommand $clearRelatedCommand */
$clearRelatedCommand = Yii::app()->db->createCommand('DELETE FROM ' . $crossTableMeta['table_name'] . ' WHERE ' . $criteria->condition);
$clearRelatedCommand->bindValues($criteria->params + [':rel_pk' => $this->primaryKey]);
$clearRelatedCommand->execute();
}
$transaction->commit();
}
}
@hom3chuk
Copy link
Author

Given

class Mail extends ActiveRecord
{
        //...
    public function relations()
    {
        return array(
            'categories' => array(self::MANY_MANY, 'Category', 'mail_to_category(mail, category)'),
            'regions' => array(self::MANY_MANY, 'Region', 'mail_to_region(mail, region)'),
        );
    }

used for checkbox groups:

            if ( isset($_POST['category']) ){
                $mail->setRelated('categories', array_keys($_POST['category']));
            }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment