サポート » 使い方全般 » カスタム投稿の公開状態を自動で変更したい

  • 解決済 d.w.c

    (@dwc-1)


    複数人でブログを書くサイトで、投稿者が書いた後は必ずカスタム投稿タイプ非公開になるようにしています。
    それを特定の条件下で自動で公開に切り替わるようにしたいと思っています。

    特定の条件で分岐する部分は後で書くとして、とりあえず非公開のものを自動で公開に切り替える仕組みを作ろうと思って下記のコードを書いてみたのですが、全く動きません。
    見様見真似で書いてみたのでいろいろ間違っていると思うのですが、調べられる範囲でいくつかの修正を試してみても解決方法がわかりませんでした。

    どのように書き換えればいいでしょうか?

    functions.php

    function blog_post_publish($post){
    	if (( $post->post_type == 'hogehoge' ) && ( $post->get_post_status() == 'private' )){
    		$post_id = $post->ID;
    		$my_blog_post = array('id' => $post_id, 'post_status' => 'publish');
    			wp_update_post( $my_blog_post );
    	}
    }
    add_action('pre_get_posts', 'blog_post_publish');
15件の返信を表示中 - 1 - 15件目 (全18件中)
  • CG

    (@du-bist-der-lenz)

    サイトへの複数人の参加者が、各自で書いたブログ記事にキュレーションする仕組みがありますが、そうしたプラグインから得られるヒントが有ると思いますよ。

    こんにちは

    条件判定の $post->get_post_status() となっている部分は、get_post_status($post) じゃないでしょうか?

    あと、pre_get_posts でフックした関数の第一引数は WP_Query なので、このままでは動かないですね

    実装する際にも pre_get_posts を使うなら、こんな感じでしょうか?

    function blog_post_publish( $query ) {
    	if ( true === $query->is_main_query() ) {
    		$post = $query->get_queried_object();
    		if ( false === $post instanceof WP_Post )
    			return
    		// 以降、必要な処理を
    	}
    }

    未検証な上、かなり適当な条件判定なので、詳細はcodexをご参照ください。。。

    • この返信は7 ヶ月、 2 週間前に  Colorful-life. さんが編集しました。理由: 補足追加
    • この返信は7 ヶ月、 2 週間前に  Colorful-life. さんが編集しました。

    ごめんなさい、やっぱり私の書いたコード全然間違ってました。
    $query->get_queried_object() では WP_Postは取得出来ないです

    一応訂正しておくと、こんな感じかと。。。

    function blog_post_publish( $query ) {
    	if ( false === is_admin() && true === $query->is_main_query() && true === $query->is_single() ) {
    		if ( true === isset($query->query_vars['post_type']) && 'hogehoge' === $query->query_vars['post_type'] && true === isset($query->query_vars['name']) ) {
    			$post = get_page_by_path($query->query_vars['name'], 'OBJECT', $query->query_vars['post_type']);
    			if ( true === empty($post) )
    				return;
    			// 以降、必要な処理を
    		}
    	}
    }

    >Colorful-life. さん
    調べていただいてありがとうございます。

    こんな感じでしょうか?

    function blog_post_publish( $query ) {
    	if ( false === is_admin() && true === $query->is_main_query() && true === $query->is_single() ) {
    		if ( true === isset($query->query_vars['post_type']) && 'hogehoge' === $query->query_vars['post_type'] && true === isset($query->query_vars['name']) ) {
    			$post = get_page_by_path($query->query_vars['name'], 'OBJECT', $query->query_vars['post_type']);
    			if ( true === empty($post) )
    				return;
    			// 以降、必要な処理を
    			
    			$post_id = $post->ID;
    			$my_blog_post = array('id' => $post_id, 'post_status' => 'publish');
    			wp_update_post( $my_blog_post );
    		}
    	}
    }

    これだと動かないようです・・・。

    $post_id = $post->ID;
    $my_blog_post = array('id' => $post_id, 'post_status' => 'publish');
    wp_update_post( $my_blog_post );

    の部分も間違ってますでしょうか?

    pre_get_posts アクションはクエリ実行の直前(単一またはアーカイブの投稿の取得直前)に実行されますが、このタイミングで特に何かが起こるわけではありませんし、このタイミングを使う意図がわかりかねます。
    フックするアクションによって引数が異なり、利用可能なデータも変わってきますので、その辺り(条件)を指定していただければ助言しやすくなります。

    投稿のステータスを変更する部分はそれで合っていると思いますが、やはりその上の条件のほうだと思います。

    $my_blog_post = array('id' => $post_id, 'post_status' => 'publish');

    この配列のキーidは大文字IDである必要があると思います

    まずはpost_statusの変更が可能か確認したいという意図でのご相談だとは思うのですが、
    @latobeam様が仰るように、非公開の記事を公開状態にする為の要件定義をはっきりさせたほうが良いかもしれませんね

    あと、補足として・・・

    特定の条件で分岐する部分は後で書くとして

    との事でしたので、コードをご覧いただければ何をしているのか(条件分岐など)はある程度ご理解いただける物と思い、説明など(コードの条件判定も)はしょっている部分があります
    // 以降、必要な処理を から下の行が実行される条件は以下になります。

    ・(管理画面では無い)投稿タイプhogehogeのシングルページをリクエストした場合(post_statusによる条件判定は入っていない)
    ※ 非公開のページがリクエストされる事自体が希(管理画面から投稿を表示した場合等)だとは思いますが、確認用のコードなのだろうという認識です

    d.w.c

    (@dwc-1)

    >LABEさん
    >Colorful-life.さん
    返信が遅くなってすみません。

    >>非公開の記事を公開状態にする為の要件定義をはっきりさせたほうが良い

    目指す形として
    1.「投稿者」に設定してあるユーザーがカスタム投稿タイプにブログを投稿する。
    2.「投稿者」はログイン後も管理画面にはアクセスできず、ACFプラグインで作成したフロントエンド側から投稿できる仕組みacf_form()を利用してブログを書く。
    3.ブログ投稿フォームにアクセスしようとすると自動で白紙の下書きが作成・保存される。
    4.「投稿者」が記事を書いて投稿ボタンを押すと下書きから非公開に変更される。
    5.非公開化から30分後に公開される。
    6.公開されている記事をacf_form()経由で編集すると再度非公開に変更される。
    7.編集後30分で公開される。

    現在5と7で詰まっています。
    投稿or最終編集時刻と現在時刻を比べて条件分岐させようと思っています。

    ===

    acf_form()で投稿しようとするとプレビュー操作ができないので、いったん非公開で投稿して記事を確認後、必要なら修正をするための時間として30分間を用意したいと思っています。
    「投稿者」は自分の非公開記事はフロントエンドから見れるようにしています。(管理画面にアクセスできないため)

    >Colorful-life.さん >>補足
    にわかなので、基本的なことがすこっと抜けてたりします。すみません…
    わからないことはその都度調べてコードを書いている状況です。

    • この返信は7 ヶ月前に  d.w.c さんが編集しました。
    • この返信は7 ヶ月前に  d.w.c さんが編集しました。
    • この返信は7 ヶ月前に  d.w.c さんが編集しました。
    • この返信は7 ヶ月前に  d.w.c さんが編集しました。
    • この返信は7 ヶ月前に  d.w.c さんが編集しました。
    • この返信は7 ヶ月前に  d.w.c さんが編集しました。
    • この返信は7 ヶ月前に  d.w.c さんが編集しました。
    • この返信は7 ヶ月前に  d.w.c さんが編集しました。
    d.w.c

    (@dwc-1)

    ちょっと思いついたのですが、functions.phpではなく、全ページ共通で表示している<body>内のヘッダーエリアに下記を書いてみたところ、目的の動作をするようになりました。

    <?php ////非公開ブログを公開に変更////
    global $post;
    $private_blog= get_posts(array(
    	'post_type'   => 'hogehoge',
    	'numberposts' => -1,
    	'post_status' => 'private'
    ));
    
    if($private_blog){
    	foreach($private_blog as $post):setup_postdata($post);
    		date_default_timezone_set('Asia/Tokyo');
    		$post_time = get_the_time('Y/m/d H:i:s');
    		$edit_time = get_the_modified_time('Y/m/d H:i:s');
    		$key_time  = date('Y/m/d H:i:s', strtotime('-30 min'));
    		
    		if (empty($edit_time)) {
    			$target_time = $post_time;
    		} else {
    			$target_time = $edit_time;
    		}
    		
    		if ($target_time <= $key_time){
    			// データベースにある投稿を公開に変更する
    			$my_post = array(
    				'id'          => $post->ID,
    				'post_status' => 'publish'
    			);
    			wp_update_post( $my_post );
    		}
    	endforeach;
    	wp_reset_postdata();
    }
    ?>

    この方法でも大丈夫でしょうか?
    何か問題があれば教えていただきたいです。

    • この返信は7 ヶ月前に  d.w.c さんが編集しました。
    LABE

    (@latobeam)

    <body> 内のヘッダーエリア、とは header.php のことでしょうか。
    そこにそのコードを書くと、確かにすべてのアクセスに際して実行されるようになります。

    コードに問題はないと思いますが、すべてのアクセスに対してクエリを実行することになるため、処理的に無駄が多いのは否めません。
    wp_cron という仕組みを使えば、「編集の30分後に投稿ステータスを公開に変更する」というような処理を予約実行できるので、効率的ではありますが、結構ややこしいので万人向けではありません。
    参考までにご紹介しておきます。

    大した問題ではないのですが、コードで少し気になった点を。
    日時の比較で Y/m/d H:i:s の形式の文字列で比較されていますが、こういう文字列の比較ってちゃんとなりましたっけ。
    いずれにせよタイムスタンプ( strtotime() )で比較したほうが動作も確実で速い気がします。
    後半のif文の部分はこんな感じではいかがでしょうか。

    if ( $private_blog ) {
    	foreach ( $private_blog as $post ) {
    		setup_postdata($post);
    		date_default_timezone_set('Asia/Tokyo');
    		$post_time = strtotime($post->post_date);
    		$edit_time = strtotime($post->post_modified);
    		$key_time  = strtotime('-30 min');
    		
    		if ( empty($edit_time) )
    			$edit_time = $post_time;
    		
    		if ( $edit_time <= $key_time ) {
    			// データベースにある投稿を公開に変更する
    			// 省略
    		}
    	}
    	wp_reset_postdata();
    }
    d.w.c

    (@dwc-1)

    >LABEさん

    すごくすっきりしていますね!
    ありがとうございます。

    >header.php のことでしょうか。
    そうです。

    >wp_cronという仕組みを使えば
    ちょっと見てみたのですが、理解に時間がかかりそうなので次回必要そうなときに再度調べながらやってみます。参考になります。

    >こういう文字列の比較って
    過去にどこかから引っ張ってきたコードを参考に作ったので出典を忘れてしまいまして…調べたらこんなのがあったので文字列でも比較できるとは思うのですが…

    教えていただいた方法の方が動作も確実で早いとのことでしたので、書き換えました。
    ありがとうございました。

    Colorful-life.

    (@colorfullifeinfo)

    こんばんは

    @latobeamさんが仰るように、wp_cronで処理する方が良いと思いますが、おいおいという事ですので、それは置いておいて…

    // データベースにある投稿を公開に変更する
    $my_post = array(
    	'id'          => $post->ID,
    	'post_status' => 'publish'
    );
    wp_update_post( $my_post );

    この部分、'ID' => $post->ID, (キーを大文字のIDにする)が正しいと思います
    (当方の環境だと小文字’id’で投稿IDを指定した配列をwp_update_postに使用すると、投稿の更新に失敗してしまうのですが、動作していますか?)

    あと、$key_time を投稿日のタイムゾーンと合わせる為に date_default_timezone_set('Asia/Tokyo'); とされていますが、
    これはやめて、WordPress関数の current_time() を使用した方が良いように思います
    $key_time = strtotime( '-30 min', current_time( 'timestamp' ) );

    current_time()関数について:
    http://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/current_time
    ちなみに、Codex日本語版だと記述が無いのですが、ver.3.9以降、date()関数と同様に日付フォーマットも解釈するようになっています

    過去にどこかから引っ張ってきたコードを参考に作ったので出典を忘れてしまいまして…調べたらこんなのがあったので文字列でも比較できるとは思うのですが…

    今回の場合、比較する日付のフォーマットを Y/m/d H:i:s として、年月日時分秒 とも二桁になるフォーマットで揃えてありますので問題無く比較できますが、タイムスタンプで比較した方が間違いがない様に思います

    d.w.c

    (@dwc-1)

    >Colorful-life. さん
    ありがとうございます。

    'id' => $post->IDの部分は当方の環境では動いていたのですが、正しくないとのことなので修正しました。
    current_time()も使って下記のようにしました。
    ちょっと長いですが、最終形態を下記に残しておきます。

    global $post;
    $private_blog= get_posts(array(
    	'post_type' => 'hogehoge',
    	'numberposts' => -1,
    	'post_status' => 'private',
    	'tax_query' => array( //非表示タクソノミーを付けたものは除外
    		'relation' => 'AND',
    		array(
    			'taxonomy' => 'blog_hide',
    			'field'    => 'slug',
    			'terms'    => 'hide_this_blog',
    			'operator' => 'NOT IN',
    		)
    	),
    ));
    
    if ( $private_blog ) {
    	foreach ( $private_blog as $post ):
    		setup_postdata($post);
    		$post_time = strtotime($post->post_date);
    		$edit_time = strtotime($post->post_modified);
    		$key_time  = strtotime( '-30 min', current_time( 'timestamp' ));
    		
    		if ( empty($edit_time) )
    			$edit_time = $post_time;
    		
    		if ( $edit_time <= $key_time ) {
    			// データベースにある投稿を公開に変更する
    			$my_post = array(
    				'ID'          => $post->ID,
    				'post_status' => 'publish'
    			);
    			wp_update_post( $my_post );
    		}
    	endforeach;
    	wp_reset_postdata();
    }

    ちなみにwp_cronを使う方法というのは、こんな感じで方向的にはあってるんでしょうか?

    if ( (!wp_next_scheduled( 'blog_cron_event' )) && ($post->post_type == 'hogehoge') && ( $post->post_status == 'private' ) && ( 投稿or編集されたとき?? ) ) {
        wp_schedule_single_event( time() + 1800,'blog_cron_event'); //今から30分後
    }
    function blog_publish_cron() {
    	$my_blog_post = array('ID' => $post->ID, 'post_status' => 'publish');
    	wp_update_post( $my_blog_post );
    }
    add_action( 'blog_cron_event', 'blog_publish_cron' );
    • この返信は7 ヶ月前に  d.w.c さんが編集しました。
    • この返信は7 ヶ月前に  d.w.c さんが編集しました。
    Colorful-life.

    (@colorfullifeinfo)

    お疲れ様です

    ちなみにwp_cronを使う方法というのは、こんな感じで方向的にはあってるんでしょうか?

    方向的にはあっていると思うのですが、このままだと
    ・wp_cron実行時に目的の投稿IDを取得出来ない
    ・’blog_cron_event’アクションにフックしたスケジュールが1つしか登録出来ない
    様に見えますので、イベントに引数(投稿ID)を渡すようにするのはどうでしょう?

    https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/wp_schedule_single_event#Schedule_an_event_one_hour_from_now_with_arguments

    4.「投稿者」が記事を書いて投稿ボタンを押すと下書きから非公開に変更される。

    6.公開されている記事をacf_form()経由で編集すると再度非公開に変更される。

    のタイミングで wp_schedule_single_event を処理させるのだと思いますので、その時の投稿IDを渡す感じでしょうか

    if ( ! wp_next_scheduled( 'blog_cron_event', array( $post->ID ) )  && /* 省略・・・ */ ) {
    	wp_schedule_single_event( time() + 1800, 'blog_cron_event', array( $post->ID ) );
    	// 省略
    
    function blog_publish_cron( $post_id ) {
    	$my_blog_post = array('ID' => $post_id, // 省略

    あと、今のスケジュール登録の条件判定だと、一度スケジュールが登録された後は再スケジュール出来ない様に見えますので、「6.投稿編集」時には、「4.新規投稿」時に登録したスケジュールが未実行だった場合に、スケジュール解除( wp_unschedule_event )してからスケジュールを再登録する、などの処理が必要になるでしょうかね?

    LABE

    (@latobeam)

    wp_cron を設定するには

    1. アクションをスケジュールに登録する。
    2. 処理を関数として用意する。
    3. アクションに関数をフックする。

    の3つのステップが必要です。
    @dwc-1 さんの(一番下の)コードでいうと1〜3行目が(1.)に、4〜7行目が(2.)に、8行目が(3.)にそれぞれ該当します。
    (1.)は「投稿or編集されたとき」に必ず呼び出されるようにします。
    publish_post とか save_post とかのアクションにフックですかね。
    @colorfullifeinfo さんが大方書いてくださったので、それに補足するなら例えば……

    add_action( 'save_post', 'set_cron_schedule', 10, 3 );
    function set_cron_schedule( $post_ID, $post, $update ) {
        $args = array( $post->ID ); // 渡す引数
        if ( ! wp_next_scheduled( 'blog_cron_event', $args ) && $post->post_type === 'hogehoge' && $post->post_status === 'private' )
            wp_schedule_single_event( time() + 1800, 'blog_cron_event', $args );
    }

    (2.)(3.)はそのまま functions.php に記述します。
    (2.)は @colorfullifeinfo さんのコードの後半です。

15件の返信を表示中 - 1 - 15件目 (全18件中)
  • このトピックに返信するにはログインが必要です。