Exemplo n.º 1
0
 /**
  * Retrieves HTML code of relating elements of initially related or
  * explicitly selected element.
  *
  * @param array $data custom data to be passed to template on rendering
  * @param string $template name of custom template to use instead of default one on rendering
  * @param int $listNodeAtIndex index of node to list properties of
  *        (default: node at opposite end of relation)
  * @return string rendering result
  */
 public function render($data = array(), $template = null, $listNodeAtIndex = -1)
 {
     $query = $this->query();
     // extend query to fetch all properties of selected node's model
     $query->addProperty($this->datasource->qualifyDatasetName($this->nodeAtIndex($listNodeAtIndex)->getName()) . '.*');
     // process query
     $matches = $query->execute()->all();
     // start variable space initialized using provided set of custom data
     $data = variable_space::fromArray($data);
     // add fetched relation instances to variable space
     $data->update('matches', $matches);
     // add reference on current relation manager instance
     $data->update('relation', $this);
     // render variable space using selected or default template
     return view::render($template ? $template : 'model/relation/generic', $data);
 }
Exemplo n.º 2
0
 /**
  * Makes user adopting current role.
  *
  * @param user $user user to adopt current role
  * @return $this
  * @throws \Exception on failing to adopt role
  */
 public function makeAdoptedBy(user $user)
 {
     $role = $this;
     if (!$this->_source->transaction()->wrap(function (datasource\connection $db) use($role, $user) {
         $userID = $user->getUUID();
         $roleID = $role->id;
         $count = $db->createQuery('user_role')->addCondition('user_uuid=?', true, $userID)->addCondition('role_id=?', true, $roleID)->execute(true)->cell();
         if ($count > 0) {
             // role has been adopted before
             return true;
         }
         $qSet = $db->qualifyDatasetName('user_role');
         return $db->test('INSERT INTO ' . $qSet . ' (user_uuid,role_id) VALUES (?,?)', $userID, $roleID) !== false;
     })) {
         throw new \RuntimeException(sprintf('adopting role %s by user %s failed', $this->label, $user->getName()));
     }
     return $this;
 }
Exemplo n.º 3
0
 /**
  * Compiles SQL term for use in WHERE clauses to select records matching all
  * properties in given filter.
  *
  * Resulting term is using parameter markers for values of those properties
  * to be matched. These values have to be given on querying data source.
  *
  * @param connection $source data source to use for quoting names
  *        of filtering properties
  * @param array $filter set of "properties" and "values"
  * @return string SQL-term
  */
 protected function _compileFilterTerm(connection $source, $filter)
 {
     return implode(' AND ', array_map(function ($n) use($source) {
         return $source->quoteName($n) . '=?';
     }, $filter['properties']));
 }
Exemplo n.º 4
0
 /**
  * Qualifies and/or quotes provided property names.
  *
  * @param array $arrNames set of property names to process
  * @param string|null $strSetOrAlias name/alias of data set, provide for qualified names, omit for unqualified names
  * @param connection $source used optionally to quote names for use in querying connected data source
  * @return array set of qualified and/or quoted names of properties
  */
 protected function _qualifyNames($arrNames, $strSetOrAlias = null, connection $source = null)
 {
     $parts = array();
     if (is_string($strSetOrAlias)) {
         $parts[] = $source ? $source->qualifyDatasetName(trim($strSetOrAlias)) : $strSetOrAlias;
     }
     foreach ($arrNames as $key => $name) {
         $temp = $parts;
         $temp[] = $source ? $source->quoteName(trim($name)) : trim($name);
         $arrNames[$key] = implode('.', $temp);
     }
     return $arrNames;
 }
Exemplo n.º 5
0
 /**
  * Retrieves query on datasource prepared to basically operate on current
  * model's data set.
  *
  * @param string $alias optional alias of model's data set in query
  * @return query customizable query on current model
  */
 public function query($alias = null)
 {
     $alias = trim($alias);
     return $this->_source->createQuery(static::set() . ($alias != '' ? " {$alias}" : ''));
 }
