Mustache でこれを行う慣用的な方法は、データのハッシュを渡すのではなく、View (または ViewModel) を作成することです。
<?php
class Dropdown
{
public $name;
public $value;
private $options;
public function __construct($name, array $options, $value)
{
$this->name = $name;
$this->options = $options;
$this->value = $value;
}
public function options()
{
$value = $this->value;
return array_map(function($k, $v) use ($value) {
return array(
'value' => $k,
'display' => $v,
'selected' => ($value === $k),
)
}, array_keys($this->options), $this->options);
}
}
次に、これを部分的に組み合わせることができますdropdown
...
<select name="{{ name }}">
{{# options }}
<option value="{{ value }}"{{# selected }} selected{{/ selected }}>
{{ display }}
</option>
{{/ options }}
</select>
次のようにテンプレートで使用できます。
{{# state }}
<label for="{{ name }}">State</label>
{{> dropdown }}
{{/ state }}
{{# country }}
<label for="{{ name }}">Country</label>
{{> dropdown }}
{{/ country }}
そしてそれをレンダリングします:
<?php
$data = array(
'state' => new Dropdown('state', $someListOfStates, 'CA'),
'country' => new Dropdown('country', $someListOfCountries, 'USA'),
);
$template->render($data);
...しかし、あなたはそれよりもさらにうまくやることができます:)
これとともに:
<?php
class StateDropdown extends Dropdown
{
static $states = array(...);
public function __construct($value, $name = 'state')
{
parent::__construct($name, self::$states, $value);
}
}
この:
<?php
class CountryDropdown extends Dropdown
{
static $countries = array(...);
public function __construct($value, $name = 'country')
{
parent::__construct($name, self::$countries, $value);
}
}
そして、これらのうちの1つ:
<?php
class Address
{
public $street;
public $city;
public $state;
public $zip;
public $country;
public function __construct($street, $city, $state, $zip, $country, $name = 'address')
{
$this->street = $street;
$this->city = $city;
$this->state = new StateDropdown($state, sprintf('%s[state]', $name));
$this->zip = $zip;
$this->country = new CountryDropdown($country, sprintf('%s[country]', $name));
}
}
新しいaddress
パーシャルを投入します。
<label for="{{ name }}[street]">Street</label>
<input type="text" name="{{ name }}[street]" value="{{ street }}">
<label for="{{ name }}[city]">City</label>
<input type="text" name="{{ name }}[city]" value="{{ city }}">
{{# state }}
<label for="{{ name }}">State</label>
{{> dropdown }}
{{/ state }}
<label for="{{ name }}[zip]">Postal code</label>
<input type="text" name="{{ name }}[zip]" value="{{ zip }}">
{{# country }}
<label for="{{ name }}">Country</label>
{{> dropdown }}
{{/ country }}
メイン テンプレートを更新します。
<h2>Shipping Address</h2>
{{# shippingAddress }}
{{> address }}
{{/ shippingAddress }}
<h2>Billing Address</h2>
{{# billingAddress }}
{{> address }}
{{/ billingAddress }}
じゃ、行け!
<?php
$data = array(
'shippingAddress' => new Address($shipStreet, $shipCity, $shipState, $shipZip, $shipCountry, 'shipping'),
'billingAddress' => new Address($billStreet, $billCity, $billState, $billZip, $billCountry, 'billing'),
};
$template->render($data);
これで、モジュール化され、再利用可能で、簡単にテストでき、拡張可能なコードとパーシャルを使用できます。
作成したクラスは「Views」または「ViewModels」であることに注意してください。それらはドメイン モデル オブジェクトではありません...永続性や検証は気にしません。気にするのは、テンプレートの値を準備することだけです。モデルも使用している場合は、さらに簡単になります。なぜなら、Address クラスのようなものがアドレス モデルをラップし、必要な値をモデルから直接取得できるため、コンストラクターに一連のものを渡す必要がないからです。 .
口ひげの禅
このアプローチを論理的な結論に導くと、アプリ内のアクション/テンプレートのペアごとに 1 つのトップレベルの View または ViewModel クラスが作成されます。私たちの Address View ですが、各アクションのレンダリングを担当する 1 つのファーストクラスの View または ViewModel が必要です。
つまり、(MVC/MVVM の世界では) Controller アクションは、それに必要な「アクション」をすべて実行し、テンプレートへの入力を担当する View または ViewModel クラスを作成し、いくつかのドメイン Model オブジェクトを渡し、render を呼び出します。テンプレート。コントローラーはデータを準備しません。それはビューレイヤーの責任だからです。いくつかのモデル オブジェクトを渡すだけです。
これで、「レンダリング」のすべてのロジックが View レイヤーにきちんとカプセル化され、すべてのマークアップがテンプレート ファイルにきちんとカプセル化され、Model は醜い書式設定ビジネスから解放され、Controller は本来あるべきように素晴らしく軽量になります :)