From 634ff10c898e73b61b382624164d6e6dda14cd67 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Tue, 2 Jun 2009 03:43:45 +0000 Subject: [PATCH] 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 --- core/model/DataObject.php | 35 +++++++++++++++++++++++++++++++---- tests/DataObjectTest.php | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/core/model/DataObject.php b/core/model/DataObject.php index 5833d80b7..b7563491e 100644 --- a/core/model/DataObject.php +++ b/core/model/DataObject.php @@ -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}") + ); } /** diff --git a/tests/DataObjectTest.php b/tests/DataObjectTest.php index b55a82805..5fc4725d3 100644 --- a/tests/DataObjectTest.php +++ b/tests/DataObjectTest.php @@ -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 {