BUGFIX Excluding names of CompositeDBFields from DataObject::custom_database_fields() and including the actually generated columns in CompositeDBField->compositeDatabaseFields(). This is necessary for DataObject->hasField() to work correctly and avoid generated queries with wrong column identifiers

BUGFIX Querying db() in DataObject->hasField() in addition to hasDatabaseField(), as the two might differ when CompositeDBFields are used
MINOR Added DataObjectTest->hasDatabaseField()

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@78235 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2009-06-02 03:43:45 +00:00
parent e1e9295fb5
commit 634ff10c89
2 changed files with 63 additions and 4 deletions

View File

@ -182,15 +182,37 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
}
/**
* Get all database fields explicitly defined on a class in {@link DataObject::$db} and {@link DataObject::$has_one}
* Get all database columns explicitly defined on a class in {@link DataObject::$db}
* and {@link DataObject::$has_one}. Resolves instances of {@link CompositeDBField}
* into the actual database fields, rather than the name of the field which
* might not equate a database column.
*
* @uses CompositeDBField->compositeDatabaseFields()
*
* @param string $class
* @return array
* @return array Map of fieldname to specification, similiar to {@link DataObject::$db}.
*/
public static function custom_database_fields($class) {
$fields = Object::uninherited_static($class, 'db');
// Remove CompositeDBField instances, and replace with the actually used fields
if($fields) foreach($fields as $fieldName => $fieldSpec) {
$fieldClass = singleton($class)->db($fieldName);
// Strip off any parameters
if(strpos('(', $fieldClass) !== FALSE) $fieldClass = substr(0,strpos('(', $fieldClass), $fieldClass);
if(ClassInfo::classImplements($fieldClass, 'CompositeDBField')) {
// Remove the original fieldname, its not an actual database column
unset($fields[$fieldName]);
// Add all composite columns
$compositeFields = singleton($fieldClass)->compositeDatabaseFields();
if($compositeFields) foreach($compositeFields as $compositeName => $spec) {
$fields["{$fieldName}{$compositeName}"] = $spec;
}
}
}
// Add has_one relationships
$hasOne = Object::uninherited_static($class, 'has_one');
if($hasOne) foreach(array_keys($hasOne) as $field) {
$fields[$field . 'ID'] = 'ForeignKey';
}
@ -1964,7 +1986,12 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @return boolean True if the given field exists
*/
public function hasField($field) {
return array_key_exists($field, $this->record) || $this->hasDatabaseField($field) || $this->hasMethod("get{$field}");
return (
array_key_exists($field, $this->record)
|| $this->hasDatabaseField($field)
|| array_key_exists($field, $this->db())
|| $this->hasMethod("get{$field}")
);
}
/**

View File

@ -692,6 +692,38 @@ class DataObjectTest extends SapphireTest {
}
}
function hasDatabaseField() {
$team = singleton('DataObjectTest_Team');
$subteam = singleton('DataObjectTest_SubTeam');
$this->assertTrue(
$team->hasDatabaseField('Title'),
"hasOwnDatabaseField() works with \$db fields"
);
$this->assertTrue(
$team->hasDatabaseField('CaptainID'),
"hasOwnDatabaseField() works with \$has_one fields"
);
$this->assertFalse(
$team->hasDatabaseField('NonExistentField'),
"hasOwnDatabaseField() doesn't detect non-existend fields"
);
$this->assertTrue(
$team->hasDatabaseField('DecoratedDatabaseField'),
"hasOwnDatabaseField() works with decorated fields"
);
$this->assertFalse(
$team->hasDatabaseField('SubclassDatabaseField'),
"hasOwnDatabaseField() doesn't pick up fields in subclasses on parent class"
);
$this->assertFalse(
$subteam->hasDatabaseField('SubclassDatabaseField'),
"hasOwnDatabaseField() picks up fields in subclasses"
);
}
}
class DataObjectTest_Player extends Member implements TestOnly {