web-dev-qa-db-ja.com

カスタム投稿タイプリストの表に "Last Edited by"列を追加する

現在私はWordpressの "Admin Columns Pro"プラグインを使ってバックエンドのいくつかの列を修正しています。このプラグインには、投稿(または私の場合は商品)の作成者を列として表示する機能が含まれています。

しかし、私は製品の最新の編集者が誰であるかを知る必要がある立場にいます。基本的に私たちが今持っているのは1人の個人が製品ドラフトを作成し、それから誰かがそれを完成させることです、そして私はそれらの名前の両方を見る必要があります。

プラグインには、カスタムカラムを作成するための ドキュメントが含まれています 、そしてそれに従うようにしましたが、 "the_modified_author" を通過させることはできません。

何かご意見は?

5
itwasluck3

Adminカラムを変更する はテーマファイルではなくプラグイン に属します。なぜならテーマはフロントエンド出力以外に何も変更すべきではないからです。ここで完全なプラグインを入手することができます:プラグイン製品エディタColumn

あなたがリンクしているドキュメントを見ると、私はプラグインの作者が複数の別々のタスクを混在させる子クラスを必要とするのを見ます。それは良いことではありません。その理由はすぐにわかります。

一方、私たちはプラグインサポートビジネスにはいません。私たちは自分のソリューションビジネスを探すにいます。それでは、そのプラグインを無視してコア機能を見てみましょう。

enter image description here

  • headers投稿タイプの/はフィルタ"manage_{$post_type}_posts_columns"に登録されています。投稿タイプproductの場合、これは"manage_product_posts_columns"になります。
  • contentは、アクション"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
    );
});

これは機能しますが、よくありません。

  • 再利用同じコードを投稿やページ、その他に追加するためにそのコードを使用することはできません。
  • test/コード)はできません。すべてが1回の呼び出しで行われるためです。
  • 懸念の分離 は起こりません。私たちは多種多様なタスクを混ぜ合わせています:
    • コールバックを登録する
    • 列名を検証する
    • Postメタテーブルから値を取得する
    • 列のエスケープ出力を印刷する

コードを改良しましょう。

コールバックの登録は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_DataColumn_Viewinterfaces であり、具象クラスではないので、そのコントローラを異なるデータプロバイダや出力ハンドラで再利用できます。これらのインターフェースを構築しましょう。

データ用のインターフェースには以下のメソッドが必要です。

  • 列ヘッダーを追加します
  • コンテンツを取得します(たとえば、著者の表示名)
  • どの投稿タイプを処理するかをコントローラに伝えます。
  • 呼び出されたときに正しい列で動作している場合はビュー
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問題 を避けます。

14
fuxia