mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
ENHANCEMENT Allow auto setting a many-to-many relation on ComplexTableField in the similar fashion of auto setting the foreign key for a one-to-many or one-to-many relation.
ENHANCEMENT Added DataObject::many_many_extraFields() for getting the many_many_extraFields fields for a component name on both sides of a many-to-many relation. MINOR Added test for many_many_extraFields in DataObjectTest git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@71613 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
bf07ed2d87
commit
fa4c42d642
@ -1342,6 +1342,90 @@ class DataObject extends ViewableData implements DataObjectInterface,i18nEntityP
|
||||
return isset($items) ? $items : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the many-to-many extra fields specification.
|
||||
*
|
||||
* If you don't specify a component name, it returns all
|
||||
* extra fields for all components available.
|
||||
*
|
||||
* @param string $component Name of component
|
||||
* @return array
|
||||
*/
|
||||
public function many_many_extraFields($component = null) {
|
||||
$classes = ClassInfo::ancestry($this);
|
||||
|
||||
foreach($classes as $class) {
|
||||
if(in_array($class, array('ViewableData', 'Object', 'DataObject'))) continue;
|
||||
|
||||
// Find extra fields for one component
|
||||
if($component) {
|
||||
$SNG_class = singleton($class);
|
||||
$extraFields = $SNG_class->stat('many_many_extraFields');
|
||||
|
||||
// Extra fields are immediately available on this class
|
||||
if(isset($extraFields[$component])) {
|
||||
return $extraFields[$component];
|
||||
}
|
||||
|
||||
$manyMany = $SNG_class->stat('many_many');
|
||||
$candidate = (isset($manyMany[$component])) ? $manyMany[$component] : null;
|
||||
if($candidate) {
|
||||
$SNG_candidate = singleton($candidate);
|
||||
$candidateManyMany = $SNG_candidate->stat('belongs_many_many');
|
||||
|
||||
// Find the relation given the class
|
||||
if($candidateManyMany) foreach($candidateManyMany as $relation => $relatedClass) {
|
||||
if($relatedClass == $class) {
|
||||
$relationName = $relation;
|
||||
}
|
||||
}
|
||||
|
||||
$extraFields = $SNG_candidate->stat('many_many_extraFields');
|
||||
if(isset($extraFields[$relationName])) {
|
||||
return $extraFields[$relationName];
|
||||
}
|
||||
}
|
||||
|
||||
$manyMany = $SNG_class->stat('belongs_many_many');
|
||||
$candidate = (isset($manyMany[$component])) ? $manyMany[$component] : null;
|
||||
if($candidate) {
|
||||
$SNG_candidate = singleton($candidate);
|
||||
$candidateManyMany = $SNG_candidate->stat('many_many');
|
||||
|
||||
// Find the relation given the class
|
||||
if($candidateManyMany) foreach($candidateManyMany as $relation => $relatedClass) {
|
||||
if($relatedClass == $class) {
|
||||
$relationName = $relation;
|
||||
}
|
||||
}
|
||||
|
||||
$extraFields = $SNG_candidate->stat('many_many_extraFields');
|
||||
if(isset($extraFields[$relationName])) {
|
||||
return $extraFields[$relationName];
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Find all the extra fields for all components
|
||||
$newItems = eval("return (array){$class}::\$many_many_extraFields;");
|
||||
|
||||
foreach($newItems as $k => $v) {
|
||||
if(!is_array($v)) {
|
||||
user_error(
|
||||
"$class::\$many_many_extraFields has a bad entry: "
|
||||
. var_export($k, true) . " => " . var_export($v, true)
|
||||
. ". Each many_many_extraFields entry should map to a field specification array.",
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return isset($items) ? array_merge($newItems, $items) : $newItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return information about a many-to-many component.
|
||||
* The return value is an array of (parentclass, childclass). If $component is null, then all many-many
|
||||
|
@ -453,21 +453,51 @@ JS;
|
||||
* this method.
|
||||
*/
|
||||
function getCustomFieldsFor($childData) {
|
||||
// If the fieldset is passed, use it
|
||||
if(is_a($this->detailFormFields,"Fieldset")) {
|
||||
if($this->detailFormFields instanceof Fieldset) {
|
||||
return $this->detailFormFields;
|
||||
|
||||
// Else use the formfields returned from the object via a string method call.
|
||||
} else {
|
||||
if(!is_string($this->detailFormFields)) $this->detailFormFields = "getCMSFields";
|
||||
$functioncall = $this->detailFormFields;
|
||||
if(!$childData->hasMethod($functioncall)) $functioncall = "getCMSFields";
|
||||
$fieldsMethod = $this->detailFormFields;
|
||||
|
||||
return $childData->$functioncall();
|
||||
if(!is_string($fieldsMethod)) {
|
||||
$this->detailFormFields = 'getCMSFields';
|
||||
$fieldsMethod = 'getCMSFields';
|
||||
}
|
||||
|
||||
if(!$childData->hasMethod($fieldsMethod)) {
|
||||
$fieldsMethod = 'getCMSFields';
|
||||
}
|
||||
|
||||
$fields = $childData->$fieldsMethod();
|
||||
}
|
||||
|
||||
if(!$this->relationAutoSetting) {
|
||||
return $fields;
|
||||
}
|
||||
|
||||
$parentClass = DataObject::get_by_id($this->getParentClass(), $this->sourceID());
|
||||
$manyManyExtraFields = $parentClass->many_many_extraFields($this->name);
|
||||
if($manyManyExtraFields) {
|
||||
foreach($manyManyExtraFields as $fieldName => $fieldSpec) {
|
||||
$dbField = new Varchar('ctf[extraFields][' . $fieldName . ']');
|
||||
$fields->addFieldToTab('Root.Main', $dbField->scaffoldFormField($fieldName));
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function getFieldsFor($childData) {
|
||||
// See if our parent class has any many_many relations by this source class
|
||||
$SNG_parentClass = singleton($this->getParentClass());
|
||||
$manyManyRelations = $SNG_parentClass->many_many();
|
||||
$manyManyRelationName = null;
|
||||
|
||||
if($manyManyRelations) foreach($manyManyRelations as $relation => $class) {
|
||||
if($class == $this->sourceClass()) {
|
||||
$manyManyRelationName = $relation;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the relation value to related records
|
||||
if(!$childData->ID && $this->getParentClass()) {
|
||||
// make sure the relation-link is existing, even if we just add the sourceClass and didn't save it
|
||||
@ -482,32 +512,28 @@ JS;
|
||||
|
||||
// only add childID if we're not adding a record
|
||||
if($childData->ID) {
|
||||
$detailFields->push(new HiddenField("ctf[childID]","",$childData->ID));
|
||||
$detailFields->push(new HiddenField('ctf[childID]', '', $childData->ID));
|
||||
}
|
||||
|
||||
// add a namespaced ID instead thats "converted" by saveComplexTableField()
|
||||
$detailFields->push(new HiddenField("ctf[ClassName]","",$this->sourceClass()));
|
||||
$detailFields->push(new HiddenField('ctf[ClassName]', '', $this->sourceClass()));
|
||||
|
||||
if($this->getParentClass()) {
|
||||
$parentIdName = $this->getParentIdName($this->getParentClass(), $this->sourceClass());
|
||||
/*
|
||||
if(!$parentIdName) {
|
||||
user_error("ComplexTableField::DetailForm() Cannot automatically
|
||||
determine 'has-one'-relationship to parent class " . $this->ctf->getParentClass() . ",
|
||||
please use setParentClass() to set it manually",
|
||||
E_USER_WARNING);
|
||||
return;
|
||||
if($manyManyRelationName && $this->relationAutoSetting) {
|
||||
$detailFields->push(new HiddenField('ctf[manyManyRelation]', '', $manyManyRelationName));
|
||||
$detailFields->push(new HiddenField('ctf[parentClass]', '', $this->getParentClass()));
|
||||
$detailFields->push(new HiddenField('ctf[sourceID]', '', $this->sourceID()));
|
||||
}
|
||||
*/
|
||||
|
||||
$parentIdName = $this->getParentIdName($this->getParentClass(), $this->sourceClass());
|
||||
if($parentIdName) {
|
||||
// add relational fields
|
||||
$detailFields->push(new HiddenField("ctf[parentClass]"," ",$this->getParentClass()));
|
||||
$detailFields->push(new HiddenField('ctf[parentClass]', '', $this->getParentClass()));
|
||||
|
||||
if( $this->relationAutoSetting ) {
|
||||
if($this->relationAutoSetting) {
|
||||
// Hack for model admin: model admin will have included a dropdown for the relation itself
|
||||
$detailFields->removeByName($parentIdName);
|
||||
$detailFields->push(new HiddenField("$parentIdName"," ",$this->sourceID()));
|
||||
$detailFields->push(new HiddenField($parentIdName, '', $this->sourceID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -580,16 +606,34 @@ JS;
|
||||
$form->saveInto($childData);
|
||||
$childData->write();
|
||||
|
||||
// Save the many many relationship if it's available
|
||||
if(isset($data['ctf']['manyManyRelation'])) {
|
||||
$parentRecord = DataObject::get_by_id($data['ctf']['parentClass'], (int) $data['ctf']['sourceID']);
|
||||
$relationName = $data['ctf']['manyManyRelation'];
|
||||
$extraFields = array();
|
||||
|
||||
if(isset($data['ctf']['extraFields'])) {
|
||||
foreach($data['ctf']['extraFields'] as $field => $value) {
|
||||
$extraFields[$field] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$componentSet = $parentRecord->getManyManyComponents($relationName);
|
||||
$componentSet->add($childData, $extraFields);
|
||||
}
|
||||
|
||||
$closeLink = sprintf(
|
||||
'<small><a href="' . $_SERVER['HTTP_REFERER'] . '" onclick="javascript:window.top.GB_hide(); return false;">(%s)</a></small>',
|
||||
_t('ComplexTableField.CLOSEPOPUP', 'Close Popup')
|
||||
);
|
||||
|
||||
$message = sprintf(
|
||||
_t('ComplexTableField.SUCCESSADD', 'Added %s %s %s'),
|
||||
$childData->singular_name(),
|
||||
'<a href="' . $this->Link() . '/item/' . $childData->ID . '/edit">' . $childData->Title . '</a>',
|
||||
$closeLink
|
||||
);
|
||||
|
||||
$form->sessionMessage($message, 'good');
|
||||
|
||||
Director::redirectBack();
|
||||
|
@ -588,6 +588,23 @@ class DataObjectTest extends SapphireTest {
|
||||
// @todo test has_many and many_many relations
|
||||
}
|
||||
|
||||
function testManyManyExtraFields() {
|
||||
$player = $this->fixture->objFromFixture('DataObjectTest_Player', 'player1');
|
||||
$team = $this->fixture->objFromFixture('DataObjectTest_Team', 'team1');
|
||||
|
||||
// Extra fields are immediately available on the Team class (defined in $many_many_extraFields)
|
||||
$teamExtraFields = $team->many_many_extraFields('Players');
|
||||
$this->assertEquals($teamExtraFields, array(
|
||||
'Position' => 'Varchar(100)'
|
||||
));
|
||||
|
||||
// We'll have to go through the relation to get the extra fields on Player
|
||||
$playerExtraFields = $player->many_many_extraFields('Teams');
|
||||
$this->assertEquals($playerExtraFields, array(
|
||||
'Position' => 'Varchar(100)'
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DataObjectTest_Player extends Member implements TestOnly {
|
||||
@ -617,6 +634,12 @@ class DataObjectTest_Team extends DataObject implements TestOnly {
|
||||
'Players' => 'DataObjectTest_Player'
|
||||
);
|
||||
|
||||
static $many_many_extraFields = array(
|
||||
'Players' => array(
|
||||
'Position' => 'Varchar(100)'
|
||||
)
|
||||
);
|
||||
|
||||
function getDynamicField() {
|
||||
return 'dynamicfield';
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user