web-dev-qa-db-ja.com

複数のカスタムメタボックスヘルプ

私はこれを1年以上かけて解決しようとしてきましたし、数多くの方法を試してみましたが、うまく機能させることができませんでした。誰かがこれで私を助けることができれば私は本当に感謝します。これが私がやろうとしていることです...

スライドショーのカスタム投稿タイプ(lom_slideshow)の書き込み画面用に15個のカスタムメタボックスを作成する必要があります。各メタボックスは、スライドの数を除いて同じになります(例:スライド1、スライド2 ...)。メタボックスを使用して、各フィールドを別々のカスタムフィールドに保存します。

各メタボックスに含まれる必要があるフィールドは次のとおりです。

  • 番号/テキストフィールド
    • タイトル:スライド番号
    • カスタムフィールドキー:slide1-number
  • Checkbox
    • タイトル:このスライドを隠す
    • カスタムフィールドキー:slide1-hide
  • ラジオ切り替え
    • タイトル:スライドタイプ
    • カスタムフィールドキー:slide1-slidetype
    • 値:画像スライドまたはビデオスライド
  • テキストフィールド
    • タイトル:スライドのタイトル
    • カスタムフィールドキー:slide1-title
  • Image(これはいくつかの方法で動作する可能性がありますが、動作する限りその動作の仕方はあまり気にしません)
    • タイトル:スライド画像
    • カスタムフィールドキー:slide1-image
    • それがどのように機能するか:それが以下のことのうちの1つをする限り、私は幸せになるでしょう...
    • A)投稿に関連付けられているすべての画像添付ファイルのドロップダウンリスト。
    • B)投稿に関連付けられているすべての添付画像のサムネイルのリスト。
    • C)画像をアップロードして投稿に添付する画像アップロードフィールド。
  • WYSIWYG
    • タイトル:スライドの説明
    • カスタムフィールドキー:slide1-desc
    • タイプ:wysiwyg
  • Textarea
    • タイトル:動画埋め込みコード
    • カスタムフィールドキー:slide1-embed

スライド2は同じになりますが、すべてのカスタムフィールド値には "slide1-"ではなく "slide2-"が追加されます

他に考慮しなければならないのは、それらをメインコンテンツコラムに入れて、スライドショーのカスタム投稿タイプ(lom_slideshow)にのみ表示することです。

私が言ったように、私はこの何度も試してみたが決して正しくない。私は過去に親密になり、WYSIWYGの複数のインスタンスをページに表示させることができなかったことを除いて、すべてがうまくいった。それ以降何度もコードを変更したので、もうそのコードはありません。私は最終的に自分のコードの代わりにMore Fieldsプラグインを使ってこれを行うことにし、5つのメタボックスでテストしましたが、15のボックスで実装しようとするとデータベーステーブルに混乱が生じました。私は本当に別のプラグインを望んでいません。

私は実際にそれをコーディングする方法を教えてくれる人を探しています。チュートリアルへのリンクは必要ありません。それが最後の1時間ほどで公開されていない限り、私はそれらすべてを試したことがあると確信しています。

いくつかの背景については、ここで私が最初にやろうとしていたことでした: カスタムメタボックスでスライドショーカスタム投稿タイプを作成するのを手伝ってください?

EAMannは私に非常に包括的な答えを提供しましたが、それは私のスキルの範囲外であり、実装することはできませんでした。私はこれでようやく行われることを期待して、ここでこの質問に私の要求を単純化しました。

7
matt

複雑な問題を(合理的に)解く

複雑な問題を解決するには、標準化された/よく知られたアプローチがあります。複雑な問題を一連の小さな問題に分割します。小さい問題は解決が簡単です。あなたがそれぞれのより小さな問題を解決したならば、あなたは最も頻繁にあなたの複雑な問題をすでに解決した。

配置して分割する