Exemplo n.º 6
0
 /**
  * Processes input on current editor.
  *
  * @param callable $validatorCallback
  * @return bool|string false on input failures requiring user action,
  *                     "saved" on input successfully saved to data source,
  *                     "cancel" on user pressing cancel button,
  *                     "delete" on user deleting record
  */
 public function processInput($validatorCallback = null)
 {
     if ($this->hasInput()) {
         switch (input::vget('_cmd')) {
             case 'cancel':
                 // permit closing editor due to user requesting to cancel editing
                 return 'cancel';
             case 'delete':
                 // delete current edited item
                 if ($this->may['delete'] && $this->item) {
                     $ctx = $this;
                     $item = $this->item;
                     $fields = $this->fields;
                     $this->datasource->transaction()->wrap(function () use($ctx, $item, $fields) {
                         foreach ($fields as $field) {
                             /** @var model_editor_field $field */
                             $field->type()->onDeleting($ctx, $item, $field);
                         }
                         $item->delete();
                         return true;
                     });
                     $this->item = null;
                     return 'delete';
                 }
                 view::flash(\de\toxa\txf\_L('You must not delete this item.'), 'error');
                 return false;
             case 'save':
                 // extract some protected properties from current instance to be used in transaction-wrapped callback
                 $ctx = $this;
                 $class = $this->class;
                 $source = $this->datasource;
                 $fields = $this->fields;
                 $enabled = $this->enabled;
                 $item = $this->item;
                 $fixed = $this->getFixed();
                 $errors = array();
                 $this->onCreating = !$this->hasItem();
                 if (!$this->onCreating && !$this->may['edit']) {
                     view::flash(\de\toxa\txf\_L('You must not edit this item.'), 'error');
                     return false;
                 }
                 // wrap modification on model in transaction
                 $success = $source->transaction()->wrap(function () use($ctx, $class, $source, $fields, $enabled, $fixed, &$item, &$errors, $validatorCallback) {
                     $properties = array();
                     foreach ($fields as $property => $definition) {
                         /** @var model_editor_field $definition */
                         if (!count($enabled) || !@$enabled[$property]) {
                             try {
                                 // normalize input
                                 $input = call_user_func(array($definition->type(), 'normalize'), $ctx->getValue($property, $definition->isCustom()), $property, $ctx);
                                 // validate input
                                 $success = call_user_func(array($definition->type(), 'validate'), $input, $property, $ctx);
                                 // save input if valid
                                 if ($success) {
                                     $properties[$property] = $input;
                                 } else {
                                     $errors[$property] = \de\toxa\txf\_L('Your input is invalid.');
                                 }
                             } catch (\Exception $e) {
                                 $errors[$property] = $e->getMessage();
                             }
                         }
                     }
                     if (count($errors)) {
                         return false;
                     }
                     if (is_callable($validatorCallback)) {
                         // provide opportunity to qualify properties for validation
                         $qualified = $properties;
                         foreach ($fields as $field) {
                             /** @var model_editor_field $field */
                             $qualified = $field->type()->beforeValidating($ctx, $item, $qualified, $field);
                         }
                         // invoke custom callback given those qualified copy of properties for validating
                         $localErrors = call_user_func($validatorCallback, $qualified, $errors, $item ? $item->id() : null);
                         if ($localErrors === false || is_string($localErrors) || is_array($localErrors) && count($localErrors)) {
                             if (is_array($localErrors)) {
                                 $errors = array_merge($errors, $localErrors);
                             } else {
                                 if (is_string($localErrors)) {
                                     view::flash($localErrors, 'error');
                                 }
                             }
                             return false;
                         }
                     }
                     if ($item) {
                         // on updating item -> don't adjust values of
                         // properties marked as fixed
                         foreach ($fixed as $name => $value) {
                             unset($properties[$name]);
                         }
                     } else {
                         // creating new item -> ensure to use fixed initial
                         // values provided additionally
                         foreach ($fixed as $name => $value) {
                             $properties[$name] = $value;
                         }
                     }
                     // optionally pre-process saving properties of item
                     foreach ($fields as $field) {
                         /** @var model_editor_field $field */
                         $properties = $field->type()->beforeStoring($ctx, $item, $properties, $field);
                     }
                     if ($item) {
                         // update properties of existing item
                         foreach ($properties as $name => $value) {
                             $item->__set($name, $value);
                         }
                     } else {
                         // create new item
                         $item = $class->getMethod('create')->invoke(null, $source, $properties);
                         // tell all elements to have item now
                         foreach ($fields as $field) {
                             /** @var model_editor_field $field */
                             $field->type()->onSelectingItem($ctx, $item, $field);
                         }
                     }
                     // optionally post-process saving properties of item
                     foreach ($fields as $field) {
                         /** @var model_editor_field $field */
                         $item = $field->type()->afterStoring($ctx, $item, $properties, $field);
                     }
                     return true;
                 });
                 // transfer adjusted properties back to protected scope of current instance
                 $this->errors = $errors;
                 // write back item created or probably replaced by afterStoring() call in transaction
                 $this->item = $item;
                 if ($success) {
                     // permit closing editor after having saved all current input
                     view::flash(\de\toxa\txf\_L('Your changes have been saved.'));
                     return 'saved';
                 }
                 view::flash(\de\toxa\txf\_L('Failed to save your changes.'), 'error');
         }
     }
     // don't close editor
     return false;
 }
Exemplo n.º 7
0
 /**
  * Retrieves name of data set optionally combined with declared alias.
  *
  * On providing datasource connection either part of resulting name is
  * quoted (and qualified) according to quoting and qualification rules of
  * connected datasource.
  *
  * @param connection $db
  * @return string
  */
 public function getFullName(connection $db = null)
 {
     $name = $this->getName(true);
     $alias = $this->alias ? $this->alias : null;
     if ($db) {
         $name = $db->qualifyDatasetName($name);
         if ($alias) {
             $alias = $db->quoteName($alias);
         }
     }
     return $alias ? $name . ' ' . $alias : $name;
 }
Exemplo n.º 8
0
 /**
  * Declares data set of model in provided data source.
  *
  * @throws datasource_exception on failing to declare model's data set
  * @param connection $source
  * @return model_relation_model current instance
  */
 public function declareInDatasource(connection $source)
 {
     $setName = $this->getSetName();
     // test runtime cache for including mark on having declared data set in
     // provided data source before
     if (array_key_exists($setName, self::$declaredCached)) {
         foreach (self::$declaredCached[$setName] as $s) {
             if ($s === $source) {
                 return $this;
             }
         }
     }
     if ($this->isVirtual()) {
         // wrapping faked model
         // -> use provided description to declare model's set in data source
         if (!$source->createDataset($setName, $this->definition, $this->primaries)) {
             throw new datasource_exception($source, 'failed to create data set of model ' . $setName);
         }
     } else {
         // wrapping actually existing model
         // -> call its model::updateSchema() on provided data source
         $this->reflection->getMethod('updateSchema')->invoke(null, $source);
     }
     // add mark to runtime cache on having declared model's data set in
     // provided data source
     self::$declaredCached[$setName] = array_merge((array) self::$declaredCached[$setName], array($source));
     return $this;
 }