9

そのため、Mustache.php を使用していくつかの複雑な html ケースを柔軟に処理するための最良の慣用的な方法に頭を悩ませています。

1つ目は、事前に選択された選択ドロップダウンです。

<select>
  <option value=''></option>
  <option value='bob'>Bob Williams</option>
  <option value='james' selected>James Smith</option>
</select>

私はこれに対処する方法を持っていますが、私の方法は本当に柔軟性がないようです:

  • PHPで配列を取得し、
  • 3 つの要素を持つ多次元配列に再フォーマットします。値、表示、選択済み (ブール値)
  • オプション、値、および選択されたものがループで出力されるテンプレートに渡します

パーシャルまたは匿名の関数またはメソッド、または私が見逃している mustache.php の他の機能を使用して、事前に選択された選択ドロップダウンを作成するための素晴らしいアプローチはありますか?

編集:明確にするために、この質問を別々の部分に切り詰めました。

4

2 に答える 2

20

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 は本来あるべきように素晴らしく軽量になります :)

于 2012-10-05T19:47:26.400 に答える