Jump to content

$this->var, $this->view->var or $view->var in controllers and components


Ivan Gretsky
 Share

Recommended Posts

Good day, @teppo!

I am trying to understand how to pass variables to views in different cases. I am a little confused, as I see different examples in different places. And more top that, some things seem to work depending on where they are used.

Controllers

In the docs you give example of passing vars to views in render() method like this:

public function render() {
    $view->some_var = "some value";
}

In the boilerplate profile and in the documentation site source you pass them like below in init():

public function init() {
    $this->view->hide_aside = true;
	...
}

Can you please tell if there is a difference? And which and where should we use better?

Components

There is no example of components usage in the boilerplate profile. But there are examples both in the docs and docs site source code. They both say we should pass vars to views in __construct() method like this:

public function __construct(\ProcessWire\RepeaterPage $item) {
    $this->title = $item->title;
}

But I did confuse it with controllers and did it like this (with $this->view instead of just $this) :

public function __construct($page) {
	$this->view->headline = $page->headline;
}

And it still worked if components were included in templates served through wireframe.php. Bit didn't in case I used components in non-wireframe templates.

Could you please give me a hint, why is that and where and how to pass vars to views for controllers. Why is that inconsistent with controllers and why in different methods?

Link to comment
Share on other sites

Hey @Ivan Gretsky

Good question, and sorry for taking a while to respond πŸ™‚

On 3/26/2022 at 2:34 PM, Ivan Gretsky said:

Controllers

In the docs you give example of passing vars to views in render() method like this:

public function render() {
    $view->some_var = "some value";
}

In the boilerplate profile and in the documentation site source you pass them like below in init():

public function init() {
    $this->view->hide_aside = true;
	...
}

Can you please tell if there is a difference? And which and where should we use better?

In this case the first example was, simply put, a mistake β€” it didn't work at all. Thanks for spotting that, it's fixed now in the docs πŸ‘πŸ™‚

As for a best practice of sorts, in my opinion in Controller context one should preferably refer to view as $this->view. Referring to it as $this->wire('view') or $this->wire()->view etc. will work as well, but there's a slight chance that it's a different view.

To be honest the "different view" situation is a bit of a border case, and something you're unlikely to run into, so usually this won't really matter. It could, though, happen if a) someone has intentionally provided a new View object to a Controller or overridden the $view API variable (in site/template level code or a module), or b) while using ProcessWire's multi-instance support and rendering content from two instances simultaneously.

Personally I also prefer $this->view because it makes sense to me and is (IMHO) syntax wise cleaner than the alternatives.

On 3/26/2022 at 2:34 PM, Ivan Gretsky said:

Components

There is no example of components usage in the boilerplate profile. But there are examples both in the docs and docs site source code. They both say we should pass vars to views in __construct() method like this:

public function __construct(\ProcessWire\RepeaterPage $item) {
    $this->title = $item->title;
}

But I did confuse it with controllers and did it like this (with $this->view instead of just $this) :

public function __construct($page) {
	$this->view->headline = $page->headline;
}

And it still worked if components were included in templates served through wireframe.php. Bit didn't in case I used components in non-wireframe templates.

Components are indeed a bit different. First a little dive into the technical background:

The Component base class extends ProcessWire's WireData class, and thus when we set $this->title in the example above, what actually happens is that the value gets stored in a local data array, so that $this->data['title'] now contains the value we set for title. When the component is rendered (Component::render() is called), everything stored in that local data array is passed to an instance of the ComponentView class, which makes it available in the view file.

In other words the View object doesn't come into play at all. ComponentView has certain similar qualities to View, but it's more limited (in both features and scope). And when I say scope, what I mostly mean is that components are intended to be their own "sandbox", so that what happens in them doesn't directly affect the "outside context", i.e. Page specific view(s), or the layout.

Quote

Bit didn't in case I used components in non-wireframe templates.

What happens in your last example is that you are storing a value in the "global view" instead of the local context of the component in question. This does make it available in the component, since ComponentView extends ProcessWire's TemplateFile which in turn makes it work, butΒ I would say that this working (in some situations) is more a side effect than an intended result πŸ˜…

The reason it won't work when rendering components outside of templates fully handled by Wireframe is, as far as I can tell, that in this situation only the necessary parts of Wireframe are initialized. Global View API variable is not one of these. (Honestly this is somewhat fuzzy area for me, and I would've expected $this->view->headline to throw some sort of notice/error.)

Reading your post does make me wonder if components should actually provide a "fake view" to make passing variables to their own view layer more in line with controllers, but at this time I'm unsure if this is a good idea. Technically speaking it would be a backwards compatibility break, since someone might currently rely on having access to the "real view". Anyway, this is probably a bit off-topic; the long story short is that "yes, data is passed from components to their views in a slightly different way" πŸ™‚

  • Like 2
Link to comment
Share on other sites

  • 4 months later...
On 4/2/2022 at 8:19 PM, teppo said:

Reading your post does make me wonder if components should actually provide a "fake view" to make passing variables to their own view layer more in line with controllers, but at this time I'm unsure if this is a good idea.

It seems like a good idea from the API consistency point of view)

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
Γ—
Γ—
  • Create New...