示例#1
0
 /**
  * Find a related recordset.
  * @param Garp_Model $model The model that spawned this data
  * @param Garp_Db_Row $row The row object
  * @param Garp_Util_Configuration $options Various relation options
  * @return String The name of the method.
  */
 protected function _getRelatedRowset(Garp_Model $model, Garp_Db_Table_Row $row, Garp_Util_Configuration $options)
 {
     /**
      * An optional passed SELECT object will be passed by reference after every query.
      * This results in an error when 'clone' is not used, because correlation names will be
      * used twice (since they were set during the first iteration). Using 'clone' makes sure
      * a brand new SELECT object is used every time that hasn't been soiled by a possible
      * previous query.
      */
     $conditions = is_null($options['conditions']) ? null : clone $options['conditions'];
     $otherModel = $options['modelClass'];
     if (!$otherModel instanceof Zend_Db_Table_Abstract) {
         $otherModel = new $otherModel();
     }
     /**
      * Do not cache related queries. The "outside" query should be the only
      * query that's cached.
      */
     $originalCacheQueriesFlag = $otherModel->getCacheQueries();
     $otherModel->setCacheQueries(false);
     $modelName = get_class($otherModel);
     $relatedRowset = null;
     // many to many
     if (!empty($options['bindingModel'])) {
         $relatedRowset = $row->findManyToManyRowset($otherModel, $options['bindingModel'], $options['rule2'], $options['rule'], $conditions);
     } else {
         /**
          * 'mode' is used to clear ambiguity with homophilic relationships. For example,
          * a Model_Doc can have have child Docs and one parent Doc. The conditionals below can never tell
          * which method to call (findParentRow or findDependentRowset) from the referenceMap.
          * Therefore, we can help the decision-making by passing "mode". This can either be
          * "parent" or "dependent", which will then force a call to findParentRow and findDependentRowset,
          * respectively.
          */
         if (is_null($options['mode'])) {
             // belongs to
             try {
                 $model->getReference($modelName, $options['rule']);
                 $relatedRowset = $row->findParentRow($otherModel, $options['rule'], $conditions);
             } catch (Exception $e) {
                 if (!Garp_Content_Relation_Manager::isInvalidReferenceException($e)) {
                     throw $e;
                 }
                 try {
                     // one to many - one to one
                     // The following line triggers an exception if no reference is available
                     $otherModel->getReference(get_class($model), $options['rule']);
                     $relatedRowset = $row->findDependentRowset($otherModel, $options['rule'], $conditions);
                 } catch (Exception $e) {
                     if (!Garp_Content_Relation_Manager::isInvalidReferenceException($e)) {
                         throw $e;
                     }
                     $bindingModel = $model->getBindingModel($modelName);
                     $relatedRowset = $row->findManyToManyRowset($otherModel, $bindingModel, $options['rule2'], $options['rule'], $conditions);
                 }
             }
         } else {
             switch ($options['mode']) {
                 case 'parent':
                     $relatedRowset = $row->findParentRow($otherModel, $options['rule'], $conditions);
                     break;
                 case 'dependent':
                     $relatedRowset = $row->findDependentRowset($otherModel, $options['rule'], $conditions);
                     break;
                 default:
                     throw new Garp_Model_Exception('Invalid value for "mode" given. Must be either "parent" or ' . '"dependent", but "' . $options['mode'] . '" was given.');
                     break;
             }
         }
     }
     // Reset the cacheQueries value. It's a static property,
     // so leaving it FALSE will affect all future fetch() calls to this
     // model. Not good.
     $otherModel->setCacheQueries($originalCacheQueriesFlag);
     return $relatedRowset;
 }
示例#2
0
 /**
  * Relate Chapters.
  * Called after insert and after update.
  * @param Array $chapters
  * @param Garp_Model_Db $model The subject model
  * @param Int $articleId The id of the involved article
  * @return Void
  */
 public function relateChapters(array $chapters, Garp_Model_Db $model, $articleId)
 {
     // Start by unrelating all chapters
     Garp_Content_Relation_Manager::unrelate(array('modelA' => $model, 'modelB' => 'Model_Chapter', 'keyA' => $articleId));
     // Reverse order since the Weighable behavior sorts chapter by weight DESC,
     // giving each new chapter the highest weight.
     $chapters = array_reverse($chapters);
     foreach ($chapters as $chapterData) {
         $chapterData = $this->_getValidChapterData($chapterData);
         /**
          * Insert a new chapter.
          * The chapter will take care of storing and relating the
          * content nodes.
          */
         $chapterModel = new Model_Chapter();
         $chapterId = $chapterModel->insert(array('type' => $chapterData['type'], 'content' => $chapterData['content']));
         Garp_Content_Relation_Manager::relate(array('modelA' => $model, 'modelB' => 'Model_Chapter', 'keyA' => $articleId, 'keyB' => $chapterId));
     }
 }
