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; }