web-dev-qa-db-ja.com

AJAX-ifyで投稿を保存する簡単な方法はありますか?

カスタム投稿タイプを管理できるプラグインがあり、投稿の保存、編集、削除にAJAX機能を追加したいと思います。私はインターネットで同様の問題を見つけることができなかったので、これが簡単に実行可能なものではないかどうか疑問に思いますか?

1
jilseego

JavaScriptを使ってpost.phpにXHRを技術的に作ることができます。以下は投稿のみを保存/編集するための概念実証です。私はまったくテストしていないので、あなたはそれを微調整する必要があるだろうと確信しています。私はあなたにこのようなことを成し遂げるための基本的なワークフローを与えたかったのです。拡張する必要がある場合は、それを取って実行することができます。

ステップ1:新規および既存の投稿用にadmin_headにAJAXハンドラを追加します

function my_post_type_xhr(){
    global $post;
    if('my_post_type' === $post->post_type){
        $post_url = admin_url('post.php'); #In case we're on post-new.php
        echo "
        <script>
            jQuery(document).ready(function($){
                //Click handler - you might have to bind this click event another way
                $('input#publish, input#save-post').click(function(){
                    //Post to post.php
                    var postURL = '$post_url';

                    //Collate all post form data
                    var data = $('form#post').serializeArray();

                    //Set a trigger for our save_post action
                    data.Push({foo_doing_ajax: true});

                    //The XHR Goodness
                    $.post(postURL, data, function(response){
                        var obj = $.parseJSON(response);
                        if(obj.success)
                            alert('Successfully saved post!');
                        else
                            alert('Something went wrong. ' + response);
                    });
                    return false;
                });
            });
        </script>";
    }
}
add_action('admin_head-post.php', 'my_post_type_xhr');
add_action('admin_head-post-new.php', 'my_post_type_xhr');

ステップ2:save_postアクションにフックします。

これは投稿がデータベースに保存された後に実行されるので、必要なポストメタを保存してページのレンダリングを停止することができます。

add_action('save_post', 'save_my_post_type');
function save_my_post_type($post_id){
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;

    #If this is your post type
    if('my_post_type' === $_POST['post_type']){
        //Save any post meta here

        #We conditionally exit so we don't return the full wp-admin load if foo_doing_ajax is true
        if(isset($_POST['foo_doing_ajax']) && $_POST['foo_doing_ajax'] === true){
            header('Content-type: application/json');
            #Send a response
            echo json_encode(array('success' => true));
            exit;
            #You should keep this conditional to degrade gracefully for no JS
        }
    }
}

私が見ることができる唯一の大きな警告はあなたがどういうわけかXHRを通してページのnonceをリフレッシュする必要があるということです。

投稿を削除するために、それが既に仕事をしているリンクをクリックするだけの場合はなぜAJAXを使用したいのかよくわかりません。どの場合でもページを削除すると、edit.phpという別のページに移動します。これは、AJAXを使用するという、1ページの読み込み時にすべてを非同期に保つという目的を達成するためのものです。

これが役に立つことを願っています。

3
Brian Fegter

Brian Fegterの応答は正しい考えでしたが、いくつかのバグがありました。これは同じ原理に基づいた、より洗練された解決策です。

ステップ1:PHP Logic

Functions.php(またはプラグインファイル)に配置する

// Saving the post via AJAX
add_action('save_post', 'save_post_ajax');
function save_post_ajax( $post_id )
{
        # Ignore autosaves
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
                return;

        # Only enabled for one post type
        # Remove this if statement if you want to enable for all post types
        if ($_POST['post_type'] == 'my_custom_post_type')
        {
                # Send JSON response
                # NOTE: We use ==, not ===, because the value may be String("true")
                if (isset($_POST['save_post_ajax']) && $_POST['save_post_ajax'] == TRUE)
                {
                        header('Content-type: application/json');
                        echo json_encode(array('success' => true));

                        # Don't return full wp-admin
                        exit;
                }
        }
}

ステップ2:外部ファイルにJavaScriptを作成する

ページに直接jsをエコーアウトするというBrianの解決策もうまくいきます。

// Avoid collisions with other libraries
(function($) {
        // Make sure the document is ready
        $(document).ready(function() {
                // This is the post.php url we localized (via php) above
                var url = ajax_object.post_url;
                // Serialize form data
                var data = $('form#post').serializeArray();
                // Tell PHP what we're doing
                // NOTE: "name" and "value" are the array keys. This is important. I use int(1) for the value to make sure we don't get a string server-side.
                data.Push({name: 'save_post_ajax', value: 1});

                // Replaces wp.autosave.initialCompareString
                var ajax_updated = false;

                /**
                 * Supercede the WP beforeunload function to remove
                 * the confirm dialog when leaving the page (if we saved via ajax)
                 * 
                 * The following line of code SHOULD work in $.post.done(), but 
                 *     for some reason, wp.autosave.initialCompareString isn't changed 
                 *     when called from wp-includes/js/autosave.js
                 * wp.autosave.initialCompareString = wp.autosave.getCompareString();
                 */
                $(window).unbind('beforeunload.edit-post');
                $(window).on( 'beforeunload.edit-post', function() {
                        var editor = typeof tinymce !== 'undefined' && tinymce.get('content');

                        // Use our "ajax_updated" var instead of wp.autosave.initialCompareString
                        if ( ( editor && !editor.isHidden() && editor.isDirty() ) ||
                                ( wp.autosave && wp.autosave.getCompareString() != ajax_updated) ) { 
                                return postL10n.saveAlert;
                        }   
                });


                // Post it
                $.post(url, data, function(response) {
                        // Validate response
                        if (response.success) {
                                // Mark TinyMCE as saved
                                if (typeof tinyMCE !== 'undefined') {
                                        for (id in tinyMCE.editors) {
                                                var editor = tinyMCE.get(id);
                                                editor.isNotDirty = true;
                                        }   
                                }
                                // Update the saved content for the beforeunload check
                                ajax_updated = wp.autosave.getCompareString();

                                console.log('Saved post successfully');
                        } else {
                                console.log('ERROR: Server returned false. ',response);
                        }
                }).fail(function(response) {
                        console.log('ERROR: Could not contact server. ',response);
                });
        });     
})(jQuery);

ステップ3:JavaScriptファイルをエンキューする

あなたがそれをエコーアウトしたならば(ブライアンのように)、あなたはこれをする必要はありません。スクリプトをデキューしたり、変数をローカライズしたり、スクリプトのロード順を簡単に調整したりできるため、この方法が適しています。

function my_post_type_xhr()
{
        global $post;
        # Only for one post type. 
        if ($post->post_type == 'custom_post_type')
        {
                # The url for the js file we created above
                $url = '/url/to/my/javascript.js';

                # Register and enqueue the script, dependent on jquery
                wp_register_script( 'my_script', $url, array('jquery') );
                wp_enqueue_script( 'my_script' );

                # Localize our variables for use in our js script
                wp_localize_script( 'my_script', 'ajax_object', array(
                                'post_id' => $post_id,
                                'post_url' => admin_url('post.php'),
                ) );
        }
}

add_action('admin_head-post.php', 'my_post_type_xhr');
add_action('admin_head-post-new.php', 'my_post_type_xhr');

このスニペットはナンスを扱っていません。とにかく、それが誰かに役立つことを願っています。

3
Jess Mann