예제 #1
0
파일: reference.php 프로젝트: cepharum/txf
 /**
  * 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
 public function onSelectingItem(model_editor $editor, model $item, model_editor_field $field)
 {
     // bind element's relation with item sharing its data source
     $this->relation->setDatasource($item->source())->bindNodeOnItem(0, $item);
 }
예제 #3
0
파일: model.php 프로젝트: cepharum/txf
 /**
  * @param model_relation $relation
  * @param $relationSpecification
  * @return model_relation
  */
 protected static function _compileRelation(model_relation $relation = null, $relationSpecification)
 {
     // extract declaration of next reference from provided specs
     $toNext = is_array(@$relationSpecification['referencing']) ? $relationSpecification['referencing'] : null;
     $fromNext = is_array(@$relationSpecification['referencedBy']) ? $relationSpecification['referencedBy'] : null;
     // ensure specs are containing either of the two options, only
     if (!(is_array($toNext) ^ is_array($fromNext))) {
         throw new \InvalidArgumentException('invalid reference mode in relation');
     }
     // select found declaration of next reference
     $referenceToAppend = $toNext ? $toNext : $fromNext;
     /*
      * get current end node of relation for appending new reference
      */
     if (is_null($relation)) {
         // there isn't any "current end node" for relation is empty, yet
         // -> start new relation on current model
         $previousNode = model_relation_node::createOnModel(new \ReflectionClass(get_called_class()));
         // -> apply optionally given global alias
         if (@$relationSpecification['alias']) {
             $previousNode->setAlias($relationSpecification['alias']);
         }
     } else {
         // get end node of existing relation
         $previousNode = $relation->nodeAtIndex(-1);
     }
     /*
      * extract description of model associated with next node to append
      */
     if (array_key_exists('model', $referenceToAppend)) {
         // got explicit name of model existing in code
         $model = static::normalizeModel($referenceToAppend['model']);
         $model = model_relation_model::createOnModel($model);
     } else {
         // missing explicit name of model existing in code
         // -> need to manage virtual model
         if ($fromNext && @$referenceToAppend['referencing'] && !@$referenceToAppend['referencedBy']) {
             // next node in relation is of type many-to-many
             // -> suitable for deriving virtual model
             $succeedingReference =& $referenceToAppend['referencing'];
             /*
              * in a relation a < m > c ...
              *
              * ... this code is about to describe virtual model of m
              * ... $previousNode refers to node a
              * ... $referenceToAppend provides information on a<m or m more specifically
              * ... $succeedingReference provides information on m>c or c more specifically
              */
             if (!@$succeedingReference['model']) {
                 // don't support chains of virtual models in relations
                 throw new \InvalidArgumentException('invalid chaining of nodes with virtual models');
             }
             // get model of node succeeding reference is referring to (model of c in example above)
             $nextModel = model_relation_model::createOnModel(static::normalizeModel($succeedingReference['model']));
             /*
              * get names of either neighbouring model's set
              *
              * (don't care for aliases here for these might change occasionally)
              */
             $previousSetName = $previousNode->getName(true);
             $nextSetName = $nextModel->getSetName();
             if (!$previousSetName || !$nextSetName) {
                 throw new \InvalidArgumentException('missing set names of models neighbouring virtual one');
             }
             /*
              * extract name of virtual model's data set
              */
             if (array_key_exists('dataset', $referenceToAppend)) {
                 // specification provides set name explicitly
                 $setName = $referenceToAppend['dataset'];
             } else {
                 // specification does not provide set name explicitly
                 // -> derive name from set names of neighbouring models
                 $setName = "{$previousSetName}_{$nextSetName}";
             }
             /*
              * derive properties and types of virtual model's data set
              */
             // get definitions of properties in either neighbouring model
             $previousDefinition = $previousNode->getModel()->getDefinition();
             $nextDefinition = $nextModel->getDefinition();
             // get properties in either neighbouring model to refer to in virtual
             $previousNames = array_values((array) \de\toxa\txf\_1(@$referenceToAppend['on'], 'id'));
             $nextNames = array_values((array) \de\toxa\txf\_1(@$succeedingReference['on'], 'id'));
             if (!count($previousNames) || !count($nextNames)) {
                 throw new \InvalidArgumentException('missing names of properties virtual node is referencing on');
             }
             // get names of properties in virtual model to use on referencing
             // - try explicitly mentioned names of properties first
             $namesOnPrevious = array_values((array) @$referenceToAppend['with']);
             $namesOnNext = array_values((array) @$succeedingReference['with']);
             // - fall back to implicitly deriving property names from referenced properties
             if (!count($namesOnPrevious)) {
                 $namesOnPrevious = array_map(function ($name) use($previousSetName) {
                     return "{$previousSetName}_{$name}";
                 }, $previousNames);
                 $referenceToAppend['with'] = $namesOnPrevious;
             }
             if (!count($namesOnNext)) {
                 $namesOnNext = array_map(function ($name) use($nextSetName) {
                     return "{$nextSetName}_{$name}";
                 }, $nextNames);
                 $succeedingReference['with'] = $namesOnNext;
             }
             // ensure either reference is working with uniquely named properties
             $clashingNames = array_intersect($namesOnPrevious, $namesOnNext);
             if (count($clashingNames)) {
                 throw new \InvalidArgumentException('invalid clash of implicit property names: ' . implode(', ', $clashingNames));
             }
             // finally compile definition of virtual model's data set
             $virtualDefinition = array();
             foreach ($namesOnPrevious as $index => $name) {
                 $relatedName = $previousNames[$index];
                 $virtualDefinition[$name] = preg_replace('/\\bprimary\\s+key\\b/i', '', $previousDefinition[$relatedName]);
             }
             foreach ($namesOnNext as $index => $name) {
                 $relatedName = $nextNames[$index];
                 $virtualDefinition[$name] = preg_replace('/\\bprimary\\s+key\\b/i', '', $nextDefinition[$relatedName]);
             }
             /*
              * create virtual model basing on description prepared above
              */
             $model = model_relation_model::createOnVirtualModel($setName, $virtualDefinition, array_keys($virtualDefinition));
         } else {
             // next node isn't of type many-to-many
             // -> reject implicitly to derive some virtual model
             throw new \InvalidArgumentException('definition of relation is missing model of node');
         }
     }
     /*
      * create node to append
      */
     $nextNode = model_relation_node::createOnModel($model);
     if ($referenceToAppend['alias']) {
         $nextNode->setAlias($referenceToAppend['alias']);
     }
     /*
      * establish reference between previous and next node as declared
      */
     $targetProperty = @$referenceToAppend['on'];
     if (!$targetProperty) {
         $targetProperty = array('id');
     }
     if ($toNext) {
         // establish reference from previous node to new node
         $previousNode->makeReferencingSuccessorIn($referenceToAppend['with']);
         $nextNode->makeReferencedByPredecessorOn($targetProperty);
     } else {
         // establish reference from new node to previous one
         $previousNode->makeReferencedBySuccessorOn($targetProperty);
         $nextNode->makeReferencingPredecessorIn($referenceToAppend['with']);
     }
     /*
      * append node to relation
      */
     if (is_null($relation)) {
         // relation hasn't been created before at all
         // -> create now
         $relation = model_relation::create($previousNode);
     }
     $expectingAnotherReference = @$referenceToAppend['referencing'] || @$referenceToAppend['referencedBy'];
     // append next node to relation
     $relation->add($nextNode, $expectingAnotherReference);
     /*
      * recursively add another node on having declaration
      */
     if ($expectingAnotherReference) {
         return static::_compileRelation($relation, $referenceToAppend);
     }
     return $relation;
 }