現在私はWordpressの "Admin Columns Pro"プラグインを使ってバックエンドのいくつかの列を修正しています。このプラグインには、投稿(または私の場合は商品)の作成者を列として表示する機能が含まれています。
しかし、私は製品の最新の編集者が誰であるかを知る必要がある立場にいます。基本的に私たちが今持っているのは1人の個人が製品ドラフトを作成し、それから誰かがそれを完成させることです、そして私はそれらの名前の両方を見る必要があります。
プラグインには、カスタムカラムを作成するための ドキュメントが含まれています 、そしてそれに従うようにしましたが、 "the_modified_author" を通過させることはできません。
何かご意見は?
Adminカラムを変更する はテーマファイルではなくプラグイン に属します。なぜならテーマはフロントエンド出力以外に何も変更すべきではないからです。ここで完全なプラグインを入手することができます:プラグイン製品エディタColumn。
あなたがリンクしているドキュメントを見ると、私はプラグインの作者が複数の別々のタスクを混在させる子クラスを必要とするのを見ます。それは良いことではありません。その理由はすぐにわかります。
一方、私たちはプラグインサポートビジネスにはいません。私たちは自分のソリューションビジネスを探すにいます。それでは、そのプラグインを無視してコア機能を見てみましょう。
"manage_{$post_type}_posts_columns"
に登録されています。投稿タイプproduct
の場合、これは"manage_product_posts_columns"
になります。"manage_{$post_type}_posts_custom_column"
に出力されます。これらは非常に貧弱な名前です。彼らはここで何が起こるのか本当に教えてくれません。
両方のフックは、ファイルwp-admin/edit.php
がロードされるときに呼び出されるので、アクション'load-edit.php'
が起動するまで待ちます。
編集ページで、get_current_screen()
から返されたオブジェクトを調べます。そのid
プロパティが"edit-$post_type"
と一致する場合は、コールバックを登録します。
"manage_{$post_type}_posts_columns"
フィルタは、既存の列ヘッダの配列を返します。エントリを追加して配列を返します。
function add_column( Array $columns )
{
$columns[ 'modified_author' ] = 'Last modified by';
return $columns;
}
"manage_{$post_type}_posts_custom_column"
アクションは$column_name
と$post_id
を与えます。他のカスタム列がある可能性があるので、列名を以前に登録した名前の'modified_author'
と比較します。それらには触れたくありません。与えられた列名が私たちのものである場合、投稿を修正した最後の作者のIDを取得するために$post_id
を使います:
$last_id = get_post_meta( $post_id, '_edit_last', TRUE );
$last_user = get_userdata( $last_id );
print esc_html( $last_user->display_name );
私たちのコードの短いバージョンはこのようになります。
add_action( 'load-edit.php', function() {
$post_type = 'product';
$col_name = 'modified_author';
$screen = get_current_screen();
if ( ! isset ( $screen->id ) )
return;
if ( "edit-$post_type" !== $screen->id )
return;
add_filter(
"manage_{$post_type}_posts_columns",
function( $posts_columns ) use ( $col_name ) {
$posts_columns[ $col_name ] = 'Last modified by';
return $posts_columns;
}
);
add_action(
"manage_{$post_type}_posts_custom_column",
function( $column_name, $post_id ) use ( $col_name ) {
if ( $col_name !== $column_name )
return;
$last_id = get_post_meta( $post_id, '_edit_last', TRUE );
if ( ! $last_id ) {
print '<i>Unknown</i>';
return;
}
$last_user = get_userdata( $last_id );
print esc_html( $last_user->display_name );
},
10, 2
);
});
これは機能しますが、よくありません。
コードを改良しましょう。
コールバックの登録はControllerによって行われるべきです。データをどこから取得し、どのようにユーザーの画面に表示するかを知る必要はありません。したがって、これらをabstract dependencies:)として渡します。
class Controller
{
/**
* @var Column_Data
*/
private $data;
/**
* @var Column_View
*/
private $view;
/**
* @param Column_Data $data
* @param Column_View $view
*/
public function __construct( Column_Data $data, Column_View $view ) {
$this->data = $data;
$this->view = $view;
}
/**
* @return void
*/
public function setup()
{
$screen = get_current_screen();
$post_type = $this->data->get_post_type();
if ( ! isset ( $screen->id ) )
return;
if ( "edit-$post_type" !== $screen->id )
return;
add_filter(
"manage_{$post_type}_posts_columns",
[ $this->data, 'add_column' ]
);
add_action(
"manage_{$post_type}_posts_custom_column",
[ $this->view, 'render_column' ],
10, 2
);
}
}
Column_Data
とColumn_View
は interfaces であり、具象クラスではないので、そのコントローラを異なるデータプロバイダや出力ハンドラで再利用できます。これらのインターフェースを構築しましょう。
データ用のインターフェースには以下のメソッドが必要です。
interface Column_Data
{
/**
* @param array $columns
* @return array
*/
public function add_column( Array $columns );
/**
* @param int $post_id
* @return string
*/
public function get_column_content( $post_id );
/**
* @return string
*/
public function get_post_type();
/**
* @param $column_name
* @return bool
*/
public function is_valid_column( $column_name );
}
Output/viewオブジェクトはただ一つのメソッドを必要とします:
interface Column_View
{
/**
* @param string $column_name
* @param int $post_id
* @return void
*/
public function render_column( $column_name, $post_id );
}
ビューはデータモデルから情報を取得する必要があるため、具象クラスのコンストラクタにインスタンスを渡します。
class Last_Mod_Author_Column_Output implements Column_View
{
/**
* @var Column_Data
*/
private $data;
/**
* @param Column_Data $data
*/
public function __construct( Column_Data $data )
{
$this->data = $data;
}
/**
* @param string $column_name
* @param int $post_id
* @return void
*/
public function render_column( $column_name, $post_id )
{
if ( ! $this->data->is_valid_column( $column_name ) )
return;
$content = $this->data->get_column_content( $post_id );
if ( '' === $content )
print '<i>Unknown</i>';
else
print esc_html( $content );
}
}
データモデルは投稿タイプを知っている必要があるので、それを具象クラスのコンストラクタに渡します。
class Last_Mod_Author_Column_Data implements Column_Data
{
/**
* @var string
*/
private $post_type;
/**
* @var string
*/
private $column_name = 'modified_author';
/**
* @param string $post_type
*/
public function __construct( $post_type )
{
$this->post_type = $post_type;
}
/**
* @param array $columns
* @return array
*/
public function add_column( Array $columns )
{
$columns[ $this->column_name ] = 'Last modified by';
return $columns;
}
/**
* @param int $post_id
* @return string
*/
public function get_column_content( $post_id )
{
$last_id = get_post_meta( $post_id, '_edit_last', TRUE );
if ( ! $last_id ) {
return '';
}
$last_user = get_userdata( $last_id );
return $last_user->display_name;
}
/**
* @return string
*/
public function get_post_type()
{
return $this->post_type;
}
/**
* @param $column_name
* @return bool
*/
public function is_valid_column( $column_name )
{
return $this->column_name === $column_name;
}
}
そして今、私たちはこれらのクラスを使うことができます:
add_action( 'load-edit.php', function() {
$model = new Last_Mod_Author_Column_Data( 'product' );
$view = new Last_Mod_Author_Column_Output( $model );
$controller = new Controller( $model, $view );
$controller->setup();
});
わずかな変更を加えるだけで、同じコードをページに使用できます。
add_action( 'load-edit.php', function() {
$model = new Last_Mod_Author_Column_Data( 'page' );
$view = new Last_Mod_Author_Column_Output( $model );
$controller = new Controller( $model, $view );
$controller->setup();
});
なぜこの長いコードのほうが良いのですか?
コントローラやビューを変更することなく、別のテーブル、テキストファイル、または外部APIから値を取得するクラスにデータインターフェイスを実装できます。
コントローラやデータモデルを変更せずに、著者名を太字にしたり、別の代替方法を使用したりするビューを使用できます。
依存関係(スタブ)用のダミーオブジェクトを提供することで、すべてのクラスのすべてのパブリックメソッドをテストできます。
私たちはあなたが言及したプラグインの作者のように継承を使わないので fragile base class問題 を避けます。