Template variable scope

Silverstripe Version: 4.1

I get different results for a function in an Included file. Just wondering why that is.

I have a function in PageController I expect an output from. I call this function from an included header file.

I have two controllers, both extend PageController.

HomePageController I render like:

return $this->customise(['Categories' => $categories])->renderWith('Layout/HomePage'); 

AccountController I render like:

return $data->renderWith('Layout/Account');

Where $data is an ArrayData().

HomePageController works, AccountController doesn’t. Everything else works fine with AccountController page.

Possible pertinent is HomePage has content in the CMS, AccountController doesn’t.

I thought it may have been scope like the docs say so I tried adding Top=$Top in the Layout/Account template when calling the Header.ss file but that didn’t work after a ?flush either.

If I change AccountController to render like the HomePageController it all works sweet, just wondering what I’m missing between the two methods.

I’m getting a bit confused, maybe because the templates you’re using don’t seem to line up with the purpose/convention. Normally you wouldn’t need to manually render any templates in the Layout folder, those are automatically picked based on the page type for the current request and render to the $Layout variable in your Page.ss or HomePage.ss template etc.

If you’re rendering a header it might be better (or just more conventional) to put those templates in the Includes folder.

Putting that aside though, you should be able to render templates regardless of where they’re stored so it seems like the $data variable just isn’t structured appropriately for the template you’re trying to render. I assume you’re just getting a blank area instead of your header, or are you getting some kind of error?

I’ll try to explain this better:

A function in PageController is not always called. The function, potato() just outputs a word to the screen. With one page I see it, with another I don’t.

Both pages have controllers that extend PageController, HomePageController and AccountController. HomePage works, Account doesn’t.

HomePage is in the CMS as it as editable content so it has a model/type: HomePage. Account doesn’t.

For no real reason I was rendering them differently in the controller.

HomePage that works:

return $this->customise(['Categories' => $categories, 'Urls' => $urls])->renderWith('Layout/HomePage');

Account that doesn’t work I went in reverse and rendered off the ArrayData like:

return $data->renderWith('Layout/Account');

Everything else about the render is fine. It just doesn’t seem to call PageController::potato() like I’d expect.

If I change the render in AccountController to:

return $this->customise($data)->renderWith('Layout/Account');

It now does call PageController::potato() like I’d expect.

There’s no drama rendering it the second way I was just wondering why the difference for future me.

Both templates live in /templates/Layout and include a Header file that attempts to call the PageController::potato() with $potato()

I don’t get any errors.

Oh okay, I think the reason that $data->renderWith is not calling your potato method is that $data is not an instance of a controller, it’s just an ArrayData object, which doesn’t know about or have access to your potato method.

$this->customise() returns a customised version of $this (more or less), so when you call renderWith in that example you are calling it on the Controller, so you can access potato.

Cheers @JonoM makes sense.

TBH I was kinda surprised rendering off ArrayData got as far as it did - I expected it to render that data into a template/partial - like a decorator, which it did of course.