Twigテンプレートで数値を美しく!number_formatの基本と応用例
基本的な使い方は以下の通りです。
{{ 変数 | number_format }}
これだけだと、デフォルトの設定で数値がフォーマットされます。より細かくフォーマットを指定したい場合は、引数を渡します。
引数
number_format
フィルターは、以下の引数を順番に取ることができます。
- decimals (オプション)
小数点以下の桁数を指定します。デフォルトは0
です。 - decimal_point (オプション)
小数点として使用する記号を指定します。デフォルトは現在のロケール設定に基づきますが、通常は.
(ピリオド) です。 - thousands_sep (オプション)
千の位の区切り文字として使用する記号を指定します。デフォルトは現在のロケール設定に基づきますが、通常は,
(カンマ) です。
-
小数点以下の桁数を指定せず、桁区切りにスペースを使用
{{ quantity | number_format(0, '.', ' ') }}
例えば、
quantity
が12345
の場合、12 345
と表示されます。 -
小数点以下2桁、小数点にカンマ、桁区切りにピリオドを使用
{{ amount | number_format(2, ',', '.') }}
例えば、
amount
が1234567.89
の場合、1.234.567,89
と表示されます。 -
小数点以下2桁で表示
{{ price | number_format(2) }}
例えば、
price
が1234.567
の場合、1234.57
と表示されます。
引数の数が間違っている
number_format
フィルターは、0個から3個までの引数を取ることができます。引数の数が多すぎたり、少なすぎたりすると、期待通りのフォーマットにならなかったり、Twigのパースエラーが発生したりすることがあります。
-
対処法
必要な引数の数(小数点以下の桁数、小数点記号、桁区切り文字)を正しく指定してください。不要な引数は削除しましょう。 -
{{ price | number_format(2, '.', ',', 'extra') }} {# 引数が多すぎる #}
引数の型が間違っている
number_format
フィルターに渡す引数は、特定の型である必要があります。
- thousands_sep
文字列である必要があります。通常は1文字の記号です。 - decimal_point
文字列である必要があります。通常は1文字の記号です。 - decimals
整数である必要があります。
誤った型の引数を渡すと、期待通りのフォーマットにならないことがあります。Twigのバージョンによってはエラーが発生する場合もあります。
-
対処法
引数の型を正しく指定してください。小数点以下の桁数は整数、小数点記号と桁区切り文字は文字列で渡します。 -
エラー例
{{ quantity | number_format('2', '.', ',') }} {# '2' は文字列ですが、整数が期待されます #} {{ amount | number_format(2, 123, ',') }} {# 小数点記号に数値が渡されています #}
適用する変数が数値型ではない
number_format
フィルターは数値型の変数に適用することを前提としています。文字列や配列などの数値型でない変数に適用すると、期待通りの結果が得られず、場合によってはエラーが発生します。
-
対処法
number_format
フィルターを適用する変数が数値型であることを確認してください。もし文字列型の数値をフォーマットしたい場合は、事前にfloat
やint
フィルターなどで型変換を試みる必要があるかもしれません。ただし、変換できない文字列の場合はエラーになる可能性があります。 -
エラー例
{{ name | number_format(2) }} {# name は文字列型の変数だと仮定 #}
ロケール設定による影響
Twigは国際化(i18n)のためにロケール設定を使用することがあります。デフォルトの小数点記号や桁区切り文字は、設定されたロケールによって異なる場合があります。意図した記号と異なる場合は、明示的に引数で指定する必要があります。
-
対処法
期待する小数点記号と桁区切り文字をnumber_format
フィルターの引数で明示的に指定することで、ロケール設定の影響を回避できます。 -
例
あるロケールでは小数点にカンマ (,
)、桁区切りにピリオド (.
) が使われる場合があります。
テンプレートキャッシュ
Twigはパフォーマンス向上のためにテンプレートをキャッシュします。もしテンプレートの修正後に期待通りの結果が得られない場合は、キャッシュをクリアしてみることをお勧めします。
- 対処法
Symfonyなどのフレームワークを使用している場合は、フレームワークのキャッシュクリアコマンドを実行してください。手動でキャッシュディレクトリを削除する必要がある場合もあります。
トラブルシューティングのヒント
- PHPのエラーログ
Twigの処理は最終的にPHPで行われるため、PHPのエラーログも確認してみると、より根本的な原因がわかることがあります。 - Twigのデバッグモード
Twigのデバッグモードを有効にすると、エラーメッセージがより詳細に表示されることがあります。 - dump() フィルターの活用
問題が発生している変数の値や型を確認するために、dump()
フィルターを使用してみましょう。{{ price | dump }} {{ price | number_format(2) | dump }}
基本的な使い方
{# 数値をデフォルトの形式で表示 #}
{{ price }} {# 例: 12345.6789 #}
{{ price | number_format }} {# 結果: 12346 (小数点以下は四捨五入されて整数で表示) #}
小数点以下の桁数を指定する
{# 小数点以下2桁で表示 #}
{{ price | number_format(2) }} {# 結果: 12345.68 #}
{# 小数点以下0桁で表示 (整数) #}
{{ quantity | number_format(0) }} {# 例: quantity が 10.5 なら結果: 11 #}
小数点記号と桁区切り文字を指定する
{# 小数点にカンマ、桁区切りにピリオドを使用 #}
{{ amount | number_format(2, ',', '.') }} {# 例: amount が 1234567.89 なら結果: 1.234.567,89 #}
{# 小数点にドット、桁区切りにスペースを使用 #}
{{ value | number_format(3, '.', ' ') }} {# 例: value が 9876.54321 なら結果: 9 876.543 #}
ループ内で異なる数値をフォーマットする例
<table>
<thead>
<tr>
<th>商品名</th>
<th>価格</th>
<th>数量</th>
<th>合計金額</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
<td>{{ item.name }}</td>
<td style="text-align: right;">{{ item.price | number_format(2) }} 円</td>
<td style="text-align: right;">{{ item.quantity | number_format(0) }}</td>
<td style="text-align: right;">{{ (item.price * item.quantity) | number_format(2) }} 円</td>
</tr>
{% endfor %}
</tbody>
</table>
この例では、商品リストの価格と合計金額を小数点以下2桁で、数量を整数で表示しています。
条件分岐と組み合わせてフォーマットを変える例
{% set discount_rate = 0.15 %}
{% set original_price = 1000 %}
{% set discounted_price = original_price * (1 - discount_rate) %}
<p>元の価格: {{ original_price | number_format(0) }} 円</p>
{% if discount_rate > 0 %}
<p>割引率: {{ discount_rate * 100 | number_format(0) }}%</p>
<p>割引後の価格: {{ discounted_price | number_format(2) }} 円</p>
{% else %}
<p>割引はありません。</p>
{% endif %}
ここでは、割引率に応じて割引後の価格を小数点以下2桁で表示しています。
変数を使ってフォーマットの引数を指定する例
{% set decimals = 3 %}
{% set decimal_point = ',' %}
{% set thousands_separator = '.' %}
{% set pi = 3.1415926535 %}
<p>円周率 ({{ decimals }}桁表示): {{ pi | number_format(decimals, decimal_point, thousands_separator) }}</p>
{# 結果: 3,142 #}
このように、変数を number_format
の引数に渡すことで、動的にフォーマットを制御できます。
- ロケール設定によっては、デフォルトの小数点記号や桁区切り文字が異なる場合があります。意図した表示にならない場合は、明示的に引数を指定することを推奨します。
number_format
は数値を文字列に変換します。そのため、フォーマット後の値を数値として計算に利用する場合は、再度数値型に変換する必要があるかもしれません。
PHPの number_format() 関数を直接利用する
Twigテンプレート内でPHPの関数を直接呼び出す設定がされている場合(セキュリティ上の理由から推奨されないことが多いですが)、PHPの number_format()
関数を直接使用できます。
{# TwigでPHP関数を直接呼び出す設定がされている場合 #}
{{ php_function('number_format', price, 2, '.', ',') }}
- 欠点
- セキュリティリスクが高まる可能性があります(テンプレート内で任意のPHPコードを実行できるようになるため)。
- Twigのテンプレートエンジンとしての役割から逸脱する可能性があります。
- Twigの可読性が低下する可能性があります。
- 利点
PHPの持つnumber_format()
の全ての機能を利用できます。
カスタム Twig フィルターを作成する
より安全でTwigの流儀に沿った方法として、独自のTwigフィルターを作成する方法があります。これにより、アプリケーションのニーズに合わせた数値フォーマット処理をカプセル化できます。
-
- ロジックをPHP側に分離できるため、テンプレートが綺麗になります。
- 複数の場所で同じフォーマット処理を再利用できます。
- テストが容易になります。
- Twigの拡張機能として管理できるため、セキュリティ上の懸念が低くなります。
-
例 (Symfonyの場合)
// src/Twig/AppExtension.php namespace App\Twig; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; class AppExtension extends AbstractExtension { public function getFilters(): array { return [ new TwigFilter('custom_number_format', [$this, 'formatNumber']), ]; } public function formatNumber($number, int $decimals = 0, string $decPoint = '.', string $thousandsSep = ','): string { return number_format($number, $decimals, $decPoint, $thousandsSep); } }
そして、Twigテンプレート内でこのカスタムフィルターを使用します。
{{ price | custom_number_format(2, ',', '.') }}
toLocaleString() (JavaScript)
もし数値のフォーマットをクライアントサイドで行うことが許容される、またはその方が都合が良い場合は、JavaScriptの toLocaleString()
メソッドを利用することも考えられます。例えば、APIから取得した数値をJavaScriptでフォーマットして表示する場合などです。
const price = 12345.6789;
const formattedPrice = price.toLocaleString('ja-JP', {
style: 'currency',
currency: 'JPY',
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
console.log(formattedPrice); // "¥12,345.68" (ロケールやオプションによって結果は異なります)
そして、Twigテンプレートでは単にその数値をJavaScriptに渡すだけです。
<span id="price">{{ price }}</span>
<script>
const priceElement = document.getElementById('price');
const priceValue = parseFloat(priceElement.textContent);
const formattedPrice = priceValue.toLocaleString('ja-JP', {
style: 'currency',
currency: 'JPY',
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
priceElement.textContent = formattedPrice;
</script>
- 欠点
- JavaScriptが有効でない環境では動作しません。
- サーバーサイドとクライアントサイドで同じフォーマットロジックを実装する必要がある場合があります。
- 利点
- クライアントサイドで処理するため、サーバー側の負荷を軽減できる場合があります。
- ユーザーのブラウザのロケール設定に基づいてフォーマットできます。
- 通貨記号など、より高度なローカライズされたフォーマットが可能です。
Intl 拡張機能 (PHP)
PHPの Intl 拡張機能を利用することで、より高度な国際化とローカライズされた数値フォーマットが可能になります。これを利用したカスタム Twig フィルターを作成することもできます。
-
欠点
- Intl 拡張機能がPHPにインストールされている必要があります。
- 設定や使い方が
number_format
より複雑になる場合があります。
-
利点
- より高度なローカライズされた数値フォーマットが可能です(通貨、パーセントなど)。
- ロケールに基づいた適切な記号や形式で表示できます。
-
例 (カスタムフィルター内での IntlNumberFormatter の使用)
use NumberFormatter; public function formatLocalizedNumber($number, string $locale = 'ja_JP', int $style = NumberFormatter::DECIMAL, int $decimals = 2): string { $formatter = new NumberFormatter($locale, $style); $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, $decimals); return $formatter->format($number); }
Twigテンプレートでの使用例:
{{ price | format_localized_number('ja_JP', constant('NumberFormatter::CURRENCY'), 2) }}
どの方法を選ぶべきか
- PHP関数を直接呼び出す
基本的には避けるべきですが、特別な理由がある場合に検討されることがあります。 - 高度な国際化対応が必要な場合
PHPの Intl 拡張機能とカスタム Twig フィルターの組み合わせが強力です。 - クライアントサイドでのフォーマットが適切な場合
JavaScriptのtoLocaleString()
を検討してください。 - より複雑なロジックや再利用性を求める場合
カスタム Twig フィルターの作成が推奨されます。 - 単純なフォーマットで十分な場合
number_format
フィルターが最も手軽で便利です。