示例#3
0
 /**
  * Unrelate 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
  * @return bool Success
  */
 public static function unrelate($options)
 {
     self::_normalizeOptionsForUnrelate($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::_unrelateHasAndBelongsToMany($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::unrelate($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::_unrelateHasAndBelongsToMany($options);
         }
     }
     /**
      * Figure out which model is leading. This depends on which of the two keys is provided.
      * This kind of flips the query around. For instance, when keyA is given, the query is
      * something like this:
      * UPDATE modelA SET foreignkey = NULL WHERE primarykey = keyA
      * When keyB is given however, the query goes something like this:
      * UPDATE modelA SET foreignkey = NULL WHERE foreignkey = keyB
      */
     $query = 'UPDATE `' . $modelA->getName() . '` SET ';
     $columnsToValues = array();
     foreach ($reference['columns'] as $column) {
         $columnsToValues[] = '`' . $column . '` = NULL';
     }
     $columnsToValues = implode(' AND ', $columnsToValues);
     $query .= $columnsToValues;
     $whereColumnsToValues = array();
     if ($options['keyA']) {
         $useColumns = 'refColumns';
         $useKeys = 'keyA';
     } else {
         $useColumns = 'columns';
         $useKeys = 'keyB';
     }
     foreach ($reference[$useColumns] as $i => $column) {
         $whereColumnsToValues[] = '`' . $column . '` = ' . $options[$useKeys][$i];
     }
     $whereColumnsToValues = implode(' AND ', $whereColumnsToValues);
     $query .= ' WHERE ';
     $query .= $whereColumnsToValues;
     return $modelA->getAdapter()->query($query);
 }
示例#4
0
 /**
  * Unrelate entities from each other
  *
  * @param array $options
  * @return bool
  */
 public function unrelate(array $options)
 {
     $this->_checkAcl('relate');
     extract($options);
     if (!isset($primaryKey) || !isset($model) || !isset($foreignKeys)) {
         throw new Garp_Content_Exception('Not enough options. "primaryKey", "model" and "foreignKeys" are required.');
     }
     $model = Garp_Content_Api::modelAliasToClass($model);
     $primaryKey = (array) $primaryKey;
     $foreignKeys = (array) $foreignKeys;
     $rule = isset($rule) ? $rule : null;
     $rule2 = isset($rule2) ? $rule2 : null;
     $bindingModel = isset($bindingModel) ? 'Model_' . $bindingModel : null;
     $bidirectional = isset($bidirectional) ? $bidirectional : null;
     Garp_Content_Relation_Manager::unrelate(array('modelA' => $this->_model, 'modelB' => $model, 'keyA' => $primaryKey, 'keyB' => $foreignKeys, 'rule' => $rule, 'ruleB' => $rule2, 'bindingModel' => $bindingModel, 'bidirectional' => $bidirectional));
 }
示例#5
0
 /**
  * Relate ContentNodes to a chapter.
  * @param Array $contentNodeList
  * @param Int $chapterId
  * @return Void
  */
 public function relateContentNodes($contentNodeList, $chapterId)
 {
     // Reverse node list because the Weighable behavior sorts different from the way
     // the CMS sends us the nodes.
     $contentNodeList = array_reverse($contentNodeList);
     foreach ($contentNodeList as $contentNode) {
         $node = $this->_getValidContentNodeData($contentNode);
         // Save ContentNode
         $node['chapter_id'] = $chapterId;
         $contentNodeId = $this->_insertContentNode($node);
         // @todo Move everything below here to Model_ContentNode::afterInsert()
         // Determine content type
         $contentTypeModelName = 'Model_' . $node['model'];
         $contentTypeModel = new $contentTypeModelName();
         // Check for existing id
         $data = $node['data'];
         if (empty($data['id'])) {
             // If no id is present, create a new subtype record
             $contentTypeId = $contentTypeModel->insert($data);
         } else {
             // Update the chapter subtype's content
             $contentTypeModel->update($data, 'id = ' . $contentTypeModel->getAdapter()->quote($data['id']));
             $contentTypeId = $data['id'];
         }
         // Relate the ContentNode to the subtype record
         Garp_Content_Relation_Manager::relate(array('modelA' => 'Model_ContentNode', 'modelB' => $contentTypeModel, 'keyA' => $contentNodeId, 'keyB' => $contentTypeId));
     }
 }