Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

Data Model Questions /

Moderators: martimiz, Sean, Ed, biapar, Willr, Ingo, swaiba

get parent relation ID before writting the Object


Go to End


12 Posts   14083 Views

Avatar
jpmcc

Community Member, 20 Posts

16 January 2015 at 3:41am

Another bump.

Tractorcow's approach is so close to being perfect. The mentioned downside of the ID being 0 in the child getCMSFields() method, unfortunately, is the killer point. My child object will also have children and I only show the child GridField if the child has actually been saved (so checking $this->ID, as per Andrew Houle's approach). Would upvote if the facility existed though.

It is a shame that the GridField doesn't pass in the parent object ID to the child object if it is being created as a matter of course.

One of the main reasons I would like to know the parent ID is due to the behaviour of SilverStripe when displaying the parent information (which the GridField does actually add to the form anyway, in a readonly fashion). If there are over 100 objects of the parent object type, rather than displaying the parent title, it just displays the ID - which isn't really helpful (in a parent has_many, child has_one relationship anyway). If the child object has already been created, that can be handled - and I do, but not during creation when only the parent ID is presented in the form.

Avatar
kinglozzer

Community Member, 187 Posts

16 January 2015 at 6:19am

Hi all,

Here’s an alternative approach that might be useful. This approach allows you to modify the fields that getCMSFields() creates - for example, showing different fields if the "parent" object has a certain property set.

I'm not sure if this exact example will solve any of the problems in this thread, I just thought I should post this feature in case it's of some use!

<?php

class Product extends Page {
	public function getCMSFields() {
		$fields = parent::getCMSFields();
		
		// Models
		$modelsConfig = GridFieldConfig_RecordEditor::create();
		$editComponent = $modelsConfig->getComponentByType('GridFieldDetailForm');

		$self = $this; // PHP 5.3 support - $this can't be used in closures
		$editComponent->setItemEditFormCallback(function($form, $itemRequest) use ($self) {
			// If the "parent" page has a property set
			if($self->SomeProperty) {
				$record = $form->getRecord(); // Get the record being edited (the "child")

				// Note that we have to manually pass the value of the record here
				$textField = TextField::create('ChildProperty', 'Some field', $record->ChildProperty);
				$form->Fields()->push($textField);
			}
		});

		$modelsField = new GridField('Models', 'Product Models', $this->Models(), $modelsConfig);
		$fields->addFieldToTab('Root.Models', $modelsField);
		
		return $fields;
	}
}

Loz

Avatar
jpmcc

Community Member, 20 Posts

22 October 2015 at 12:35am

Edited: 22/10/2015 12:39am

ok, revisiting this and I think Loz's approach is something to work with. Looking at the sequence of events, the GridField does assign the parent ID to the new child object, but only after getCMSFields() has been called on it. However, the ItemEditFormCallback method is called after that - so at that point the the new child object does have access to the parent ID.

This is probably still a bit hacky, but it appears to work for my purposes.

In my child objects' getCMSFields() method, I check for the parent ID. If it isn't present, I just return the results from parent::getCMSFields(). I also set an internal member "$reload" to true.

In the callback, I check if the child object should reload. If so, I replace the contents of the $form's fields with a call to the child's getCMSFields() method again. so:

$editComponent->setItemEditFormCallback(function($form, $itemRequest) use ($self) {
	$child = $form->getRecord();
	if($child->shouldReload()) {  //just returns the protected $reload member
		$form->setFields($child->getCMSFields());
		//there are some bits to reset here (pulled from GridFieldDetailForm_ItemRequest::ItemEditForm() )
		if($form->Fields()->hasTabset()) {
			$form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
		}
	}
});

This appears to work. When re-editing the child object, it doesn't get reloaded in the callback as we already have the parent ID.

I guess this wouldn't be required if the parent ID was set before the call to the child object's getCMSFields() method in the first place. There doesn't look like there would be any impact if that was the case.

Jason.

Avatar
micschk

Community Member, 22 Posts

4 February 2016 at 11:57pm

I also have to show some data from the parent relation ID (foreign ID) in getCMSFields().
Here's what I came up with (works for ModelAdmin gridfields, should probably work for regular gridfields as well).

This code gets the parent relation ID in getCMSFields(), which gets called from GridFieldDetailForm:

            // When adding a request to an existing contact, load the Contact by foreign ID from the GF supplied params
            $ctrl = Controller::curr();
            if( ! $this->ID
                && $ctrl->request->Param('ModelClass')=="Contact" 
                && $cID = $ctrl->request->Param('ID') ){
//               Available parameters in GridFieldDetailForms:
//                    [ModelClass] => Contact
//                    [Action] => ItemEditForm
//                    [FieldName] => Requests
//                    [ID] => 30
//                    [ClassName] => Request_Information
                $contact = Contact::get()->ByID($cID);
            }

Go to Top