🎻
[Symfony] Twigのattribute関数で孫以下のプロパティにもアクセスしたい
Twigのattribute関数
Twigでオブジェクトのプロパティにアクセスするには
{{ person.name }}
みたいにすればいいですよね。
では、プロパティ名が変数に格納されている場合はどうすればいいかと言うと、
{# propName == 'name' #}
{{ attribute(person, propName) }}
こんなふうに attribute 関数を使えばOKです。
孫プロパティにもアクセスしたい
ではここで、以下のようなオブジェクトを考えてみましょう。
class Person
{
public $name;
/** @var Address */
public $address;
}
class Address
{
public $prefecture;
public $city;
}
このとき、Twigで都道府県名を出力したければ、当然
{{ person.address.prefecture }}
とすればいいのですが、例えばちょっと変化球で、以下のように person
以下のプロパティパスを渡せば共通処理で値を出力してくれるような実装をしたいとしましょう。
名前: {% include 'widget.html.twig' with { propertyPath: 'name' } %}
都道府県: {% include 'widget.html.twig' with { propertyPath: 'address.prefecture' } %}
市区郡: {% include 'widget.html.twig' with { propertyPath: 'address.city' } %}
{# widget.html.twig #}
{{ attribute(person, propertyPath) }}
このとき、 name
については
{{ attribute(person, 'name') }}
が実行されて正常に出力されますが、 address.prefecture
address.city
については
{{ attribute(person, 'address.prefecture') }}
{{ attribute(person, 'address.city') }}
を実行することになり、これは attribute()
関数の正しい使い方ではないのでエラーになってしまいます。
こんなふうに、 attribute
関数で孫以下のプロパティにもアクセスしたいというケースがたまにあります。
自分でTwig関数を作ってしまえば解決
こういう場合は、自分でTwig関数を実装してしまえば解決できます。
Symfonyの PeropertyAccess コンポーネントを使えば、今回の例のようなドット区切りのプロパティパスで孫以下のプロパティにも簡単にアクセスできます。
例えば以下のような実装でよいでしょう。
<?php
// src/Twig/AppExtension.php
namespace App\Twig;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class AppExtension extends AbstractExtension
{
/**
* @var PropertyAccessorInterface
*/
private $accessor;
public function __construct(PropertyAccessorInterface $accessor)
{
$this->accessor = $accessor;
}
public function getFunctions()
{
return [
new TwigFunction('getAttribute', [$this, 'getAttribute']),
];
}
public function getAttribute($objectOrArray, $propertyPath)
{
return $this->accessor->getValue($objectOrArray, $propertyPath);
}
}
# config/services.yaml
services:
App\Twig\AppExtension:
tags: ['twig.extension']
これで、Twig内で getAttribute()
という関数が使えるようになるので、以下のようなコードが書けます👍
名前: {% include 'widget.html.twig' with { propertyPath: 'name' } %}
都道府県: {% include 'widget.html.twig' with { propertyPath: 'address.prefecture' } %}
市区郡: {% include 'widget.html.twig' with { propertyPath: 'address.city' } %}
{# widget.html.twig #}
{{ getAttribute(person, propertyPath) }}
参考: https://stackoverflow.com/questions/23364538/access-child-entity-property-from-string-twig-symfony
まとめ
- Twigの
attribute()
関数で孫以下のプロパティにもアクセスしたい場合、attribute()
関数自体にそんな機能はないので、自分でTwig関数を実装してしまえばOK
Discussion