rcrowe/TwigBridge

Twig calls Model's relation method when it wants its property

rudiedirkx opened this issue · 4 comments

TwigBridge 0.11.2 with Twig 2.12.2.

Twig has changed its twig_get_attribute(). (I came from Twig 1.42.2) If a relationship returns NULL, Twig tries to call its method and gets the Relationship object.

In twig:

{% if g_organization.is(g_user.organization) %}

The problem is the g_user.organization accessor (by twig_get_attribute()). It should pass this check:

// object property
if (isset($object->$item) || \array_key_exists((string) $item, (array) $object)) {

because it's a magic property, but it doesn't, because the result is null, so not isset(). A few lines later, there's a

// object method

which it passes, because there's a method 'organization()', but it returns a BelongsTo relationship, not an Organization model.

Twig tries the property, (Laravel resolves the relationship: null), Twig tries the method, Laravel returns the BelongsTo object.

In my case that crashes in Model::is(), because that expects a Model argument, not a relationship, but the real problem happens before, with Twig accessing Laravel properties. Laravel is too much magic for Twig =(

I wouldn't know how to fix decently. The property exists, but is not isset(). Is the miscommunication Laravel's fault or Twig's? I fixed it for now by overriding __isset() in my app's custom Model:

public function __isset($key) {
	$this->getAttribute($key);
	return array_key_exists($key, $this->toArray());
}

If the attribute exists, even if it's NULL, isset() is now true. Not great...

Can you try with this extension enabled? fd856b3

Ehm. Yea. That works. Everyone knew this issue? So my whole essay was unnecessary? =(

But upgrading rcrowe/twigbridge doesn't upgrade the config file, so how does anyone find out? Should I compare Laravel config files after a package upgrade?

Yeah I don't know what exactly changed, but it was submitted as a fix so I'm not 100% about any side-effects. But it does seems to be necessary for new Laravel version, so I think we might need to just always add it in the core.

I agree. Twig counts on Symfony style entities and accessors. For Laravel, this custom getter is the way to go. No need to 'break' my Model::__isset(). Very cool. I didn't even know this was a Twig thing.

I'm not sure about efficiency though. Compiled twig templates call lots of GetAttrNode::attribute() now, which loads class GetAttrExpression and more of the compiler. Runtime doesn't need all that compiler code. Maybe the attribute override function should be a separate function, not a static method in the compiler. Might be 1% more efficient.