示例#1
0
 /**
  * Retrieves opposite reference on selected node also involved in current
  * reference.
  *
  * @example
  * Consider relation spanning over nodes/models "a", "b" and "c". This
  * relation consists of two references "a"-"b" and "b"-"c", with the latter
  * referencing from "b" to "c" ("b"->"c").
  *
  * The opposite reference of its referencing node ("b") is "a"-"b" while the
  * opposite reference of its referenced node ("c") is missing, thus
  * returning null here.
  *
  * @param model_relation_node $node either end of current reference
  * @return model_relation_reference|null opposite reference at selected end,
  *         null if missing another reference there
  */
 public function getOppositeReferenceAtNode(model_relation_node $node)
 {
     if ($node != $this->predecessorEnd && $node != $this->successorEnd) {
         throw new \InvalidArgumentException('foreign node rejected');
     }
     if ($node == $this->predecessorEnd && $this->intIndexInRelation == 0) {
         // there is no opposite of predecessor if current reference is first
         // one in relation
         return null;
     }
     try {
         return $this->relation->referenceAtIndex($this->intIndexInRelation + ($node == $this->predecessorEnd ? -1 : +1));
     } catch (\OutOfRangeException $e) {
         return null;
     }
 }
示例#2
0
文件: related.php 项目: cepharum/txf
 protected function _getSelectorOfExisting(model $item = null)
 {
     $mutableNode = $this->mutable->getReferencingNode();
     $selector = array('model' => $mutableNode->getModel(), 'properties' => $this->mutable->getReferencingPropertyNames(), 'filter' => array('properties' => array(), 'values' => array()));
     $identifyingReference = $this->mutable->getOppositeReferenceAtNode($mutableNode);
     if ($identifyingReference) {
         // mutable node is involved in another (opposite) reference
         // -> use that one's binding on mutable node to select existing
         //    relations for editor of mutable node
         $selector['filter']['properties'] = $this->mutable->getOppositePropertiesOf($mutableNode);
         $selector['filter']['values'] = $this->mutable->getOppositeValuesOf($mutableNode);
         // is identifying reference bound?
         // (it is unbound if associated editor is going to create new item
         //  and identifying reference is including that item yet missing)
         if (!is_array($selector['filter']['values'])) {
             if ($this->relation->referenceAtIndex(0) !== $identifyingReference || !$this->getEditor() || $this->getEditor()->hasItem()) {
                 // basically this case should be excluded by code in
                 // constructor ...
                 throw new \RuntimeException('mutable reference of relation is not bound properly');
             }
             // identifying reference is first reference of relation, but
             // item in editor does not exist yet
             // -> reference can't be bound
             //    -> there aren't any relations on that item not yet existing
             //       -> create filter always failing (to fetch empty set)
             $selector['filter']['properties'] = array('(1+1)');
             // parentheses are used to cheat name quoting
             $selector['filter']['values'] = array(3);
             // for getting term "(1+1)=3" (to mismatch every row)
         }
         $selector['filter']['values'] = array_values($selector['filter']['values']);
         // choose mode to use on updating bindings
         if ($this->mutable->getReferencingNode()->isManyToMany()) {
             // case: a < m > c with a-m bound and m-c unbound
             // - delete all instances of mutable node matching binding of
             //   opposite reference
             //   TODO: what if extra data is attached to m? (have some callback? require use of derived class?)
             $selector['drop'] = array('model' => $identifyingReference->getReferencingNode()->getModel(), 'filter' => &$selector['filter']);
         } else {
             // case: a > m > c with a-m bound and m-c unbound
             // - for a>m there can be one m at most,
             // - for a-m is bound there must be one m at least
             //   -> don't delete m, keep as is, but "null" it's referencing
             //      in m>c
             $selector['null'] = array('model' => $mutableNode->getModel(), 'filter' => &$selector['filter'], 'properties' => $this->mutable->getReferencingPropertyNames());
         }
     } else {
         // mutable node is not involved in another (opposite) reference
         // -> mutable node is either endpoint of relation
         // -> is mutable node at start of relation?
         $startNode = $this->relation->nodeAtIndex(0);
         if ($mutableNode !== $startNode) {
             // -> yes
             throw new \RuntimeException('relation can be adjusted from opposite end point, only');
         }
         // is editor actually working on existing item?
         // (... having ID for use in relations?)
         $editor = $this->getEditor();
         $itemToUse = $editor->hasItem() ? $editor->item() : $item;
         if (!$itemToUse) {
             // -> no
             //    there aren't any existing records to load, actually
             // but is model managed by editor matching model of mutable node?
             if (!$mutableNode->getModel()->isSameModel($editor->model())) {
                 // -> no
                 throw new \RuntimeException('relation is not compatible with item in editor');
             }
             // okay, create filter always mismatching w/o failing
             // - get records with (first) property of ID matching 0 and 1 simultaneously
             $idName = $editor->model()->getMethod('idName')->invoke(null, 0);
             $selector['filter']['properties'] = array($idName, $idName);
             $selector['filter']['values'] = array(0, 1);
         } else {
             // yes, there is an item in editor
             // is mutable node's model compatible with model of item in editor?
             if (!$mutableNode->getModel()->isSameModel($itemToUse->getReflection())) {
                 // -> no
                 throw new \RuntimeException('relation is not compatible with item in editor');
             }
             // -> item in editor is suitable instance of mutable node
             //    use item's ID for identifying existing relations on it
             //    (since mutable node is referring to item in editor and
             //     since mutable node is the referencing node, there is
             //     a single existing relation at most, though)
             $id = $itemToUse->id();
             $selector['filter']['properties'] = array_keys($id);
             $selector['filter']['values'] = array_values($id);
         }
         // case: m > b with m matching item in editor and m-b unbound
         // - m is item in editor and thus mustn't be deleted on saving relation
         //   -> keep m as is, but "null" it's referencing properties
         $selector['null'] = array('model' => $mutableNode->getModel(), 'filter' => &$selector['filter'], 'properties' => $this->mutable->getReferencingPropertyNames());
     }
     return $selector;
 }