これまでのところ理論です。あなたのニーズをチェックしましょう。私はあなたが上で説明したものを私自身の言葉で並べる:

  • 一連のオプションを複数のインスタンス(スライド1〜スライドN)で処理します。
  • ポストメタ値に値を格納します。
  • 複数のインスタンスのメタボックス(スライド1からスライドNまで)。
  • Metabox内のスライドショーエディタ(フォーム)(やはり複数のインスタンス)
  • データを編集するための基本フォーム(単純なHTMLフォーム要素など)
  • 解決する技術的な問題、複数のWYSIWYGエディタがあります。
  • あなたのより複雑な画像入力。

それでそれをどのようにコーディングするのですか?最も重要な部分は、実際のコーディングを始める前に、本当に達成したいことと、問題をより小さな部分に分割する方法を決心することです。上記のリストは完全ではないかもしれません、それは私があなたの質問から読むことができたものだけです。そしてフォーマットは全く不特定です。それはあなたが書いたものからの多かれ少なかれの繰り返しですが、少しはシングルポイントに順序付けられています。

それで、それらがあなたの必要性であるかどうか調べて、あなたが適当と思うすべてにそのリストを広げてください。

完了したら、次のステップは、それらの必要なものを簡単な言葉で、そしてプラグインの機能のリストとしてどのように表現できるかを調べることです。

  1. このためのプラグイン:SlideshowPlugin。
  2. スライドで構成されるスライドショー。
  3. スライドショーを編集するためのエディタ。
  4. スライドショーデータを保存および取得する場所。

非常に単純に見えますね。私はおそらくここで何かを逃したかもしれないので、あなたが続ける前にあなた自身で二重チェックしてください。書かれているように、コーディングを始める前に、単純な言葉遣いと言葉で頭を悩ませてください。どの部分に問題があるのか​​、どの部分に問題がないのか、またはHTML Input要素の命名のような詳細のコーディング方法についても考えないでください。あなたがすでにそのような長い時間をかけて最初からやり直そうとしたのであれば、それは難しいことだと思います。

鉛筆と紙をつかみます。これはしばしば誰かの心を補うのに役立ちます。

ご覧のとおり、ここではMetaboxまたはCustom Post Typeの必要性を指定していません。最初にあなたの問題の部分について学ぶのは具体的すぎます。 MetaboxまたはCustom Post Typeは非常に具体的で、おそらく既にプラグインをコーディングする方法があります。それで私はしばらくの間これを避けて、簡潔にすることを試みましたが正確にニーズを記述してください。 Metaboxなどは、デザインにおいて役割を果たす可能性があるものです。どれどれ。

プラグインをデザインする

何を達成したいのかを知った後は、プラグインの設計方法について決心することができます。これは小さな絵を描くことでできます。機能リストから部品を識別して、互いに関連して設定するだけです。お分かりのように、あなたは実際に美術をする必要はありません。

design of a slideshow plugin

私の悪い執筆を恐れて、私はこれが読まれることを願っています。いずれにせよ、あなたはあなた自身のイメージを作成することができます、これはほんの一例です。デザインはさまざまである可​​能性があるので、同じように描かないのであれば、それは普通のことです。

私は紙の上でデザインのステップの始めをするのが好きです。なぜならそれは上から問題についてのより良い見方を得るのを助けます、そしてそれは紙よりコンピュータよりはるかに速いです。

それで今あなたはあなたのリストをあなたのデザインと比較して、あなたがデザインの中に持っている部分によってリストからのすべての特徴がカバーされているかどうかチェックすることができました。これまでのところ私のリストと私のイメージにとっては良さそうに見えるので、続けますが、このステップを飛ばさないでください。さもなければあなたが何かを逃したかどうかわかりません。そして、すぐにコーディングを始めると、すでにコーディングされているものをイメージやリストより変更するのがはるかに困難になります。

設計における問題の分離

今、このプラグインは心の中でより具体的になります。ちょっとしたデザインの後は、おそらくコーディングを開始する適切な時期です。私たちが一番上にリストを持っているので、私は各部分がお互いにどのように関係しているか知っているように私はそれ自身で各点について検討し、デザインと照合することができます。

すべての部分が終わっていれば、プラグインはすぐにすべてのことを頭に入れないで準備ができているので、これが元の問題でした。

私は今コメントスタイルといくつかのサンプルコードで少しコーディングをします。これは最初の実装のアイデアであり、コードはテストされていません。それは、私にとって、そしてあなたにとっておそらく汚れのないようにするためのものです。私は時々すでに具体的すぎることが多いので、気にしてください。私がコードを書くとき、私はそれを作成する間、それをかなり頻繁に書き直す、しかし私はサンプルコードを作成する間、これを見えるようにすることができない。これを覚えておいてください。何かもっと簡単にやるべきことがあれば、自分のルートを選択してください。後でコードを変更して拡張する必要があるので、これは実際にはいくつかのコード例にすぎません。

プラグイン

フックの登録やスライドショーとそのスライドとエディタ用のメタボックスの提供など、基本的な操作を処理するクラスです。これがすべての始まりです。プラグインはコードの一箇所から始まります。私はそのブートストラップと呼びます。

<?php
/** Plugin Headers ... */

return SlideshowPlugin::bootstrap(); 

class SlideshowPlugin {
    /** @var Slideshow */
    private $slideshow;
    /** @var SlideshowMetaboxes */
    private $metaboxes;
    /** @var SlideshowPlugin */
    static $instance;
    static public function bootstrap() {
        $pluginNeeded = (is_admin() && /* more checks based your needs */ );
        if (!$pluginNeeded) 
            return;
        }
        if (NULL === self::$instance) {
            // only load the plugin code while it's really needed:
            require_once('library/Slideshow.php');
            require_once('library/SlideshowSlide.php');
            require_once('library/Store.php');
            require_once('library/Metaboxes.php');
            require_once('library/Metabox.php');
            require_once('library/Form.php');
            // ...
            self::$instance = new SlideshowPlugin();
        }
        return self::$instance;
    }
    private function addAction($action, $parameters = 0, $priority = 10) {
        $callback = array($this, $action);
        if (!is_callable($callback)) {
            throw new InvalidArgumentExeception(sprintf('Plugin %s does not supports the %s action.', __CLASS__, $action));
        }
        add_action($action, $callback, $parameters, $priority);
    }
    public function __construct() {
        $this->addAction('admin_init');
    }
    /**
     * @return bool
     */
    private function isEditorRequest() {
        // return if or not the request is the editor page
    }
    /**
     * @-wp-hook
     */
    public function admin_init() {
        // register anything based on custom post type and location in the admin.
        // I don't care about the details with CPT right now.
        // It's just that editorAction is called when we're on the slideshow
        // editor page:
        if ($this->isEditorRequest()) {
            $this->editorAction();
        }
    }
    private function getPostID() {
        // ... code goes here to get post id for request 
        return $postID;
    }
    private function getSlideshow() {
        is_null($this->slideshow)
            && ($postID = $this->getPostID())
            && $slideshow = new Slideshow($postID)
            ;
        return $slideshow;
    }
    private function getMetaboxes() {
        is_null($this->metaboxes) 
            && ($slideshow = $this->getSlideshow())
            && $this->metaboxes = new SlideshowMetaboxes($slideshow)
            ;
        return $this->metaboxes;
    }
    private function editorAction() {
        $metaboxes = $this->getMetaboxes();
    }
}

だからこのプラグインクラスはすでにかなり完成しています。 postIDの取得方法は指定しませんでしたが、すでに独自の関数にカプセル化されています。その隣に、これがエディタを表示するのに適したページであるかどうかをチェックするコードはありませんでしたが、そのためのスタブコードはすでにいくつかあります。

最後に、リクエストが実際のカスタム投稿タイプのエディタページにある場合はeditorAction()が呼び出され、そこで、Metaboxが取得されます。それでおしまい。プラグインはもうかなり完成しているはずです。スライドショーがあり、Metaboxesの面倒を見る。デザインと比較して、それらはプラグインとリンクされている部分です。画像と比較してください。

  1. エディタを表示するかどうかの決定。
  2. プラグインとスライドショーの間の接続。プラグインにはすでにスライドショーがあります。
  3. メタボックスへの接続。プラグインはすでにメタボックスを持っています。

完全に見えます。仕事はその部分で終わった。

スライドショーとスライド

スライドショーは投稿に1:1でマッピングされます。そのため、投稿IDが必要です。スライドショーはデー​​タを保持することができるので、基本的にはデータ型です。そこに必要なすべての値が格納されています。 0からN個のスライドがあるという意味で、これはcompoundデータ型です。また、スライドは、各スライドの情報を保持する別のデータ型です。

スライドはメタボックスとおそらくフォームによって使用されます。

私はさらにスライドショーデータの保存と検索をそれらのデータ型(デザインではStoreとラベルされたボックス)に実装することを選択しました。データ型と実際のオブジェクトが混在しているので、どういうわけか汚いのです。

しかし、StoreはSlideshowとSlidesに接続されているのでonly私はそれらを互いに接続しました。おそらく近い将来には近づきすぎますが、設計の最初の実装では、今のところ有効なアプローチだと思います。これが最初のアプローチなので、とにかくリファクタリングされてからそれほど時間はかかりません。そのため、問題がいくつかあっても、その方向性が正しいと確信しています。

class SlideshowSlide {
    private $slideshow;
    private $index;
    public $number, $hide, $type, $title, $image, $wysiwyg, $embed
    public function __construct($slideshow, $index) {
        $this->slideshow = $slideshow;
        $this->index = $index;
    }
    public function getSlideshow() { return $this->slideshow; }
    public function getIndex() { return $this->index; }
}

class Slideshow implements Countable, OuterIterator {
    private $postID;
    private $slides = array();
    private function loadSlidesCount() {
        $postID = $this->postID;
        // implement the loading of the count of slides here
    }
    private function loadSlide($index) {
        $postID = $this->postID;
        // implement the loading of slide data here
        $data = ... ;
        $slide = new SlideshowSlide($this, $index);
        $slide->setData($data); // however this is done.
        return $slide;            
    }
    private function loadSlides() {
        $count = $this->loadSlidesCount();
        $slides = array();
        $index = 0;
        while(($index < $count) && ($slide = $this->loadSlide($index++)))
            FALSE === $slide || $slides[] = $slide
            ;
        $this->slides = $slides;
    }
    public function __construct($postID) {
        $this->postID = $postID;
        $this->loadSlides();
    }
    public function count() {
        return count($this->slides);
    }
    public function getInnerIterator() {
        return new ArrayIterator($this->slides);
    }
    private function touchIndex($index) {
        $index = (int) $index;
        if ($index < 0 || $index >= count($this->slides) {
            throw new InvalidArgumentExpression(sprintf('Invalid index %d.', $index));
        }
        return $index;
    }
    public function getSlide($index) {
        $index = $this->touchIndex($index);
        return $this->slides[$index];
    }
}

SlideshowクラスとSlideクラスもかなり充実していますが、実際のコードもありません。それは、プロパティ/メソッドとそれを処理するものを持つという私の考えをデータの検索をどのように実装することができるかについても示すためだけのものです。

メタボックス

Metaboxは、それがどのスライドを表しているのかを知る必要があります。スライドショーと具体的なスライドを知る必要があります。スライドショーはプラグインによって提供されることができ、スライドはインデックスによって表されることができる(例えば0…N、ここでNはスライドショー内のスライドの数−1である)。

class Metabox {
    public function __construct(SlideshowSlide $slide) {
    }
}

Metaboxクラスは実際にはどういうわけかプラグインクラスを拡張しています。プラグインクラスでもできることはいくつかありますが、複数のインスタンスを持つことができるようにしながらプラグインのコンテキストでスライドを表現させたいので、この方法を選びました。

Metaboxは要求ロジックを処理する必要があります。実際には何らかの方法で出力されるMetaboxを表しますが、フォーム入力を処理する必要があるため入力されます。

良いことは、フォームの出力と入力はフォームオブジェクトによって行われるため、実際には詳細を処理しないことです。

そのため、このクラスを最後までコーディングしたとしたら、おそらく完全に削除したはずです。今はわかりません。現時点では、特定のスライドのエディタページにMetaboxが表示されています。

メタボックス

class Metaboxes
    private $slideshow;
    private $boxes;
    public function __construct(Slideshow $slideshow) {
        $this->slideshow = $slideshow;
        $this->editorAction();
    }
    private function createMetaboxes() {
        $slides = $this->slideshow;
        $boxes = array();
        foreach($slides as $slide) {
            $boxes[] = new Metabox($slide);
        }
        $this->boxes = $boxes;
    }
    private function editorAction() {
        $this->createMetaboxes();
    }

これまでのところ、ここでは小さなコードをいくつか書いただけです。 Metaboxesクラスは、すべてのメタボックスのマネージャとして機能します。 Metaboxとしてのスライドショーの代表はスライドを表します。

そのスタブコードは、スライドごとに1つのMetaboxをインスタンス化するだけで、それほど多くはありません。それは結局もっと多くのことができるし、しなければならない。

ここで Composite Pattern を使用することをお勧めします。そのため、Metaboxesオブジェクトに対してアクションを実行すると、それが運ぶすべてのMetaboxに対して同じアクションが実行されます。新しいMetaboxを作成するインスタンス化と比較できます。したがって、後で個々のMetaboxを扱う必要はなく、単にMetaboxesオブジェクトを扱う必要はありません。ただのアイデアです。

Formはおそらく、あなたが持っているものの中で最も複雑なものであり、ものとコード行を処理するという意味です。ブラウザ経由で処理されるようにデータを抽象化する必要があります。そのため、複数のインスタンスを処理できなければなりません。これを実現するには、フォーム要素の名前(一意である必要があるため)の前にgenrealの接頭辞(例: "slide")を付け、次に識別子(slide index、ここではindexと命名します)例えばソートキーを持つ場合は数字、次に実際の値の識別子(例: "number"または "hide")。

見てみましょう:フォームはそのプレフィックス、スライドの番号、そしてそれに含まれる全てのフィールドを知っています。これは、上記で説明したSlideshowおよびSlideデータ型にほぼ対応しています。いくつかの小さなスタブコード:

/**
 * SlideForm
 *
 * Draw a Form based on Slide Data and a Form definition. Process it's Input.
 */
class SlideForm {
    /** @var Slide */
    private $slide;
    private $prefix = 'slide';
    public function __construct(Slide $slide) {
       $this->slide = $slide;
    }
    private function inputNamePrefix() {
       $index = $this->slide->getIndex();
       $prefix = $this->prefix;
       return sprintf('%s-%d-', $prefix, $index);
    }
    private function inputName($name) {
       return $this->inputNamePrefix().$name;
    }
    private function printInput(array $element) {
        list($type, $parameters) = $element;
        $function = 'printInput'.$type;
        $callback = array($this, $function)
        call_user_func_array($callback, $parameters);
    }
    private function printInputText($value, $name, $label, $size = 4) {
        $inputName = $this->inputName($name);
        ?>
        <label for="<?php echo $inputName ; ?>">
          <?php echo htmlspecialchars($label); ?>:
        </label>
        <input type="text" 
          name="<?php echo $inputName; ?>"
          size="<?php echo $size; ?>"  
          value="<?php echo htmlspecialchars($value); ?>">
        <?php
    }
    private function printInputCheckbox($value, $name, $label, ... ) { ... }
    private function printInputRadio($value, $name, $label, ... ) { ... }
    private function printInputImage($value, $name, $label, ... ) { ... }
    private function printInputWysiwyg($value, $name, $label, ... ) { ... }
    private function printInputTextarea($value, $name, $label, ... ) { ... }
    // ...
    private function mapSlideValueTo(array $element) {
        $slide = $this->slide;
        list($type, $parameters) = $element;
        list($name) = $parameters;
        $value = $slide->$name;
        array_unshift($parameters, $value);
        $element[1] = $parameters;
        return $element;
    }
    /**
     * @return array form definition
     */
    private function getForm() {
        // Form Definition
        $form = array(
           array(
               'Text', 
               array(
                   'number', 'Number'
               ),
           array(
               'Checkbox', 
               array(
                   'hide', 'Display', 'Hide This Slide'
               ),
           ),
           array(
               'Radio', 
               array(
                   'type', 'Type', array('Image', 'Video')
               ),
           ),
           array(
               'Text', 
               array(
                   'title', 'Title', 16
               ),
           ),
           // ...
        );
        return $form;
    }
    public function printFormHtml() {
        $form = $this->getForm();

        foreach($form as $element) {
            $element = $this->mapSlideValueTo($element);
            $this->printInput($element);
        }
    }
    public function processFormElement($element) {
        list($type, $parameters) = $element;
        list($name) = $parameters;
        $inputName = $this->inputName($name);

        $map = array(
            'Text' => 'String', 
            'Checkbox' => 'Checkbox', 
            'Radio' => 'Radio', 
        );

        // I would need to continue to code there.
        throw new Exception('Continue to code: Process Form Input based on Form Definition');
    }
    public function processForm() {
        $form = $this->getForm();

        foreach($form as $element) {
            $this->processFormElement($element);  // <- this function needs to be coded
        }
    }
    // ...
}

このクラスは、3つのことを一度に処理するため、非常に大きくなっています。

