Commit d64ba0ca authored by Jochen Rau's avatar Jochen Rau
Browse files

[+BUGFIX] Extbase (Reflection): Fixed a bug that would occur if an array with...

[+BUGFIX] Extbase (Reflection): Fixed a bug that would occur if an array with a non-existing key would be passed to ObjectAccess::getProperty(). Related to #6073.
[+BUGFIX] Extbase (Reflection): ObjectAccess::getProperty() now throws an exception when a property does not exist, fixes #6005.
[~TASK] Extbase (Persistence): Removed implodeAnd() and implodeOr(). You can pass an array of constraints as an argument to logicalAnd() and logicalOr(), or you can pass one or more constraints directly as reguments. Resolves #6735.
[~TASK] Extbase (Persistence): The unions are now translated into LEFT JOIN instead of INNER JOIN. And there is no pid constraint added for (internal) unions anymore. Related to #6735.
[~TASK] Extbase: Empty DateTime field values are now translated into NULL as property value (0->NULL). This is necessary because of the way TYPO3 4.x treats 0 as a "special" timestamp. The same is with incoming values which are not accepted as constructor argument of a DateTime object.
parent 5753d510
......@@ -594,7 +594,11 @@ class Tx_Extbase_Persistence_Mapper_DataMap {
case Tx_Extbase_Persistence_PropertyType::DECIMAL:
return (float) $string;
case Tx_Extbase_Persistence_PropertyType::DATE:
return new DateTime(date('r', $string));
if (empty($string)) { // 0 -> NULL !!!
return NULL;
} else {
return new DateTime(date('c', $string));
}
case Tx_Extbase_Persistence_PropertyType::BOOLEAN:
return (boolean) $string;
default:
......@@ -613,7 +617,7 @@ class Tx_Extbase_Persistence_Mapper_DataMap {
$convertedValue = $propertyValue ? 1 : 0;
} elseif ($propertyValue instanceof Tx_Extbase_DomainObject_AbstractDomainObject) {
$convertedValue = $propertyValue->getUid();
} elseif (is_a($propertyValue, 'DateTime')) {
} elseif ($propertyValue instanceof DateTime) {
$convertedValue = $propertyValue->format('U');
} elseif (is_int($propertyValue)) {
$convertedValue = $propertyValue;
......
......@@ -341,97 +341,56 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
}
/**
* Performs a logical conjunction of the given constraints.
* Performs a logical conjunction of the given constraints. The method takes one or more contraints and concatenates them with a boolean AND.
* It also scepts a single array of constraints to be concatenated.
*
* @param object $constraint1 First constraint
* @param object $constraint2 Second constraint
* @param mixed $constraint1 The first of multiple constraints or an array of constraints.
* @return Tx_Extbase_Persistence_QOM_AndInterface
* @api Passing more than two constraints as arguments is currently not yet conform to the api of FLOW3
* @api
*/
public function logicalAnd($constraint1, $constraint2) {
$constraints = func_get_args();
$numberOfConstraints = func_num_args();
if (func_num_args() > 1) {
$resultingConstraint = array_shift($constraints);
foreach ($constraints as $constraint) {
$resultingConstraint = $this->qomFactory->_and(
$resultingConstraint,
$constraint
);
}
public function logicalAnd($constraint1) {
if (is_array($constraint1)) {
$resultingConstraint = array_shift($constraint1);
$constraints = $constraint1;
} else {
throw new Tx_Extbase_Persistence_Exception_InvalidNumberOfConstraints('There must be at least two constraints. Got only ' . func_num_args() . '.', 1268056288);
}
return $resultingConstraint;
}
/**
* Performs a logical conjunction of the given constraints.
*
* @param array $constraints An array of constraints
* @return Tx_Extbase_Persistence_QOM_AndInterface
*/
public function implodeAnd(array $constraints) {
$numberOfConstraints = count($constraints);
$resultingConstraint = NULL;
if ($numberOfConstraints === 1) {
return array_shift($constraints);
} elseif ($numberOfConstraints > 1) {
$constraints = func_get_args();
$resultingConstraint = array_shift($constraints);
foreach ($constraints as $constraint) {
$resultingConstraint = $this->qomFactory->_and(
$resultingConstraint,
$constraint
);
}
}
if ($resultingConstraint === NULL) {
throw new Tx_Extbase_Persistence_Exception_InvalidNumberOfConstraints('There must be at least one constraint or a non-empty array of constraints given.', 1268056288);
}
foreach ($constraints as $constraint) {
$resultingConstraint = $this->qomFactory->_and(
$resultingConstraint,
$constraint
);
}
return $resultingConstraint;
}
/**
* Performs a logical disjunction of the two given constraints
*
* @param object $constraint1 First constraint
* @param object $constraint2 Second constraint
* @param mixed $constraint1 The first of multiple constraints or an array of constraints.
* @return Tx_Extbase_Persistence_QOM_OrInterface
* @api Passing more than two constraints as arguments is currently not yet conform to the api of FLOW3
* @api
*/
public function logicalOr($constraint1, $constraint2) {
$constraints = func_get_args();
$numberOfConstraints = func_num_args();
if (func_num_args() > 1) {
$resultingConstraint = array_shift($constraints);
foreach ($constraints as $constraint) {
$resultingConstraint = $this->qomFactory->_or(
$resultingConstraint,
$constraint
);
}
public function logicalOr($constraint1) {
if (is_array($constraint1)) {
$resultingConstraint = array_shift($constraint1);
$constraints = $constraint1;
} else {
throw new Tx_Extbase_Persistence_Exception_InvalidNumberOfConstraints('There must be at least two constraints. Got only ' . func_num_args() . '.', 1268056288);
}
return $resultingConstraint;
}
/**
* Performs a logical conjunction of the given constraints.
*
* @param array $constraints An array of constraints
* @return Tx_Extbase_Persistence_QOM_AndInterface
*/
public function implodeOr(array $constraints) {
$numberOfConstraints = count($constraints);
$resultingConstraint = NULL;
if ($numberOfConstraints === 1) {
return array_shift($constraints);
} elseif ($numberOfConstraints > 1) {
$constraints = func_get_args();
$resultingConstraint = array_shift($constraints);
foreach ($constraints as $constraint) {
$resultingConstraint = $this->qomFactory->_or(
$resultingConstraint,
$constraint
);
}
}
if ($resultingConstraint === NULL) {
throw new Tx_Extbase_Persistence_Exception_InvalidNumberOfConstraints('There must be at least one constraint or a non-empty array of constraints given.', 1268056288);
}
foreach ($constraints as $constraint) {
$resultingConstraint = $this->qomFactory->_or(
$resultingConstraint,
$constraint
);
}
return $resultingConstraint;
}
......
......@@ -172,22 +172,20 @@ interface Tx_Extbase_Persistence_QueryInterface {
/**
* Performs a logical conjunction of the two given constraints.
*
* @param object $constraint1 First constraint
* @param object $constraint2 Second constraint
* @param mixed $constraint1 The first of multiple constraints or an array of constraints.
* @return object
* @api
*/
public function logicalAnd($constraint1, $constraint2);
public function logicalAnd($constraint1);
/**
* Performs a logical disjunction of the two given constraints
*
* @param object $constraint1 First constraint
* @param object $constraint2 Second constraint
* @param mixed $constraint1 The first of multiple constraints or an array of constraints.
* @return object
* @api
*/
public function logicalOr($constraint1, $constraint2);
public function logicalOr($constraint1);
/**
* Performs a logical negation of the given constraint
......
......@@ -276,8 +276,7 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis
$this->parseOrderings($query->getOrderings(), $source, $sql);
$this->parseLimitAndOffset($query->getLimit(), $query->getOffset(), $sql);
$tables = array_merge(array_keys($sql['tables']), array_keys($sql['unions']));
foreach ($tables as $tableName) {
foreach (array_keys($sql['tables']) as $tableName) {
if (is_string($tableName) && strlen($tableName) > 0) {
$this->addAdditionalWhereClause($query->getQuerySettings(), $tableName, $sql);
}
......@@ -397,7 +396,7 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis
}
$sql['tables'][$leftTableName] = $leftTableName;
$sql['unions'][$rightTableName] = 'INNER JOIN ' . $rightTableName;
$sql['unions'][$rightTableName] = 'LEFT JOIN ' . $rightTableName;
$joinCondition = $join->getJoinCondition();
if ($joinCondition instanceof Tx_Extbase_Persistence_QOM_EquiJoinCondition) {
......@@ -594,25 +593,25 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis
$childTableName = $columnMap->getChildTableName();
if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_ONE) {
if (isset($parentKeyFieldName)) {
$sql['unions'][$childTableName] = 'INNER JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
$sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
} else {
$sql['unions'][$childTableName] = 'INNER JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $columnName . '=' . $childTableName . '.uid';
$sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $columnName . '=' . $childTableName . '.uid';
}
$className = $this->dataMapper->getType($className, $propertyName);
} elseif ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
if (isset($parentKeyFieldName)) {
$sql['unions'][$childTableName] = 'INNER JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
$sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
} else {
$onStatement = '(' . $tableName . '.' . $columnName . ' LIKE CONCAT(\'%,\',' . $childTableName . '.uid,\',%\')';
$onStatement .= ' OR ' . $tableName . '.' . $columnName . ' LIKE CONCAT(\'%,\',' . $childTableName . '.uid)';
$onStatement .= ' OR ' . $tableName . '.' . $columnName . ' LIKE CONCAT(' . $childTableName . '.uid,\',%\'))';
$sql['unions'][$childTableName] = 'INNER JOIN ' . $childTableName . ' ON ' . $onStatement;
$sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $onStatement;
}
$className = $this->dataMapper->getElementType($className, $propertyName);
} elseif ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
$relationTableName = $columnMap->getRelationTableName();
$sql['unions'][$relationTableName] = 'INNER JOIN ' . $relationTableName . ' ON ' . $tableName . '.uid=' . $relationTableName . '.uid_local';
$sql['unions'][$childTableName] = 'INNER JOIN ' . $childTableName . ' ON ' . $relationTableName . '.uid_foreign=' . $childTableName . '.uid';
$sql['unions'][$relationTableName] = 'LEFT JOIN ' . $relationTableName . ' ON ' . $tableName . '.uid=' . $relationTableName . '.uid_local';
$sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $relationTableName . '.uid_foreign=' . $childTableName . '.uid';
$className = $this->dataMapper->getElementType($className, $propertyName);
} else {
throw new Tx_Extbase_Persistence_Exception('Could not determine type of relation.', 1252502725);
......
......@@ -253,7 +253,7 @@ class Tx_Extbase_Property_Mapper {
try {
$propertyValue = new $targetType($propertyValue);
} catch (Exception $e) {
throw new InvalidArgumentException('Conversion to a ' . $targetType . ' object is not possible. Cause: ' . $e->getMessage(), 1190034628);
$propertyValue = NULL;
}
}
} else {
......
......@@ -43,30 +43,40 @@ class Tx_Extbase_Reflection_ObjectAccess {
/**
* Get a property of a given object.
* Tries to get the property the following ways:
* - if the target is an array, and has this property, we call it.
* - if public getter method exists, call it.
* - if the target object is an instance of ArrayAccess, it gets the property
* on it if it exists.
* - if public getter method exists, call it.
* - if public property exists, return the value of it.
* - else, throw exception
*
* @param object $object Object to get the property from
* @param mixed $subject Object or array to get the property from
* @param string $propertyName name of the property to retrieve
* @return object Value of the property.
* @throws InvalidArgumentException in case $subject was not an object or $propertyName was not a string
* @throws RuntimeException if the property was not accessible
*/
static public function getProperty($object, $propertyName) {
if (!is_object($object) && !is_array($object)) throw new InvalidArgumentException('$object must be an object or an array, ' . gettype($object). ' given.', 1237301367);
static public function getProperty($subject, $propertyName) {
if (!is_object($subject) && !is_array($subject)) throw new InvalidArgumentException('$subject must be an object or array, ' . gettype($subject). ' given.', 1237301367);
if (!is_string($propertyName)) throw new InvalidArgumentException('Given property name is not of type string.', 1231178303);
if (is_array($object) && array_key_exists($propertyName, $object)) {
return $object[$propertyName];
} elseif (is_callable(array($object, $getterMethodName = self::buildGetterMethodName($propertyName)))) {
return call_user_func(array($object, $getterMethodName));
} elseif ($object instanceof ArrayAccess && isset($object[$propertyName])) {
return $object[$propertyName];
} elseif (is_object($object) && array_key_exists($propertyName, get_object_vars($object))) {
return $object->$propertyName;
if (is_array($subject)) {
if (array_key_exists($propertyName, $subject)) {
return $subject[$propertyName];
}
} else {
if (is_callable(array($subject, 'get' . ucfirst($propertyName)))) {
return call_user_func(array($subject, 'get' . ucfirst($propertyName)));
} elseif (is_callable(array($subject, 'is' . ucfirst($propertyName)))) {
return call_user_func(array($subject, 'is' . ucfirst($propertyName)));
} elseif ($subject instanceof ArrayAccess && isset($subject[$propertyName])) {
return $subject[$propertyName];
} elseif (array_key_exists($propertyName, get_object_vars($subject))) {
return $subject->$propertyName;
}
}
return NULL;
throw new RuntimeException('The property "' . $propertyName . '" on the subject was not accessible.', 1263391473);
}
/**
......@@ -81,8 +91,11 @@ class Tx_Extbase_Reflection_ObjectAccess {
static public function getPropertyPath($object, $propertyPath) {
$propertyPathSegments = explode('.', $propertyPath);
foreach ($propertyPathSegments as $pathSegment) {
$object = self::getProperty($object, $pathSegment);
if ($object === NULL) return NULL;
if (is_object($object) && self::isPropertyGettable($object, $pathSegment)) {
$object = self::getProperty($object, $pathSegment);
} else {
return NULL;
}
}
return $object;
}
......@@ -162,16 +175,19 @@ class Tx_Extbase_Reflection_ObjectAccess {
}
return $properties;
}
/**
* Build the getter method name for a given property by capitalizing the
* first letter of the property, and prepending it with "get".
* Tells if the value of the specified property can be retrieved by this Object Accessor.
*
* @param string $property Name of the property
* @return string Name of the getter method name
* @param object $object Object containting the property
* @param string $propertyName Name of the property to check
* @return boolean
*/
static protected function buildGetterMethodName($property) {
return 'get' . ucfirst($property);
static public function isPropertyGettable($object, $propertyName) {
if (!is_object($object)) throw new InvalidArgumentException('$object must be an object, ' . gettype($object). ' given.', 1259828921);
if (array_search($propertyName, array_keys(get_class_vars(get_class($object)))) !== FALSE) return TRUE;
if (is_callable(array($object, 'get' . ucfirst($propertyName)))) return TRUE;
return is_callable(array($object, 'is' . ucfirst($propertyName)));
}
/**
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment