/** * 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 */ 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 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)); } }
/** * Relate entities to each other, optionally removing previous existing relations. * * @param array $options * @return bool */ public function relate(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; if (array_key_exists('unrelateExisting', $options) && $options['unrelateExisting']) { Garp_Content_Relation_Manager::unrelate(array('modelA' => $this->_model, 'modelB' => $model, 'keyA' => $primaryKey, 'rule' => $rule, 'ruleB' => $rule2, 'bindingModel' => $bindingModel, 'bidirectional' => $bidirectional)); } $success = $attempts = 0; foreach ($foreignKeys as $i => $relationData) { if (!array_key_exists('key', $relationData)) { throw new Garp_Content_Exception('Foreign key is a required key.'); } $foreignKey = $relationData['key']; $extraFields = array_key_exists('relationMetadata', $relationData) ? $relationData['relationMetadata'] : array(); if (Garp_Content_Relation_Manager::relate(array('modelA' => $this->_model, 'modelB' => $model, 'keyA' => $primaryKey, 'keyB' => $foreignKey, 'extraFields' => $extraFields, 'rule' => $rule, 'ruleB' => $rule2, 'bindingModel' => $bindingModel, 'bidirectional' => $bidirectional))) { $success++; } $attempts++; } return $success == $attempts; }
/** * 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)); } }