  1. フォーム定義
  2. フォーム出力レンダリング
  3. フォーム入力処理

これをそれが表す3つの部分に分割するのが賢明です。私はあなたに任せます。フォームの機能をより小さなタスクにカプセル化する方法をすでに示しているので、ニーズに応じるのは簡単です。例えば。 Image Input要素のフォーム出力を微調整する必要がある場合は、簡単に拡張/磨くことができます。 WYSIWYGと同じです。スライドショーやスライドデータ型にはあまり影響がないので、後で実装を変更できます。

この背後にある原理は、懸念の分離とも呼ばれ、それが私が私の答えを始めたのと同じことです。問題をより小さな問題に分割する。これらの別々の問題は解決が簡単です。

これがしばらくの間役立つことを願っています。結局私はカスタム投稿タイプにも戻ってこなかった。私は彼らがプラグインの中に入らなければならないことを知っています、しかし新しいデザインを使うとコードを書く場所を見つけるのはおそらく簡単です。

何が残っている?

  • コードを複数のファイルに分割してください。ファイルごとに1つのクラス。後でそれらを簡単にマージすることができますが、開発のために、小さな問題や部品を独自に解決したい場合は、別のものにしてください。
  • テスト:あなたは自分で部品の機能をテストする必要があります。スライドはそれがすべきことをやっていますか?あなたがクラスを書くならば、喜んであなたはUnittestsを利用することができます、PHPのために PHPUnit があります。これにより、コードが正確に機能することを認識しながらコードを開発できます。これは、単純な問題をそれぞれの単位で解決するのに役立ちます。また、テストを常時実行できるため、後でコードを簡単に変更できます。
  • テスト#2:プラグイン全体をテストする必要があることを確認してください。そうすれば、あなたが探していることを行っていることがわかります。いつでも同じ品質でテストを繰り返すことができるように自動的にこれを行うことができる方法を想像できますか?
22
hakre

私はあなたがあなたが別のプラグインが欲しくないと言ったことを知っています、しかし私は「Verveメタボックス」か「カスタムフィールドテンプレート」をチェックすることをお勧めします。

データの問題に関しては、なぜあなたは複数のwysiwygフィールドが必要なのか、そしてなぜこれがデータベースに影響を与えているのかを詳しく説明できますか。私はいくつかのページで40を超えるカスタムフィールドを使用しましたが、問題に気付きませんでした。

スライドショーについても、私は似たようなことをしていて、jqueryとquery_post型を使って自分で書く必要がありました。たとえば、これは、 "movies"というカスタム投稿タイプのスライダーにランダムな投稿を表示します。 get_post_metaを使用して、任意のカスタムメタ値を追加できます。

<?php $Rand_posts = query_posts('post_type=movies&posts_per_page=5&orderby=Rand');
foreach($Rand_posts as $post) :
?>
2
Wyck