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.

Data Model Questions

Parent Controller-methods aren`t available in Custom Controller?


Go to End
Reply

10 Posts   2604 Views

Avatar
mtz

16 August 2010 at 1:21am Community Member, 17 Posts

Hello,

im a little bit confused about the fact that i don`t get an contoller-Object back when im calling the
DataObject::get_one method in custom controller:

class App_Controller extends Page_Controller {

   function getSiteviaURLSegment($arguments) {
            
      $params = $arguments->allparams();
      $whereStatement = "URLSegment = '" . $params['URLSegment'] . "'";

      return DataObject::get_one("Page", $whereStatement);
      
....

   }

}

The Object im getting back is an ContentPage - Object in my Case. But i expected an Page-Controller - Object.
That also seems to be reason for the fact that im unable to call the parent Controller-Methods like:

$page = getSiteviaURLSegment($arguments);

return $page->someFunctionThatIhaveDefinedintheparentPage_Controller();

help Anybody?

Cheers,
Mattes

Avatar
mtz

16 August 2010 at 9:37pm Community Member, 17 Posts

ok, by trying around i found out that the init() - Function in Page_Contoller is fireing by loading my AppController. So what is about the other Page_Controller-methods?

i don`t get it .... ;(

Avatar
MarcusDalgren

16 August 2010 at 10:37pm Community Member, 288 Posts

DataObject::get_one() does just that, it gets a DataObject or in this case a Page object.
That's what you get back, not the controller.

Could you explain what you're trying to do?

Avatar
Martijn

16 August 2010 at 11:12pm Community Member, 271 Posts

App_Controller extends Page_Controller : then App_Controller has all the methods defined in Page_Controller....

Avatar
tilluigi

17 August 2010 at 12:42am (Last edited: 17 August 2010 12:44am), Community Member, 9 Posts

hi, i am here with MTZ trying to solve this.

the problem we are having is that the dataRecord property of our custom controller is empty.
we tried to set the property manually in one of the Controller's action methods, but it didn't work.

In the documentation it says that the ContentController is passed a DataObject on construction and that the DataRecord is being fetched from the SiteTree based on the URLSegment. however, this is not happening in our case, our DataRecord is always empty/ a new object:

"Its constructor is passed a DataObject which is stored in $this->dataRecord. Any unrecognised method calls, for example, Title() and Content(), will be passed along to the data record.

Subclasses of ContentController are generally instantiated by ModelAsController; this will create a controller based on the URLSegment action variable, by looking in the SiteTree table."
(http://api.silverstripe.org/2.4/sapphire/control/Page_Controller.html)

How do we have to configure SS so our controller will be instantiated WITH a DataObject (by ModelAsController?)

Our Rules look like this:

Director::addRules(50, array(
   'myapp/$Action/$URLSegment' => 'AppController'
));

The controller (essentials) like this:

class AppController extends Page_Controller {

   
function content($arguments) {

      $html = '';
      
      $html .= $this->Title; // THIS WILL ALWAYS BE EMPTY
      
      return $html;
   }
   

}

if this is solved, the next question would be how to use a custom template for our controller..hehe..thanks for your help
tilluigi

Avatar
MarcusDalgren

17 August 2010 at 1:53am Community Member, 288 Posts

Since you made your own URL rule for AppController then ModelAsController won't do it's thing. Like the docs say it attaches a DataRecord when __construct() is run. This is because the Object class runs defineMethods() on __construct() which dynamically adds methods from extensions etc.

They do this and set a property called failover to the dataRecord like this (from ContentController.php __construct()):

$this->dataRecord = $dataRecord;
$this->failover = $this->dataRecord;

The crux is that this has to be done before parent::__construct() is called, otherwise it won't work. The failover concept comes from ViewableData. Also keep in mind that if you're extending ContentController (which you are indirectly) then it's pretty much assuming that the attached dataRecord will be a SiteTree object of some sort and will fail under some circumstances if the dataRecord isn't.

I can help you guys all the way down this road since I've made my own Controller implementation that works outside of the SiteTree model but with pretty much all the cool functionality we've come to expect. However you really only should go down this road if you're absolutely shure that the normal way of doing things really won't work for you.

Avatar
tilluigi

17 August 2010 at 8:56pm Community Member, 9 Posts

thanks for clarifying! of course we could work our ways around this circumstance by fetching our page object manually and creating all the HTML within the controller - but this wouldn't have all the elegance we're used to from silverstripe...

but what you say sounds a little scary!! will we have to sell our smiles to go down that road?? ;-)
even then your solutions sounds very interesting, but will it mean mingling in the core of SS and loosing updateability?
or: under what circumstances would you recommend this solution?

we are working on a custom controller to feed an iPad App with custom HTML and we've done AJAXified websites where your solution would have been very handy, too!

Avatar
MarcusDalgren

17 August 2010 at 9:28pm Community Member, 288 Posts

No you won't have to touch the core at all so no worries there. What I've done is code my own nested controller which replaces ModelAsController. Since ModelAsController has such a low priority in the routing it's easy to replace. I then wrote my own version of a ContentController that uses any kind of dataobject as a dataRecord instead of just SiteTree objects.

The functionality mimics many other PHP frameworks in the sense that for example mysite.com/projects would map to ProjectController and it will look for a Project dataobject to use as a dataRecord unless you set it to something else. It also comes with a templating system based on SS normal templating system so you don't have to put your html in the controllers. It also hands over to ModelAsController if it doesn't find anything that matches so your regular site will keep on working just fine.

When to use it? I use it whenever I have to build something that really doesn't fit in the normal site with pages model. In one case a customer wanted a project management tool to use with their customers and that doesn't fit so then I use my own custom controllers for that. If you just want to show custom page layout when someone visits your site with an iphone or when they're using AJAX then you can easily do that with normal SilverStripe. You'd do something like this in your Page_Controller for example.

public function index() {
//However you check for Iphone visitors, $isIphone is just a dummy
if ($isIphone) {
return $this->renderWith(array("IphoneLayout", "IphoneMaster"));
}
else {
return $this->render();
}
}

Attached to this post are the two controllers I use for this. Keep in mind though that the BaseController uses an external library ([url=http://flourishlib.com/]Flourish[/url]) to figure out the singular versions of verbs so you either have to include Flourish in your project or go into BaseController and change how it figures out what controller to load. All that stuff is done in getNestedController().

You'll also need to put the URL rule below in your _config.php in order for the BaseController to take over.

Director::addRules(2, array(
'$URLSegment//$Action/$ID/$OtherID' => 'BaseController'
));

Go to Top