投稿の自動保存(Autosave)の判別について
この記事は公開または最後に更新されてから2116日が経過しています。情報が古くなっている可能性があるのでご注意下さい。
『全てのアップ画像によるスライドショーのjQuery 』において、画像のリストファイルを作成するにあたり、アクションフックsave_post を使っています。
アクションフックsave_post においては、公開や下書きのボタンを押した時だけではなく、Autosave におけるrevision の自動保存の時にも効力があります。
今回の画像のリストファイルの作成のように、意図的にしかも最終的な意味でボタンを押すなどした時に、その投稿において一度だけ動きさえすれば良い場合には、Autosave の時には動かないように、その保存がAutosave によるものか否かを判別する必要がありました。
通常であればsave_post ではなく投稿が公開されるときのフックであるpublish_post を使えばなんら苦労する必要はありません。がしかし、アップロードした画像を画像リストに登録したいという私的な場合において、予約投稿の画像においても即座にリストに加えたいといった場合では、publish_post は使えないのでいろいろと余計な事を考えなくてはならない状況に陥っているというわけなのです。
codex を探してみると、とりあえすこの三つが使えそうです。
- if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
- wp_is_post_revision()
- wp_is_post_autosave()
《 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) 》
これはcodex の『関数リファレンス/add meta box 』のページに出ています。日本語ページにあるということは英語ページにも当然あります。
<用例>において 「データが入力された際 save_post アクションフックを使って何かを行う→投稿を保存した際、カスタムデータも保存する」の中で使用例が示されています。save_post 絡みであるわけです。
この定数”DOING_AUTOSAVE” がどこで定義されているかと探して見ると、「 /wp-admin/includes/post.php 」の
”function wp_autosave()” の中、1740行目あたりに出現します。ちなみに「 post.php 」は「 /wp-admin/post.php 」と「 /wp-includes/post.php 」というように同名ファイルが違うディレクトリに存在しています。紛らわしいですね。「 revision.php 」もまた同様です。
《 wp_is_post_revision() & wp_is_post_autosave() 》
この二つは「 /wp-includes/revision.php 」にあります。
この二つの違いは、autosave の方がpost_name にautosave があるか判別している点です。
《 アクションフック save_post 》
アクションフックsave_post 自体は、これの場合は「 /wp-includes/post.php 」の中に記述してあって、 ”function wp_insert_post()”の一番最後の部分、3580行以降にあります。そのアクションフックの部分を抜き出しますと以下の様になってます。
post type 指定限定型のsave_post に通常のsave_post 、そしてwp_insert_post が並んでいます。これを見るとそれぞれ引数も全て同じ三つで、save_post とwp_insert_post は名前が異なるだけのものです。
投稿タイプを限定できるようにpost_type 指定型があるのはこれを見て知りました。codex には通常型を使用して投稿タイプを判別する方法が書いてあって、これの使用方法は載っていないと思います。新しく追加されたんですね。
”function wp_insert_post()”の一番最後の部分に記述してあるということで、これらのフックは投稿のデータが全てデータベースに保存された後で実行されるものだということです。
ソースを読めばいかなるように動くのかはわかりそうなものですが、今一つ実感がつかめないので下記のようなコードを作って実験してみました。
- 投稿を新たに書き始める時に「新規追加」をクリックして何かしらを記入するためにフォーカスを移動などすると、すぐにその投稿用のデータが作成されます。このデータのその時のpost_status は「 auto-draft 」となっていて、自動的に作成されるデータではありますが DOING_AUTOSAVE は定義されておらず引っ掛かりません。wp_is_post_autosave() の返り値は「 false 」です(当然なのですが・・・後述)。
- 記事を書き始めると時間が経過するにつれ、自動的に保存されるようになります。この時点では初めに作成された元のデータが上書きされて保存されていきます。自動保存が始まった時のpost_status は「 draft 」に変更されています。そしてこれらの自動保存は DOING_AUTOSAVE が定義され引っ掛かるようになります。しかし、wp_is_post_autosave() の返り値は「 false 」です(当然・・・)。
- ここで”公開”や”下書き”ボタンをクリックして投稿を意図的に保存すると、当然のことながら DOING_AUTOSAVE には引っ掛からず、wp_is_post_autosave() の返り値も「 false 」です。そしてpost_status は”公開”であれば「 publish 」、”予約”であれば「 future 」に更新されます。そしてこの時に投稿の元データとは別のデータとして revision データが作成されます。こちらのpost_status は「 inherit 」
- 改めて記事を更新するとして、投稿一覧から記事をクリックし、記事の編集を始めます。再び時間が経つにつれ自動保存されていくようになりますが、この場合は自動保存されるたびに revision として新しいデータが作成されて溜まっていきます。この場合は DOING_AUTOSAVE は定義されていて引っ掛かり、wp_is_post_autosave() の返り値は「 false 」ではなくなり初めて元の原稿データのpost_ID を返すようになります。
- ”更新”などをクリックして意図的に保存した場合は、上記、”公開”や”下書き”ボタンをクリックした時と同じ結果となります。
自動保存におけるrevision データに関しては、codex の『リビジョン管理』のページに「WordPress のリビジョンシステムは、保存された下書きまたは公開済み投稿それぞれの更新記録を保存します。」と、書いてあるように、新規に記事を作成して一度意図的に保存したことのあるデータに対して作成されます。
wp_is_post_autosave() においては、そのrevision データを元にして判別した結果なので、まだ一度も保存されていない新規作成の段階ではrevision データは存在せず、それゆえに返す結果は全て「 false 」であり、その段階でのautosave の判別には全く効力が無いということになります。これは基本的に同じ事をしているwp_is_post_revision() にしても同様です。
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )においては、新規作成の画面を開いてすぐに作成されるこの投稿自体のデータ(post_status が ”auto-draft”)を除いて、それ以降に保存される元データの上書き時(post_status が ”draft”)においても、一度保存した後での revision が作成される時にも有効となります。
ちなみにアクションフックsave_post は、当然のことながら予約投稿が公開される時(post_status が future → publish に更新される)や、投稿が削除される時にもフックされます。
DOING_AUTOSAVE を使用すれば、とりあえず自動保存は避けられるとしても、最初にデータが作成される時、意図的に保存した時の最低でも二度は動いてしまうことになります。予約投稿であれば公開する時も入りますから3度ということです。データを更新して保存すればその都度起動するのは当然のこと。
・・・さて、アクションフックsave_post で動かしたいコードを一度だけにするにはどうしたらいいかと?
そのコードを起動してやりたいことが、その時投稿する内容に関することを全く使用しないものであれば、迷うことなく最初にこのデータが作成される時を利用すれば良いという事になります。
パラメータから$post が得られるので、$post->post_status が”auto-draft” で限定すれば出来るはず。しかし、念を押すとこの時点では投稿の内容というものは全く無いに等しいです。
と、いうことであれば、特に autosave を検出しなくとも、得られるpost_status により判別すれば良いということになります。
「 publish 」か「 future 」で限定すれば良いと。しかし、「 future 」の場合は、いずれ「 publish 」に更新する時に2度目の起動をしてしまいます。それをどうするかということになります。
投稿のステイタス( post_status )が変更される時のアクションフックが『 Post Status Transitions 』としてちゃんと用意されています。
- transition_post_status Hook
- {old_status}_to_{new_status} Hook
- {status}_{post_type} Hook
このなかで、特定のステイタスの変移に絞ってフックできる形式が、
” {old_status}_to_{new_status} Hook ” です。
codex から抜粋すると以下のとおり。
An {old_status}_to_{new_status} action will execute when a post transitions from {old_status} to {new_status}. The action is accompanied by the $post object. In the add_action() function call, the action priority may be set between 0 and 20 (default is 10) and it is necessary to specify the number of arguments do_action() should pass to the callback function.
ちなみにこのフックは「 /wp-includes/post.php 」の” function wp_transition_post_status ”の中、4057行あたりに記述してあります。
これを使用して ” future_to_publish ” で限定してフックし、その時だけsave_post のアクションフックが作動しないように remove してしまえばいいんじゃないかと。
あと、たとえば予約投稿をいくつもまとめて行う場合など、アップロードディレクトリのスキャンは投稿するごとに毎回動かなくとも最後の投稿の時一度だけ動けば良いわけです。その場合にどうするかということになりますが、たとえばスキャンをキャンセルする投稿においては一度レビュー待ちで保存した後で予約投稿すれば、post_status はpending → future となるはずで、であればこの場合も同じように ” pending_to_future ” フックで remove すれば意図的に動かなくすることができるのではと。
ちなみにアクションフック save_postのパラメータで得られる$update 。これに関しては、既にそのデータが存在していればtrue を返します。よってまだ一度も投稿を保存していない状態では最初の” auto-draft ”の時だけがfalse ということになります。投稿を保存したことがあり、その編集時における autosave によって revision が作成される時には、各 revision データというのは新規作成になるので$update で得られる値は全てfalse になります。これは・・・?
と、いうことで以下がそうしてできたコードです。
元々のコードは恥ずかしながらここにあります。→『全てのアップ画像によるスライドショーのjQuery 』 尚、投稿タイプが” post ” の時だけ起動させたいので、post type 指定限定型のフック、save_post_{$post->post_type}を使っています。
Post : 2015/11/20 15:08
Comments feed
Trackback URL : https://strix.main.jp/wp-trackback.php?p=60332