The problem at play here is the difference between the DataObject
/Page
and the Controller
. Looping over $Children
returns you a DataObject
whereas the Form
function and template variable are part of UserDefinedForm
's controller.
The other answer shows one working solution however it has some hair on it:
- Jumping scope to your controller to pass an ID to get your form
- Additional DB query
- Requires all the child pages to be of type
UserDefinedForm
We can implement a more generic solution that removes some of those elements and making your code a little more maintainable.
Take the following which would be added to the Page
class (not the controller):
function getInLoopForm() {
if (in_array('UserDefinedForm', $this->ClassAncestry)) {
$controllerName = $this->ClassName . '_Controller';
$controller = $controllerName::create($this);
if ($controller->hasMethod('Form')) {
return $controller->Form();
}
}
return false;
}
The first part of that checks whether the current object has UserDefinedForm
in its class ancestry. If it is, we then create the appropriate controller and return the form.
Your template code would look like this instead:
<% loop $Children %>
<div>
<h2>$Title</h2>
$InLoopForm
</div>
<% end_loop %>
This solution is generic for three reasons:
- In our
getInLoopForm
function, the value "UserDefinedForm" can be replaced with any class that extends Page
. It could even be brought out to a YML value if you were so inclined.
- For SilverStripe, controller names for pages must match "{PageClassName}_Controller" so we can abuse that by working out the controller name dynamically. This allows for you to extend
UserDefinedForm
and its controller and we can still call the right function.
- You only require your
DataObject
to access the form, you don't need your own controller.