library/Garp/Content/Relation/Manager.php
public static function relate($options) {
        self::_normalizeOptionsForRelate($options);
        $modelA  = $options['modelA'];
        $modelB  = $options['modelB'];

        /**
         * If bindingModel is set, we can safely skip all those difficult checks below.
         */
        if (is_object($options['bindingModel'])) {
            return self::_relateHasAndBelongsToMany($options);
        }
        try {
            /**
             * If this succeeds, it's a regular relationship where the foreign key
             * resides inside modelA. Continue as usual.
             */
            $reference = $modelA->getReference(get_class($modelB), $options['rule']);
        } catch (Exception $e) {
            if (!self::isInvalidReferenceException($e)) {
                throw $e;
            }
            try {
                /**
                 * If this succeeds, the foreign key resides in the modelA.
                 * Flip modelA and modelB and keyA and keyB in order to normalize
                 * the given configuration.
                 * Call self::relate() recursively with these new options.
                 */
                $reference = $modelB->getReference(get_class($modelA), $options['rule']);

                $keyA = $options['keyA'];
                $keyB = $options['keyB'];
                $options['modelA'] = $modelB;
                $options['modelB'] = $modelA;
                $options['keyA'] = $keyB;
                $options['keyB'] = $keyA;
                return Garp_Content_Relation_Manager::relate($options);
            } catch (Exception $e) {
                if (!self::isInvalidReferenceException($e)) {
                    throw $e;
                }
                /**
                 * Goody, we're dealing with a hasAndBelongsToMany relationship here.
                 * Try to construct the intersection model and save the relation
                 * that way.
                 */
                return self::_relateHasAndBelongsToMany($options);
            }
        }

        $rowA = call_user_func_array(array($options['modelA'], 'find'), (array)$options['keyA']);
        if (!count($rowA)) {
            $errorMsg = sprintf(
                self::EXCEPTION_ROW_NOT_FOUND_BY_PRIMARY_KEY,
                $modelA->getName(), implode(',', (array)$options['keyA'])
            );
            throw new Garp_Content_Relation_Exception($errorMsg);
        }
        $rowA = $rowA->current();
        self::_addForeignKeysToRow($rowA, $reference, $options['keyB']);
        return $rowA->save();
    }
public static function relate($options) {
        self::_normalizeOptionsForRelate($options);
        $modelA  = $options['modelA'];
        $modelB  = $options['modelB'];

        /**
         * If bindingModel is set, we can safely skip all those difficult checks below.
         */
        if (is_object($options['bindingModel'])) {
            return self::_relateHasAndBelongsToMany($options);
        }
        try {
            /**
             * If this succeeds, it's a regular relationship where the foreign key
             * resides inside modelA. Continue as usual.
             */
            $reference = $modelA->getReference(get_class($modelB), $options['rule']);
        } catch (Exception $e) {
            if (!self::isInvalidReferenceException($e)) {
                throw $e;
            }
            try {
                /**
                 * If this succeeds, the foreign key resides in the modelA.
                 * Flip modelA and modelB and keyA and keyB in order to normalize
                 * the given configuration.
                 * Call self::relate() recursively with these new options.
                 */
                $reference = $modelB->getReference(get_class($modelA), $options['rule']);

                $keyA = $options['keyA'];
                $keyB = $options['keyB'];
                $options['modelA'] = $modelB;
                $options['modelB'] = $modelA;
                $options['keyA'] = $keyB;
                $options['keyB'] = $keyA;
                return Garp_Content_Relation_Manager::relate($options);
            } catch (Exception $e) {
                if (!self::isInvalidReferenceException($e)) {
                    throw $e;
                }
                /**
                 * Goody, we're dealing with a hasAndBelongsToMany relationship here.
                 * Try to construct the intersection model and save the relation
                 * that way.
                 */
                return self::_relateHasAndBelongsToMany($options);
            }
        }

        $rowA = call_user_func_array(array($options['modelA'], 'find'), (array)$options['keyA']);
        if (!count($rowA)) {
            $errorMsg = sprintf(
                self::EXCEPTION_ROW_NOT_FOUND_BY_PRIMARY_KEY,
                $modelA->getName(), implode(',', (array)$options['keyA'])
            );
            throw new Garp_Content_Relation_Exception($errorMsg);
        }
        $rowA = $rowA->current();
        self::_addForeignKeysToRow($rowA, $reference, $options['keyB']);
        return $rowA->save();
    }
public static function relate ( $ options ) { self :: _normalizeOptionsForRelate ( $ options ) ; $ modelA = $ options [ 'modelA' ] ; $ modelB = $ options [ 'modelB' ] ; /**
         * If bindingModel is set, we can safely skip all those difficult checks below.
         */ if ( is_object ( $ options [ 'bindingModel' ] ) ) { return self :: _relateHasAndBelongsToMany ( $ options ) ; } try { /**
             * If this succeeds, it's a regular relationship where the foreign key
             * resides inside modelA. Continue as usual.
             */ $ reference = $ modelA -> getReference ( get_class ( $ modelB ) , $ options [ 'rule' ] ) ; } catch ( Exception $ e ) { if ( ! self :: isInvalidReferenceException ( $ e ) ) { throw $ e ; } try { /**
                 * If this succeeds, the foreign key resides in the modelA.
                 * Flip modelA and modelB and keyA and keyB in order to normalize
                 * the given configuration.
                 * Call self::relate() recursively with these new options.
                 */ $ reference = $ modelB -> getReference ( get_class ( $ modelA ) , $ options [ 'rule' ] ) ; $ keyA = $ options [ 'keyA' ] ; $ keyB = $ options [ 'keyB' ] ; $ options [ 'modelA' ] = $ modelB ; $ options [ 'modelB' ] = $ modelA ; $ options [ 'keyA' ] = $ keyB ; $ options [ 'keyB' ] = $ keyA ; return Garp_Content_Relation_Manager :: relate ( $ options ) ; } catch ( Exception $ e ) { if ( ! self :: isInvalidReferenceException ( $ e ) ) { throw $ e ; } /**
                 * Goody, we're dealing with a hasAndBelongsToMany relationship here.
                 * Try to construct the intersection model and save the relation
                 * that way.
                 */ return self :: _relateHasAndBelongsToMany ( $ options ) ; } } $ rowA = call_user_func_array ( array ( $ options [ 'modelA' ] , 'find' ) , ( array ) $ options [ 'keyA' ] ) ; if ( ! count ( $ rowA ) ) { $ errorMsg = sprintf ( self :: EXCEPTION_ROW_NOT_FOUND_BY_PRIMARY_KEY , $ modelA -> getName ( ) , implode ( ',' , ( array ) $ options [ 'keyA' ] ) ) ; throw new Garp_Content_Relation_Exception ( $ errorMsg ) ; } $ rowA = $ rowA -> current ( ) ; self :: _addForeignKeysToRow ( $ rowA , $ reference , $ options [ 'keyB' ] ) ; return $ rowA -> save ( ) ; }
Relate records.

@param array|Garp_Util_Configuration $options Lots o' options:
'modelA'    string|Garp_Model_Db    The first model or classname thereof
'modelB'    string|Garp_Model_Db    The second model or classname thereof
'keyA'      mixed                   Primary key(s) of the first model
'keyB'      mixed                   Primary key(s) of the second model
'rule'      string                  The rule that stores this relationship in one
of the reference maps
'extraFields' Array                 Extra fields that can be saved with a HABTM record
@return bool Success
Relate records .