Cannot access Eloquent attributes on Twig

一曲冷凌霜 提交于 2019-12-04 12:07:34

I had the same issue and stumbled upon this question. After solving it myself, I thought I'd try to help you out.

Try doing a Eager Load on the model:

Field::with('type')->get()

This should allow you to do the following with no other issues.

{{ f.type }}

See more info here: http://laravel.com/docs/4.2/eloquent#eager-loading

If you don't want to do an eager load, you can override the magic __isset method in your Field class to return true for the type relationship property:

public function __isset($name)
{
    if (in_array($name, [
        'type'
    ])) {
        return true;
    } else {
        return parent::__isset($name);
    }
}

Explanation

The problem is in the interaction between how Eloquent implements the "dynamic property" for a relation (in your case, f.type), and the rules that Twig uses for accessing "attributes" of a variable.

From the Twig documentation:

For convenience's sake foo.bar does the following things on the PHP layer:

  • check if foo is an array and bar a valid element;
  • if not, and if foo is an object, check that bar is a valid property;
  • if not, and if foo is an object, check that bar is a valid method (even if bar is the constructor - use __construct() instead);
  • if not, and if foo is an object, check that getBar is a valid method;
  • if not, and if foo is an object, check that isBar is a valid method;
  • if not, return a null value.

foo['bar'] on the other hand only works with PHP arrays:

  • check if foo is an array and bar a valid element;
  • if not, return a null value.

The key here is the part where it says "check that bar is a valid property". This means that on the level of PHP, Twig is calling isset on $f->type. Eloquent implements the magic __isset method in Model, so you might think that this wouldn't be a problem.

However, take a look at how it actually implements __isset for model relations:

/**
 * Determine if an attribute exists on the model.
 *
 * @param  string  $key
 * @return bool
 */
public function __isset($key)
{
    return (isset($this->attributes[$key]) || isset($this->relations[$key])) ||
            ($this->hasGetMutator($key) && ! is_null($this->getAttributeValue($key)));
}

It determines whether a relation is "set" by looking in its array of loaded relationships (isset($this->relations[$key])). The problem is that if type hasn't been loaded yet, Eloquent will say that it is not "set".

Thus when Twig looks at $f->type, it will think that type is not a valid property, and move on to the next rule:

...if foo is an object, check that bar is a valid method

So now it will look for the method type(), which it finds. The only problem? type() (the method) returns a Relation (specifically, a BelongsTo), rather than a Type object. And BelongsTo objects aren't models.

If you want Twig to know that the property $f->type does indeed exist, you have two choices:

  • You can eager-load the related Type object along with your Field, as suggested by @roger-collins, or;
  • You can overload the __isset magic method in Field:

public function __isset($name) { if (in_array($name, [ 'type' ])) { return true; } else { return parent::__isset($name); } }

This will force Twig to recognize that type is a valid property for Field, even before the related model has actually been loaded.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!