diff --git a/library/Icinga/Authentication/User/DbUserBackend.php b/library/Icinga/Authentication/User/DbUserBackend.php index 7a3de8966..8a7991a0f 100644 --- a/library/Icinga/Authentication/User/DbUserBackend.php +++ b/library/Icinga/Authentication/User/DbUserBackend.php @@ -76,11 +76,15 @@ class DbUserBackend extends DbRepository implements UserBackendInterface ); /** - * The value conversion rules to apply on a query/statement + * The value conversion rules to apply on a query or statement * * @var array */ - protected $conversionRules = array('password'); + protected $conversionRules = array( + 'user' => array( + 'password' + ) + ); /** * Initialize this database user backend diff --git a/library/Icinga/Authentication/UserGroup/IniUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/IniUserGroupBackend.php index f17cbb6a8..53eadc147 100644 --- a/library/Icinga/Authentication/UserGroup/IniUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/IniUserGroupBackend.php @@ -35,14 +35,16 @@ class IniUserGroupBackend extends IniRepository implements UserGroupBackendInter protected $filterColumns = array('group'); /** - * The value conversion rules to apply on a query + * The value conversion rules to apply on a query or statement * * @var array */ protected $conversionRules = array( - 'created_at' => 'date_time', - 'last_modified' => 'date_time', - 'users' => 'comma_separated_string' + 'groups' => array( + 'created_at' => 'date_time', + 'last_modified' => 'date_time', + 'users' => 'comma_separated_string' + ) ); /** diff --git a/library/Icinga/Repository/DbRepository.php b/library/Icinga/Repository/DbRepository.php index c43e8df54..9131b5052 100644 --- a/library/Icinga/Repository/DbRepository.php +++ b/library/Icinga/Repository/DbRepository.php @@ -401,6 +401,46 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, } } + /** + * Return whether this repository is capable of converting values for the given table + * + * @param array|string $table + * + * @return bool + */ + public function providesValueConversion($table) + { + return parent::providesValueConversion($this->removeTablePrefix($this->clearTableAlias($table))); + } + + /** + * Return the name of the conversion method for the given alias or column name and context + * + * @param array|string $table The datasource's table + * @param string $name The alias or column name for which to return a conversion method + * @param string $context The context of the conversion: persist or retrieve + * + * @return string + * + * @throws ProgrammingError In case a conversion rule is found but not any conversion method + */ + protected function getConverter($table, $name, $context) + { + if ( + $this->validateQueryColumnAssociation($table, $name) + || $this->validateStatementColumnAssociation($table, $name) + ) { + $table = $this->removeTablePrefix($this->clearTableAlias($table)); + } else { + $table = $this->findTableName($name); + if (! $table) { + throw new ProgrammingError('Column name validation seems to have failed. Did you require the column?'); + } + } + + return parent::getConverter($table, $name, $context); + } + /** * Validate that the requested table exists * @@ -692,6 +732,8 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, return $aliasTableMap[$column]; } + // TODO(jom): Elaborate whether it makes sense to throw ProgrammingError + // instead (duplicate aliases in different tables?) foreach ($aliasTableMap as $alias => $table) { if (strpos($alias, '.') !== false) { list($_, $alias) = split('.', $column, 2); diff --git a/library/Icinga/Repository/Repository.php b/library/Icinga/Repository/Repository.php index 7fba11727..7568a17f6 100644 --- a/library/Icinga/Repository/Repository.php +++ b/library/Icinga/Repository/Repository.php @@ -106,7 +106,7 @@ abstract class Repository implements Selectable protected $sortRules; /** - * The value conversion rules to apply on a query + * The value conversion rules to apply on a query or statement * * This may be initialized by concrete repository implementations and describes for which aliases or column * names what type of conversion is available. For entries, where the key is the alias/column and the value @@ -403,27 +403,30 @@ abstract class Repository implements Selectable } /** - * Return whether this repository is capable of converting values + * Return whether this repository is capable of converting values for the given table + * + * @param string $table * * @return bool */ - public function providesValueConversion() + public function providesValueConversion($table) { $conversionRules = $this->getConversionRules(); - return !empty($conversionRules); + return !empty($conversionRules) && isset($conversionRules[$table]); } /** * Convert a value supposed to be transmitted to the data source * + * @param string $table The table where to persist the value * @param string $name The alias or column name * @param mixed $value The value to convert * * @return mixed If conversion was possible, the converted value, otherwise the unchanged value */ - public function persistColumn($name, $value) + public function persistColumn($table, $name, $value) { - $converter = $this->getConverter($name, 'persist'); + $converter = $this->getConverter($table, $name, 'persist'); if ($converter !== null) { $value = $this->$converter($value); } @@ -434,14 +437,15 @@ abstract class Repository implements Selectable /** * Convert a value which was fetched from the data source * + * @param string $table The table the value has been fetched from * @param string $name The alias or column name * @param mixed $value The value to convert * * @return mixed If conversion was possible, the converted value, otherwise the unchanged value */ - public function retrieveColumn($name, $value) + public function retrieveColumn($table, $name, $value) { - $converter = $this->getConverter($name, 'retrieve'); + $converter = $this->getConverter($table, $name, 'retrieve'); if ($converter !== null) { $value = $this->$converter($value); } @@ -452,6 +456,7 @@ abstract class Repository implements Selectable /** * Return the name of the conversion method for the given alias or column name and context * + * @param string $table The datasource's table * @param string $name The alias or column name for which to return a conversion method * @param string $context The context of the conversion: persist or retrieve * @@ -459,12 +464,13 @@ abstract class Repository implements Selectable * * @throws ProgrammingError In case a conversion rule is found but not any conversion method */ - protected function getConverter($name, $context) + protected function getConverter($table, $name, $context) { $conversionRules = $this->getConversionRules(); + $tableRules = $conversionRules[$table]; // Check for a conversion method for the alias/column first - if (array_key_exists($name, $conversionRules) || in_array($name, $conversionRules)) { + if (array_key_exists($name, $tableRules) || in_array($name, $tableRules)) { $methodName = $context . join('', array_map('ucfirst', explode('_', $name))); if (method_exists($this, $methodName)) { return $methodName; @@ -472,22 +478,22 @@ abstract class Repository implements Selectable } // The conversion method for the type is just a fallback, but it is required to exist if defined - if (isset($conversionRules[$name])) { - $identifier = join('', array_map('ucfirst', explode('_', $conversionRules[$name]))); + if (isset($tableRules[$name])) { + $identifier = join('', array_map('ucfirst', explode('_', $tableRules[$name]))); if (! method_exists($this, $context . $identifier)) { // Do not throw an error in case at least one conversion method exists if (! method_exists($this, ($context === 'persist' ? 'retrieve' : 'persist') . $identifier)) { throw new ProgrammingError( 'Cannot find any conversion method for type "%s"' . '. Add a proper conversion method or remove the type definition', - $conversionRules[$name] + $tableRules[$name] ); } Logger::debug( 'Conversion method "%s" for type definition "%s" does not exist in repository "%s".', $context . $identifier, - $conversionRules[$name], + $tableRules[$name], $this->getName() ); } else { @@ -623,7 +629,7 @@ abstract class Repository implements Selectable if ($filter->isExpression()) { $column = $filter->getColumn(); $filter->setColumn($this->requireFilterColumn($table, $column, $query)); - $filter->setExpression($this->persistColumn($column, $filter->getExpression())); + $filter->setExpression($this->persistColumn($table, $column, $filter->getExpression())); } elseif ($filter->isChain()) { foreach ($filter->filters() as $chainOrExpression) { $this->requireFilter($table, $chainOrExpression, $query); @@ -826,7 +832,7 @@ abstract class Repository implements Selectable { $resolved = array(); foreach ($data as $alias => $value) { - $resolved[$this->requireStatementColumn($table, $alias)] = $this->persistColumn($alias, $value); + $resolved[$this->requireStatementColumn($table, $alias)] = $this->persistColumn($table, $alias, $value); } return $resolved; diff --git a/library/Icinga/Repository/RepositoryQuery.php b/library/Icinga/Repository/RepositoryQuery.php index 8048ce434..9a8eb7e83 100644 --- a/library/Icinga/Repository/RepositoryQuery.php +++ b/library/Icinga/Repository/RepositoryQuery.php @@ -153,7 +153,7 @@ class RepositoryQuery implements QueryInterface, Iterator { $this->query->where( $this->repository->requireFilterColumn($this->target, $column, $this), - $this->repository->persistColumn($column, $value) + $this->repository->persistColumn($this->target, $column, $value) ); return $this; } @@ -388,10 +388,10 @@ class RepositoryQuery implements QueryInterface, Iterator } $result = $this->query->fetchOne(); - if ($result !== false && $this->repository->providesValueConversion()) { + if ($result !== false && $this->repository->providesValueConversion($this->target)) { $columns = $this->getColumns(); $column = isset($columns[0]) ? $columns[0] : key($columns); - return $this->repository->retrieveColumn($column, $result); + return $this->repository->retrieveColumn($this->target, $column, $result); } return $result; @@ -409,13 +409,13 @@ class RepositoryQuery implements QueryInterface, Iterator } $result = $this->query->fetchRow(); - if ($result !== false && $this->repository->providesValueConversion()) { + if ($result !== false && $this->repository->providesValueConversion($this->target)) { foreach ($this->getColumns() as $alias => $column) { if (! is_string($alias)) { $alias = $column; } - $result->$alias = $this->repository->retrieveColumn($alias, $result->$alias); + $result->$alias = $this->repository->retrieveColumn($this->target, $alias, $result->$alias); } } @@ -434,12 +434,12 @@ class RepositoryQuery implements QueryInterface, Iterator } $results = $this->query->fetchColumn(); - if (! empty($results) && $this->repository->providesValueConversion()) { + if (! empty($results) && $this->repository->providesValueConversion($this->target)) { $columns = $this->getColumns(); $aliases = array_keys($columns); $column = is_int($aliases[0]) ? $columns[0] : $aliases[0]; foreach ($results as & $value) { - $value = $this->repository->retrieveColumn($column, $value); + $value = $this->repository->retrieveColumn($this->target, $column, $value); } } @@ -460,15 +460,15 @@ class RepositoryQuery implements QueryInterface, Iterator } $results = $this->query->fetchPairs(); - if (! empty($results) && $this->repository->providesValueConversion()) { + if (! empty($results) && $this->repository->providesValueConversion($this->target)) { $columns = $this->getColumns(); $aliases = array_keys($columns); $newResults = array(); foreach ($results as $colOneValue => $colTwoValue) { $colOne = $aliases[0] !== 0 ? $aliases[0] : $columns[0]; $colTwo = count($aliases) < 2 ? $colOne : ($aliases[1] !== 1 ? $aliases[1] : $columns[1]); - $colOneValue = $this->repository->retrieveColumn($colOne, $colOneValue); - $newResults[$colOneValue] = $this->repository->retrieveColumn($colTwo, $colTwoValue); + $colOneValue = $this->repository->retrieveColumn($this->target, $colOne, $colOneValue); + $newResults[$colOneValue] = $this->repository->retrieveColumn($this->target, $colTwo, $colTwoValue); } $results = $newResults; @@ -489,7 +489,7 @@ class RepositoryQuery implements QueryInterface, Iterator } $results = $this->query->fetchAll(); - if (! empty($results) && $this->repository->providesValueConversion()) { + if (! empty($results) && $this->repository->providesValueConversion($this->target)) { $columns = $this->getColumns(); foreach ($results as $row) { foreach ($columns as $alias => $column) { @@ -497,7 +497,7 @@ class RepositoryQuery implements QueryInterface, Iterator $alias = $column; } - $row->$alias = $this->repository->retrieveColumn($alias, $row->$alias); + $row->$alias = $this->repository->retrieveColumn($this->target, $alias, $row->$alias); } } } @@ -545,13 +545,13 @@ class RepositoryQuery implements QueryInterface, Iterator public function current() { $row = $this->iterator->current(); - if ($this->repository->providesValueConversion()) { + if ($this->repository->providesValueConversion($this->target)) { foreach ($this->getColumns() as $alias => $column) { if (! is_string($alias)) { $alias = $column; } - $row->$alias = $this->repository->retrieveColumn($alias, $row->$alias); + $row->$alias = $this->repository->retrieveColumn($this->target, $alias, $row->$alias); } }