Example #1
0
 /**
  * Retrieves name of set associated with described model.
  *
  * The name is optionally qualified and quoted on providing data source
  * considered to contain data set.
  *
  * @param connection $source data source to use for quoting name
  * @param model_relation_model $model description of a model
  * @return string optionally quoted name of set associated with described model
  */
 protected function _setNameOfModel(connection $source = null, model_relation_model $model)
 {
     $set = $model->getSetName();
     return is_null($source) ? $set : $source->qualifyDatasetName($set);
 }
Example #2
0
 /**
  * Normalizes provided information to describe class of a model to associate
  * with relational node.
  *
  * Input is validated for being model instance or a model's reflection.
  * Output is always a model's reflection then.
  *
  * @throws \InvalidArgumentException on providing neither model nor its reflection
  * @param model|\ReflectionClass|mixed $model information on model to normalize
  * @return model_relation_model
  */
 protected static function _normalizeModel($model)
 {
     if ($model instanceof model_relation_model) {
         return $model;
     }
     if ($model instanceof model) {
         $model = $model->getReflection();
     }
     if (!$model instanceof \ReflectionClass) {
         throw new \InvalidArgumentException('invalid relation node model');
     }
     return model_relation_model::createOnModel($model);
 }
Example #3
0
 /**
  * @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;
 }
Example #4
0
 /**
  * Detects if provided model is same as current one.
  *
  * @param model|\ReflectionClass|string|model_relation_model $model
  * @return bool true if provided model is same as current one
  */
 public function isSameModel($model)
 {
     if ($this->isVirtual()) {
         return $model instanceof model_relation_model && $model->isVirtual() && $model->set == $this->set;
     }
     return model::normalizeModel($model)->getName() == $this->reflection->getName();
 }