これは@kaiserの答えの代替アプローチであり、私はかなり良いと感じました(私から+1)が、コアWP関数で使用するには追加の作業が必要であり、それ自体はテンプレート階層と統合されています。
私が共有したいアプローチは、テンプレートのレンダリングデータを処理する単一のクラス(私が取り組んでいるものから削除されたバージョンです)に基づいています。
いくつかの(IMO)興味深い機能があります。
- テンプレートは標準のWordPressテンプレートファイル(single.php、page.php)であり、もう少し力があります
- 既存のテンプレートが機能するため、既存のテーマのテンプレートを簡単に統合できます。
- @kaiserアプローチとは異なり、テンプレートでは
$this
キーワードを使用して変数にアクセスします。これにより、未定義の変数の場合に本番環境で通知を回避できる可能性があります。
Engine
クラス
namespace GM\Template;
class Engine
{
private $data;
private $template;
private $debug = false;
/**
* Bootstrap rendering process. Should be called on 'template_redirect'.
*/
public static function init()
{
add_filter('template_include', new static(), 99, 1);
}
/**
* Constructor. Sets debug properties.
*/
public function __construct()
{
$this->debug =
(! defined('WP_DEBUG') || WP_DEBUG)
&& (! defined('WP_DEBUG_DISPLAY') || WP_DEBUG_DISPLAY);
}
/**
* Render a template.
* Data is set via filters (for main template) or passed to method for partials.
* @param string $template template file path
* @param array $data template data
* @param bool $partial is the template a partial?
* @return mixed|void
*/
public function __invoke($template, array $data = array(), $partial = false)
{
if ($partial || $template) {
$this->data = $partial
? $data
: $this->provide(substr(basename($template), 0, -4));
require $template;
$partial or exit;
}
return $template;
}
/**
* Render a partial.
* Partial-specific data can be passed to method.
* @param string $template template file path
* @param array $data template data
* @param bool $isolated when true partial has no access on parent template context
*/
public function partial($partial, array $data = array(), $isolated = false)
{
do_action("get_template_part_{$partial}", $partial, null);
$file = locate_template("{$partial}.php");
if ($file) {
$class = __CLASS__;
$template = new $class();
$template_data = $isolated ? $data : array_merge($this->data, $data);
$template($file, $template_data, true);
} elseif ($this->debug) {
throw new \RuntimeException("{$partial} is not a valid partial.");
}
}
/**
* Used in templates to access data.
* @param string $name
* @return string
*/
public function __get($name)
{
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
if ($this->debug) {
throw new \RuntimeException("{$name} is undefined.");
}
return '';
}
/**
* Provide data to templates using two filters hooks:
* one generic and another query type specific.
* @param string $type Template file name (without extension, e.g. "single")
* @return array
*/
private function provide($type)
{
$generic = apply_filters('gm_template_data', array(), $type);
$specific = apply_filters("gm_template_data_{$type}", array());
return array_merge(
is_array($generic) ? $generic : array(),
is_array($specific) ? $specific : array()
);
}
}
(ここで要点として利用できます。)
使い方
必要なのはEngine::init()
、おそらく'template_redirect'
オンフックでメソッドを呼び出すことだけです。これは、テーマ内functions.php
またはプラグインから実行できます。
require_once '/path/to/the/file/Engine.php';
add_action('template_redirect', array('GM\Template\Engine', 'init'), 99);
それで全部です。
既存のテンプレートは期待どおりに機能します。ただし、カスタムテンプレートデータにアクセスできるようになりました。
カスタムテンプレートデータ
カスタムデータをテンプレートに渡すには、2つのフィルターがあります。
'gm_template_data'
'gm_template_data_{$type}'
1つ目はすべてのテンプレートに対して起動され、2つ目はテンプレート固有です。実際、動的な部分{$type}
はファイル拡張子のないテンプレートファイルのベース名です。
例えば、フィルター'gm_template_data_single'
を使用してデータをsingle.php
テンプレートに渡すことができます。
これらのフックに接続されたコールバックは、キーが変数名である配列を返す必要があります。
たとえば、次のようなテンプレートデータとしてメタデータを渡すことができます。
add_filter('gm_template_data', function($data) {
if (is_singular()) {
$id = get_queried_object_id();
$data['extra_title'] = get_post_meta($id, "_theme_extra_title", true);
}
return $data;
};
次に、テンプレート内で次のように使用できます。
<?= $this->extra_title ?>
デバッグモード
定数WP_DEBUG
との両方WP_DEBUG_DISPLAY
がtrueの場合、クラスはデバッグモードで動作します。変数が定義されていない場合、例外がスローされることを意味します。
クラスがデバッグモードでない場合(おそらく本番環境)、未定義の変数にアクセスすると、空の文字列が出力されます。
データモデル
データを整理するための優れた保守可能な方法は、モデルクラスを使用することです。
これらは、上記と同じフィルターを使用してデータを返す非常に単純なクラスにすることができます。従う特定のインターフェイスはありません。好みに応じて整理できます。
以下に、ほんの一例がありますが、独自の方法で自由に行うことができます。
class SeoModel
{
public function __invoke(array $data, $type = '')
{
switch ($type) {
case 'front-page':
case 'home':
$data['seo_title'] = 'Welcome to my site';
break;
default:
$data['seo_title'] = wp_title(' - ', false, 'right');
break;
}
return $data;
}
}
add_filter('gm_template_data', new SeoModel(), 10, 2);
__invoke()
この方法は、(クラスがコールバックのように使用されたときに実行される)のために使用する文字列を返し<title>
テンプレートのタグ。
渡された2番目の引数が'gm_template_data'
テンプレート名であるという事実のおかげで、このメソッドはホームページのカスタムタイトルを返します。
上記のコードを持つと、次のようなものを使用することが可能になります
<title><?= $this->seo_title ?></title>
<head>
ページのセクション。
パーシャル
WordPressには、パーシャルをメインテンプレートに読み込むために使用できる、get_header()
またはget_template_part()
使用できる機能があります。
これらの関数は、他のすべてのWordPress関数と同様に、テンプレートを使用するときに使用できます。 Engine
クラス。
唯一の問題は、WordPressのコア関数を使用して読み込まれたパーシャル内では、を使用してカスタムテンプレートデータを取得する高度な機能を使用できないこと$this
です。
このため、Engine
クラスにはpartial()
パーシャルを(完全に子テーマと互換性のある方法で)ロードし、パーシャルでカスタムテンプレートデータを使用できるメソッドがあります。
使い方はとても簡単です。
partials/content.php
テーマ(または子テーマ)フォルダー内に名前の付いたファイルがあると仮定すると、以下を使用して含めることができます。
<?php $this->partial('partials/content') ?>
そのパーシャル内では、すべての親テーマデータにアクセスできますが、同じ方法です。
WordPressの関数とは異なり、このEngine::partial()
メソッドは特定のデータをパーシャルに渡すことができ、単純にデータの配列を2番目の引数として渡します。
<?php $this->partial('partials/content', array('greeting' => 'Welcome!')) ?>
デフォルトでは、パーシャルは親テーマで利用可能なデータおよび渡されたデータの明示性にアクセスできます。
partialに明示的に渡された変数が親テーマ変数と同じ名前を持つ場合、明示的に渡された変数が優先されます。
ただし、分離モードにパーシャルを含めることもできます。つまり、パーシャルは親テーマデータにアクセスできません。これを行うには、true
3番目の引数としてを渡すだけですpartial()
:
<?php $this->partial('partials/content', array('greeting' => 'Welcome!'), true) ?>
結論
たとえ非常に単純であっても、Engine
クラスはかなり完成していますが、確実にさらに改善することができます。たとえば、変数が定義されているかどうかを確認する方法はありません。
WordPressの機能とテンプレート階層との100%の互換性により、既存のコードやサードパーティのコードと問題なく統合できます。
ただし、これは部分的にしかテストされていないため、まだ発見していない問題がある可能性があります。
「何が得られたのか」の5つのポイント 中@kaiserの 答え:
- データ構造を変更せずにテンプレートを簡単に交換
- 読みやすいテンプレート
- グローバルスコープを避ける
- 単体テスト可能
- 他のコンポーネントを損なうことなくモデル/データを交換できます
私のクラスでもすべて有効です。