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.

General Questions /

General questions about getting started with SilverStripe that don't fit in any of the categories above.

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

Two DataObjects in one form


Go to End


9 Posts   4867 Views

Avatar
Judge

Community Member, 79 Posts

1 March 2010 at 2:31am

This forum seems to be the one that gets the most activity and responses, so I'll post it here, but apologies in advance if there was a more appropriate place to post it.

I have a one-to-one relationship between two data objects Foo and Bar. Foo may have one Bar. Bar will be linked to one Foo.

Foo +--- Bar

In the ModelAdmin scaffolding, I would the user to be able to create or edit a Foo and at the same time create or edit its associated Bar. A second tab on the pop-up Foo form would make sense. The tab is kind of easy - getCMSFields() in the Foo data model can create individual fields or move fields to the second tab, and they all get submitted at once when the pop-up is saved.

Now, can Foo pull in a complete form for Bar into one of the tabs? If so, what happens when they are submitted, since fields in both Foo and Bar could have the same name? Is this simple to do with a few lines of code? Are we looking at an AJAX solution to keep the two forms separate? Or do I need to manually create the fields in Foo that Bar will use, and manually push them into a Bar DataObject on form submission of Foo?

Hope that makes sense!

-- Jason

Avatar
zenmonkey

Community Member, 545 Posts

2 March 2010 at 9:30am

I usually use the DataObject Manager module to control Nested DataObjects. Which is what I thin you're trying to do.

You can use OnBeforeWrite to copy data from one field to another, but that'll only happen on the save, but if you need live real-time updating on multiple fields it'll probably require a JavaScript solution

Avatar
Judge

Community Member, 79 Posts

3 March 2010 at 4:08am

Do you have an example of how this would work? Does the DataObject Manager allow one-to-one relationships to be enforced?

I am banging my head against a brick wall with this thing. I just can't work out whether I need a few choice lines of code to join the dots that I just can't see, or need to manually hard-code a whole lot of functionality that does not exist in SS.

Just on the question of two forms on one screen (same tab or separate tabs) submitted at the same time: since there is no name-spacing on the form item names to keep the items in the two forms separate, I assume this just not possible using the built-in functionality? Or maybe there is a way to tell SS to prefix all the names of form items for a given DataObject (that would make sense to me)?

I have had a play around with getCMSFields() for object Foo, manually inserting extra fields (into a separate tab) that will end up in object Bar. The child Bar can be fetched from within Foo using:

$this->Foo = $this->getComponent('Bar');

This will get either the existing child Bar DataObject or create a blank one if there is no child object yet (there may not even be a parent object yet either). The data from this object can then be used to populate custom form items added to the Foo form.

Now, when the Foo form is submitted, the submitted data from those custom form items need to be fetched and used to update Bar (I'm using the $_POST array, though I'm sure there is a better way).

Now, how would I then save that Bar DataObject to the database, get its ID, add that ID to the current Foo DataObject being saved? The $this->Bar object I set up in getCMSFields() does not seem to be available to the OnBeforeWrite() or write() methods, so something kooky is going on with the calling order.

-- Jason

Avatar
Judge

Community Member, 79 Posts

3 March 2010 at 4:18am

Has *anyone* here managed to implement a one-to-one relationship between two DataObjects and used the built-in DataObject Manager or scaffolding to achieve it?

I don't mean master/detail, or one-to-many, but a real one-to-one.

I'm only asking because all my Google searched turn up is my own posts.

-- Jason

Avatar
carlos

Community Member, 42 Posts

3 March 2010 at 2:59pm

Hi

so you have 2 DataObjects, lets say 'Employee' and 'Desk'

Employee.php

<?php

class Employee extends DataObject {

   static $db = array(
      'Name' => 'Varchar(255),
      'Position' => 'Varchar(255)'
   );

   static $has_one = array(
      'Desk' => 'Desk'
   );
?>

Desk.php

<?php

class Desk extends DataObject {

   static $db = array(
      'Floor' => 'Varchar(255),
      'PhoneNumber' => 'Varchar(255)'
   );

?>

Now you can create a DataModelAdmin (from SS 2.3 I think)

EmployeeAdmin.php

<?php

class EmployeeAdmin extends ModelAdmin {
	
	public static $managed_models = array(
		'Employee',
                'Desk'
	);

	static $url_segment = 'employee';
	static $menu_title = 'Employees';
	
}

?>

Hope it helps

cheers

Avatar
Judge

Community Member, 79 Posts

3 March 2010 at 10:19pm

Edited: 03/03/2010 10:21pm

Thanks Carlos,

That's a good overview of what I have (swap Desk for Address and Employee for PeriodOfEmployment and it is just about spot on).

However, in this example the Desk admin form is not appearing. It only appears (as a table list) if there is a has-many link back from Desks to Employees.

One subtle difference though, and the crux to what I am trying to achieve, is that I don't want to be able to (or to have to) edit the Desks as a separate entity:

* Every Employee brings their own desk to work with them, so when you add an Employee record you add the Desk record at the same time.

* Employees will never swap desks, so when you open the Employee record, the *details* of the desk record are right there with them.

* When an employee leaves, they take their desk with them; delete the Employee and the Desk is deleted.

* Desks are never shared. "One Employee/One Desk" is the company motto.

* Desks are stored in a separate data store, because every Visitor, DeskPlant, BoxOfPaper and CoffeeMachine has their own desk. This keeps the list of desks, no matter who they belong to, all in one place.

-- Jason

One thought just occurred to me - I wonder if the join can be done at the Employee list level? At the moment if you go into the Employee menu you get a list of Employees and you can edit each one. Clicking on the row - anywhere on the row - will edit that employee. If a button could be added to that row to edit the [single, one and only] desk for that employee, then that would serve my purposes nicely; it keeps the "Edit Employee" and "Edit Employee's Desk" together in one record, which is the idea.

Avatar
carlos

Community Member, 42 Posts

3 March 2010 at 10:44pm

hey Judge,

don't quite get why you need a different DataObject for Desk.

what you can do is have just 1 DataObject and create different tabs in ModelAdmin, one for Employee and another for Desk.

you'd need something like this in you DataObject

function getCMSFields() {
		$fields = new FieldSet(
			new TabSet("Root",
				new Tab('Employee',
					new TextField("Name", "Name"),
					new TextField("Position", "Position")
				),
                               new Tab('Desk',
                                       new TextField('Floor','Floor)
                               )
			)
		);

		return $fields;    	
	}

in the list of employees you can add Desk information too with static $summary_fields

hope it helps you

cheers

Avatar
Judge

Community Member, 79 Posts

3 March 2010 at 11:21pm

Thanks again.

I got that far - the main Employee record in the 'main' tab (form fields were automatically set for that) and individual Desk fields set in the second tab. When the form is used to add and edit records, an Employee record in the database is written - that's fine.

The Desk fields hanging off the second tab also get submitted, but there is nothing at the server end to pick them up. I have tried this in Employee::getCMSFields():

$desk = $this->getComponent('Desk');

$desk->setField('DeskName', $_POST['DeskName']);
$desk->setField('Floor', $_POST['Floor']);
$desk->write();

That works when editing an Employee, but not when creating one. I guess what I need there is something to collect up the submitted fields for the Desk, then create or update the Desk row in the database, and finally to link that Desk to the Employee before that is saved.

-- Jason

Go to Top