mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Merge branch 'master' of git@github.com:sminnee/sapphire
From: Sam Minnee <sam@silverstripe.com> git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@90072 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
9a83be9140
commit
709f162f13
@ -483,6 +483,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
$this->addWrapperMethod($relationship, 'getComponent');
|
||||
}
|
||||
}
|
||||
if($belongsTo = $this->belongs_to()) foreach(array_keys($belongsTo) as $relationship) {
|
||||
$this->addWrapperMethod($relationship, 'getComponent');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1192,31 +1195,36 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
if(isset($this->components[$componentName])) {
|
||||
return $this->components[$componentName];
|
||||
}
|
||||
|
||||
if($componentClass = $this->has_one($componentName)) {
|
||||
$childID = $this->getField($componentName . 'ID');
|
||||
|
||||
if($childID && is_numeric($childID)) {
|
||||
$component = DataObject::get_by_id($componentClass,$childID);
|
||||
|
||||
if($class = $this->has_one($componentName)) {
|
||||
$joinField = $componentName . 'ID';
|
||||
$joinID = $this->getField($joinField);
|
||||
|
||||
if($joinID) {
|
||||
$component = DataObject::get_by_id($class, $joinID);
|
||||
}
|
||||
|
||||
// If no component exists, create placeholder object
|
||||
if(!isset($component)) {
|
||||
$component = new $componentClass();
|
||||
// We may have had an orphaned ID that needs to be cleaned up
|
||||
$this->setField($componentName . 'ID', 0);
|
||||
|
||||
if(!isset($component) || !$component) {
|
||||
$component = new $class();
|
||||
}
|
||||
|
||||
// If no component exists, create placeholder object
|
||||
if(!$component) {
|
||||
$component = new $componentClass();
|
||||
} elseif($class = $this->belongs_to($componentName)) {
|
||||
$joinField = $this->getRemoteJoinField($componentName, 'belongs_to');
|
||||
$joinID = $this->ID;
|
||||
|
||||
if($joinID) {
|
||||
$component = DataObject::get_one($class, "\"$joinField\" = $joinID");
|
||||
}
|
||||
|
||||
if(!isset($component) || !$component) {
|
||||
$component = new $class();
|
||||
$component->$joinField = $this->ID;
|
||||
}
|
||||
|
||||
$this->components[$componentName] = $component;
|
||||
return $component;
|
||||
} else {
|
||||
user_error("DataObject::getComponent(): Unknown 1-to-1 component '$componentName' on class '$this->class'", E_USER_ERROR);
|
||||
throw new Exception("DataObject->getComponent(): Could not find component '$componentName'.");
|
||||
}
|
||||
|
||||
$this->components[$componentName] = $component;
|
||||
return $component;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1248,7 +1256,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
user_error("DataObject::getComponents(): Unknown 1-to-many component '$componentName' on class '$this->class'", E_USER_ERROR);
|
||||
}
|
||||
|
||||
$joinField = $this->getRemoteJoinField($componentName);
|
||||
$joinField = $this->getRemoteJoinField($componentName, 'has_many');
|
||||
|
||||
if($this->isInDB()) { //Check to see whether we should query the db
|
||||
$query = $this->getComponentsQuery($componentName, $filter, $sort, $join, $limit);
|
||||
@ -1288,7 +1296,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
user_error("DataObject::getComponentsQuery(): Unknown 1-to-many component '$componentName' on class '$this->class'", E_USER_ERROR);
|
||||
}
|
||||
|
||||
$joinField = $this->getRemoteJoinField($componentName);
|
||||
$joinField = $this->getRemoteJoinField($componentName, 'has_many');
|
||||
|
||||
$id = $this->getField("ID");
|
||||
|
||||
@ -1304,20 +1312,21 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
* field can be found it defaults to 'ParentID'.
|
||||
*
|
||||
* @param string $component
|
||||
* @param string $type the join type - either 'has_many' or 'belongs_to'
|
||||
* @return string
|
||||
*/
|
||||
public function getRemoteJoinField($component) {
|
||||
$remoteClass = $this->has_many($component, false);
|
||||
public function getRemoteJoinField($component, $type = 'has_many') {
|
||||
$remoteClass = $this->$type($component, false);
|
||||
|
||||
if(!$remoteClass) {
|
||||
throw new Exception("Unknown has_many component '$component' on class '$this->class'");
|
||||
throw new Exception("Unknown $type component '$component' on class '$this->class'");
|
||||
}
|
||||
|
||||
if($fieldPos = strpos($remoteClass, '.')) {
|
||||
return substr($remoteClass, $fieldPos + 1) . 'ID';
|
||||
}
|
||||
|
||||
$remoteRelations = array_flip(Object::combined_static($this->has_many($component), 'has_one', 'DataObject'));
|
||||
$remoteRelations = array_flip(Object::combined_static($remoteClass, 'has_one', 'DataObject'));
|
||||
|
||||
// look for remote has_one joins on this class or any parent classes
|
||||
foreach(array_reverse(ClassInfo::ancestry($this)) as $class) {
|
||||
@ -1480,7 +1489,34 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
}
|
||||
return isset($items) ? $items : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the class of a remote belongs_to relationship. If no component is specified a map of all components and
|
||||
* their class name will be returned.
|
||||
*
|
||||
* @param string $component
|
||||
* @param bool $classOnly If this is TRUE, than any has_many relationships in the form "ClassName.Field" will have
|
||||
* the field data stripped off. It defaults to TRUE.
|
||||
* @return string|array
|
||||
*/
|
||||
public function belongs_to($component = null, $classOnly = true) {
|
||||
$belongsTo = Object::combined_static($this->class, 'belongs_to', 'DataObject');
|
||||
|
||||
if($component) {
|
||||
if($belongsTo && array_key_exists($component, $belongsTo)) {
|
||||
$belongsTo = $belongsTo[$component];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if($belongsTo && $classOnly) {
|
||||
return preg_replace('/(.+)?\..+/', '$1', $belongsTo);
|
||||
} else {
|
||||
return $belongsTo ? $belongsTo : array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all of the database fields defined in self::$db and all the parent classes.
|
||||
* Doesn't include any fields specified by self::$has_one. Use $this->has_one() to get these fields
|
||||
@ -3240,12 +3276,28 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
public static $default_records = null;
|
||||
|
||||
/**
|
||||
* one-to-one relationship definitions.
|
||||
* This is a map from component name to data type.
|
||||
* One-to-zero relationship defintion. This is a map of component name to data type. In order to turn this into a
|
||||
* true one-to-one relationship you can add a {@link DataObject::$belongs_to} relationship on the child class.
|
||||
*
|
||||
* Note that you cannot have a has_one and belongs_to relationship with the same name.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $has_one = null;
|
||||
|
||||
|
||||
/**
|
||||
* A meta-relationship that allows you to define the reverse side of a {@link DataObject::$has_one}.
|
||||
*
|
||||
* This does not actually create any data structures, but allows you to query the other object in a one-to-one
|
||||
* relationship from the child object. If you have multiple belongs_to links to another object you can use the
|
||||
* syntax "ClassName.HasOneName" to specify which foreign has_one key on the other object to use.
|
||||
*
|
||||
* Note that you cannot have a has_one and belongs_to relationship with the same name.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $belongs_to;
|
||||
|
||||
/**
|
||||
* This defines a one-to-many relationship. It is a map of component name to the remote data class.
|
||||
*
|
||||
|
@ -23,7 +23,8 @@ abstract class DataObjectDecorator extends Extension {
|
||||
*/
|
||||
protected static $decoratable_statics = array(
|
||||
'db' => true,
|
||||
'has_one' => true,
|
||||
'has_one' => true,
|
||||
'belongs_to' => true,
|
||||
'indexes' => true,
|
||||
'defaults' => true,
|
||||
'has_many' => true,
|
||||
|
@ -846,6 +846,41 @@ class DataObjectTest extends SapphireTest {
|
||||
|
||||
$this->assertEquals('CurrentCompanyID', $company->getRemoteJoinField('CurrentStaff'));
|
||||
$this->assertEquals('PreviousCompanyID', $company->getRemoteJoinField('PreviousStaff'));
|
||||
|
||||
$ceo = new DataObjectTest_CEO();
|
||||
|
||||
$this->assertEquals('CEOID', $ceo->getRemoteJoinField('Company', 'belongs_to'));
|
||||
$this->assertEquals('PreviousCEOID', $ceo->getRemoteJoinField('PreviousCompany', 'belongs_to'));
|
||||
}
|
||||
|
||||
public function testBelongsTo() {
|
||||
$company = new DataObjectTest_Company();
|
||||
$ceo = new DataObjectTest_CEO();
|
||||
|
||||
$company->write();
|
||||
$ceo->write();
|
||||
|
||||
$company->CEOID = $ceo->ID;
|
||||
$company->write();
|
||||
|
||||
$this->assertEquals($company->ID, $ceo->Company()->ID, 'belongs_to returns the right results.');
|
||||
|
||||
$ceo = new DataObjectTest_CEO();
|
||||
$ceo->write();
|
||||
|
||||
$this->assertTrue (
|
||||
$ceo->Company() instanceof DataObjectTest_Company,
|
||||
'DataObjects across belongs_to relations are automatically created.'
|
||||
);
|
||||
$this->assertEquals($ceo->ID, $ceo->Company()->CEOID, 'Remote IDs are automatically set.');
|
||||
|
||||
$ceo->write(false, false, false, true);
|
||||
$this->assertTrue($ceo->Company()->isInDB(), 'write() writes belongs_to components to the database.');
|
||||
|
||||
$newCEO = DataObject::get_by_id('DataObjectTest_CEO', $ceo->ID);
|
||||
$this->assertEquals (
|
||||
$ceo->Company()->ID, $newCEO->Company()->ID, 'belongs_to can be retrieved from the database.'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -963,6 +998,11 @@ class DataObjectTest_ValidatedObject extends DataObject implements TestOnly {
|
||||
}
|
||||
|
||||
class DataObjectTest_Company extends DataObject {
|
||||
public static $has_one = array (
|
||||
'CEO' => 'DataObjectTest_CEO',
|
||||
'PreviousCEO' => 'DataObjectTest_CEO'
|
||||
);
|
||||
|
||||
public static $has_many = array (
|
||||
'CurrentStaff' => 'DataObjectTest_Staff.CurrentCompany',
|
||||
'PreviousStaff' => 'DataObjectTest_Staff.PreviousCompany'
|
||||
@ -976,6 +1016,13 @@ class DataObjectTest_Staff extends DataObject {
|
||||
);
|
||||
}
|
||||
|
||||
class DataObjectTest_CEO extends DataObjectTest_Staff {
|
||||
public static $belongs_to = array (
|
||||
'Company' => 'DataObjectTest_Company.CEO',
|
||||
'AnotherCompany' => 'DataObjectTest_Company.PreviousCEO'
|
||||
);
|
||||
}
|
||||
|
||||
DataObject::add_extension('DataObjectTest_Team', 'DataObjectTest_Team_Decorator');
|
||||
|
||||
?>
|
||||
|
Loading…
x
Reference in New Issue
Block a user