サポート » 使い方全般 » カスタムフィールドで画像の保存時に他の画像が使えない

  • 解決済 ryokitnk

    (@ryokitnk)


    現在、WordPressを用いてカフェのホームページを作っています。
    カスタムフィールドの機能が非常に便利なので使っています。

    今回、TOPページのスライドショーに用いる画像とその説明文をカスタムフィールドにて登録し、front-page.php内で呼び出すことで管理者が後からスライドショーの画像と説明文を変更できるようにしようと考えています。

    WordPressのTOPページの設定は、TOPページ用の固定ページを作成し、そのページのテンプレートとしてfront-page.phpを使用しています。
    また、その固定ページを設定用のページとして、TOPページに実際に表示するHTMLなどは直接front-page.php内に記述し、固定ページはカスタムフィールドを入力するためのページとして使用します。

    こちらの記事やカスタムフィールドの作成、入力、保存に関する各関数のCODEXを参考に以下のソースコードをテーマ内のfunction.phpに記載しましたが、保存時に意図しない挙動があり、その原因を突き止めたいです。

      // カスタムフィールド ボックスを追加
      function add_my_cf($post) {
        $post_id = $post->ID;
        if($post_id == get_option('page_on_front')) {
          add_meta_box( 'basic_info', '基本情報', 'insert_basic_info_cf', 'page', 'normal', 'high');
          add_meta_box('slideshow_img', 'スライドショーで使う画像', 'insert_slideshow_img_cf', 'page', 'normal', 'high');
        }
      }
      add_action('add_meta_boxes_page', 'add_my_cf', 10, 2);
    
      function custom_metabox_edit_form_tag(){
        echo ' enctype="multipart/form-data"';
      }
      //画像をアップする場合は、multipart/form-dataの設定が必要なので、post_edit_form_tagをフックしてformタグに追加
      add_action('post_edit_form_tag', 'custom_metabox_edit_form_tag');
      
      // カスタムフィールド:基本情報 の入力エリア
      function insert_basic_info_cf() {
        global $post;
        // nonceフィールドを追加して後でチェックする
      	wp_nonce_field( 'save_my_cf', 'basic_info_cf_nonce' );
    
        echo '電話番号: <input type="text" name="basic_info_phone" id="basic_info_phone" value="'.get_post_meta($post->ID, 'basic_info_phone', true).'" size="50" /><br>';
        echo 'メールアドレス: <input type="text" name="basic_info_email" id="basic_info_email" value="'.get_post_meta($post->ID, 'basic_info_email', true).'" size="50" /><br>';
        echo '郵便番号: <input type="text" name="basic_info_postalcode" id="basic_info_postalcode" value="'.get_post_meta($post->ID, 'basic_info_postalcode', true).'" size="50" /><br>';
        echo '住所: <input type="text" name="basic_info_address" id="basic_info_address" value="'.get_post_meta($post->ID, 'basic_info_address', true).'" size="50" /><br>';
        echo '定休日: <input type="text" name="basic_info_holiday" id="basic_info_holiday" value="'.get_post_meta($post->ID, 'basic_info_holiday', true).'" size="50" /><br>';
        echo '開店時間: <input type="text" name="basic_info_open" id="basic_info_open" value="'.get_post_meta($post->ID, 'basic_info_open', true).'" size="50" /><br>';
        echo '閉店時間: <input type="text" name="basic_info_close" id="basic_info_close" value="'.get_post_meta($post->ID, 'basic_info_close', true).'" size="50" /><br>';
        echo '最終入店時間: <input type="text" name="basic_info_last_entering" id="basic_info_last_entering" value="'.get_post_meta($post->ID, 'basic_info_last_entering', true).'" size="50" /><br>';
        
      }
    
      // カスタムフィールド:スライドショー画像の入力エリア
      function insert_slideshow_img_cf() {
        global $post;
    
        // nonceフィールドを追加して後でチェックする
      	wp_nonce_field( 'save_my_cf', 'slideshow_img_cf_nonce' );
    
        for($i = 1; $i <= 6; $i++) {
          echo '画像'.$i.'のファイル: <input type="file" name="slideshow_img'.$i.'_img" accept="image/*" /><br>';
          if(!empty(get_post_meta($post->ID, 'slideshow_img'.$i.'_img', true)) && strlen(get_post_meta($post->ID, 'slideshow_img'.$i.'_img', true)) > 0){
            //キーのpostmeta情報がある場合は、画像を表示
            echo '<img style="width: 200px;display: block;margin: 1em;" src="'.wp_get_attachment_url(get_post_meta($post->ID, 'slideshow_img'.$i.'_img', true)).'">';
            echo '<div>'.get_post_meta($post->ID, 'slideshow_img'.$i.'_img', true).'</div>';
          }
          echo '画像'.$i.'の説明文(最大100文字): <input type="text" name="slideshow_img'.$i.'_desc" id="slideshow_img'.$i.'_desc" value="'.get_post_meta($post->ID, 'slideshow_img'.$i.'_desc', true).'" size="100" /><br>';
        }
      }
      
      // カスタムフィールドの値を保存
      function save_my_cf( $post_id ) {
        // nonceがセットされているかどうか確認
        $cf_nonces = ['basic_info_cf_nonce','slideshow_img_cf_nonce'];
        foreach($cf_nonces as $cf_nonce) {
          if ( ! isset( $_POST[$cf_nonce] ) ) {
            return;
          }
          // nonceが正しいかどうか検証
          if ( ! wp_verify_nonce( $_POST[$cf_nonce], 'save_my_cf' ) ) {
            return;
          }
        }
    
        // 自動保存の場合はなにもしない
        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
          return;
        }
    
        // ユーザー権限の確認
        if ( isset( $_POST['post_type'] ) && 'page' == $_POST['post_type'] ) {
    
          if ( ! current_user_can( 'edit_page', $post_id ) ) {
            return;
          }
    
        } else {
    
          if ( ! current_user_can( 'edit_post', $post_id ) ) {
            return;
          }
        }
    
        /* 安全が確認できたのでデータを保存する */
    
        // basic_info
        $basic_info_cf_keys = ['basic_info_phone', 'basic_info_email', 'basic_info_postalcode', 'basic_info_address', 'basic_info_holiday', 'basic_info_open', 'basic_info_close', 'basic_info_last_entering'];
    
        foreach($basic_info_cf_keys as $basic_info_cf_key) {
          if(!empty($_POST[$basic_info_cf_key])) {
            update_post_meta($post_id, $basic_info_cf_key, sanitize_text_field($_POST[$basic_info_cf_key]));
          } else {
            delete_post_meta($post_id, $basic_info_cf_key, get_post_meta($post_id, $basic_info_cf_key, true));
          }
        }
    
        // slideshow_img
        // slideshow_img_img
        $slideshow_img_img_cf_keys = [];
        for($i = 1; $i <= 6; $i++) {
          $slideshow_img_img_cf_keys[] = 'slideshow_img'.$i.'_img'; 
        }
        foreach($slideshow_img_img_cf_keys as $slideshow_img_img_cf_key) {
    
          if(!empty($_FILES[$slideshow_img_img_cf_key]) && $_FILES["slideshow_img".$i."_img"]["size"] !== 0){
            $file_name = basename($_FILES[$slideshow_img_img_cf_key]['name']);
            $file_name = trim($file_name);
            $file_name = str_replace(" ", "-", $file_name);
      
            $wp_upload_dir = wp_upload_dir(); //現在のuploadディクレトリのパスとURLを入れた配列
            $upload_file = $_FILES[$slideshow_img_img_cf_key]['tmp_name'];
            $upload_path = $wp_upload_dir['path'].'/'.$file_name; //uploadsディレクトリ以下などに配置する場合は$wp_upload_dir['basedir']を利用する
            //画像ファイルをuploadディクレトリに移動させる
            move_uploaded_file($upload_file,$upload_path);
      
            $file_type = $_FILES[$slideshow_img_img_cf_key]['type'];
            //正規表現で拡張子なしのスラッグ名を生成
            $slug_name = preg_replace('/\.[^.]+$/', '', basename($upload_path));
      
            if(file_exists($upload_path)){
                //保存に成功してファイルが存在する場合は、wp_postsテーブルなどに情報を追加
                $attachment = array(
                    'guid'           => $wp_upload_dir['url'].'/'.basename($file_name), 
                    'post_mime_type' => $file_type, 
                    'post_title' => $slug_name, 
                    'post_content' => '', 
                    'post_status' => 'inherit'
                );
                //添付ファイルを追加
                $attach_id = wp_insert_attachment($attachment,$upload_path,$post_id);
                if(!function_exists('wp_generate_attachment_metadata')){
                    require_once(ABSPATH . "wp-admin" . '/includes/image.php');
                }
                //添付ファイルのメタデータを生成し、wp_postsテーブルに情報を保存
                $attach_data = wp_generate_attachment_metadata($attach_id,$upload_path);
                wp_update_attachment_metadata($attach_id,$attach_data);
                //wp_postmetaテーブルに画像のattachment_id(wp_postsテーブルのレコードのID)を保存
                update_post_meta($post_id, $slideshow_img_img_cf_key,$attach_id);
            }else{
                //保存失敗
                echo '画像保存に失敗しました';
                exit;
            }
          }
    
        }
    
        // slideshow_img_desc
        $slideshow_img_desc_cf_keys = [];
        for($j = 1; $j <= 6; $j++) {
          $slideshow_img_desc_cf_keys[] = 'slideshow_img'.$j.'_desc';
        }
        foreach($slideshow_img_desc_cf_keys as $slideshow_img_desc_cf_key) {
          if(!empty($_POST[$slideshow_img_desc_cf_key])) {
            update_post_meta($post_id, $slideshow_img_desc_cf_key, sanitize_text_field($_POST[$slideshow_img_desc_cf_key]));
          } else {
            delete_post_meta($post_id, $slideshow_img_desc_cf_key, get_post_meta($post_id, $slideshow_img_desc_cf_key, true));
          }
        }
      }
      add_action('save_post', 'save_my_cf');
    ?>

    このコード内で行っていることを自分なりにまとめますと、

    • 2つのカスタムフィールド:基本情報(key:’basic_info_cf’)と スライドショー画像(key:’slideshow_img’)を設定
    • 画像アップロードに必要なmultipart/form-dataの設定を追加
    • 基本情報(key:’basic_info_cf’)の入力エリアの設定
    • スライドショー画像(key:’slideshow_img’)の入力エリアを設定
    • カスタムフィールドの値を保存する設定

    を行っています。

    これによりTOPページに設定した固定ページの編集時に2つのカスタムフィールドが表示されました。
    1つ目の基本情報のカスタムフィールドに関しては問題なく保存され他のページで取得することができました。
    しかし、2つ目のスライドショー画像のカスタムフィールドに関しては、意図通り
    「画像1のファイル」
    「画像1の説明文」
    「画像2のファイル」
    「画像2の説明文」



    「画像6のファイル」
    「画像6の説明文」
    と表示されましたが、画像ファイルの保存時に意図していない挙動が起こります。
    例えば、画像1、画像2にファイルをアップロードし、記事の更新を行うと、画像1と画像2が保存されましたが、その後に画像3のみをアップロードし記事の更新を行うと、画像3以外の画像が取得できなくなります。

    このカスタムフィールドで登録した画像はサーバー内の\wordpress\wp-content\uploads\2021\05に保存されるのですが、先ほどの例のような登録を行うと、画像1、画像2、画像3のファイルはディレクトリ内に存在していますが、1度目にアップロードした画像1と画像2のみ、情報を取得できません。

    ※追記:WordPress上でメディアライブラリを確認したところ、「05」(おそらく現在のMonth)というファイルがたくさんアップロードされており、それが原因と考えられます。ただ、そのファイルがアップロードされる理由が分からないです。
    ※追記2:データベースのwp_postmetaを見ると、記事を更新するとmeta_keyが_wp_attached_file、meta_valueが2021/05/のデータが6つ登録され、その後にアップロードした画像の分のみmeta_keyが_wp_attached_file、meta_valueが画像名のデータが追加されています。
    これらの問題が起こるのは何が原因なのでしょうか?
    また、今回は画像1のファイルと画像1の説明文を別々のkeyで登録・取得していますが、各画像のファイルと説明文をもっと良い方法で登録、取得する方法はありますでしょうか?

    • このトピックは2年、 11ヶ月前にryokitnkが編集しました。理由: メディアライブラリを確認したことの追記を行いました。
    • このトピックは2年、 11ヶ月前にryokitnkが編集しました。理由: 追記2
4件の返信を表示中 - 1 - 4件目 (全4件中)
  • ryokitnkさん、こんにちは。

    検証してないので結論ではありませんが、軽くコードを見ていると画像に関しては登録時に成功した場合のみmeta情報の登録を行っていませんか。
    更新時にもmeta情報を同じ内容で更新しなければなかったことにされてしまうと思うのですが…

    ご参考になれば。

    トピック投稿者 ryokitnk

    (@ryokitnk)

    shokun0803さん、返信ありがとうございます。

    確かに、ファイルをアップロードしたものしかメタ情報を登録できていませんでした。

    解決方法としては、上記のコードの画像保存の関数内の
    if(isset($_FILES[$slideshow_img_img_cf_key]) && $_FILES["slideshow_img".$i."_img"]["size"] !== 0){}
    に以下を追加する感じですかね?

    else {
      // 既存のデータベースの値を取得し、再度保存する処理
      // wp_generate_attachment_metadata(), update_post_meta()等
    }

    またこの場合、$_FILESや画像をアップロードした場合の処理内で定義した$file_nameや$wp_upload_dirなどの変数は使えるのでしょうか?

    トピック投稿者 ryokitnk

    (@ryokitnk)

    meta情報の更新に関して、

    if(isset($_FILES[$slideshow_img_img_cf_key]) && $_FILES["slideshow_img".$i."_img"]["size"] !== 0){}
    という条件分岐により
    そもそもinputタグに画像をセットせずに更新した場合は処理を行わないようにしているつもりですが、セットしていない部分についてもwp_postmetaに値が登録され、そのmeta_valueが$wp_upload_dir[‘url’].’/’.basename($file_name)の$file_nameが”(空)の展開したものになっているため、この条件分岐が役目をはたしていないと考えました。

    調べるとinputタグ(type=”file”)のvalueの初期値は”(空)なので、

    $file_name = basename($_FILES[$slideshow_img_img_cf_key]['name']);
     $file_name = trim($file_name);
     $file_name = str_replace(" ", "-", $file_name);

    が空になっている=先ほどの条件分岐が機能していないのかなと考えています。

    inputタグにファイルをセットしたもののみ画像の保存やmeta情報の更新をしているつもりなのですが、この条件ではおかしいですか?

    ryokitnkさん、こんにちは。

    時間がなくて詳細を調べることができないので、参考程度の情報ですが、例えばSmart Custom Fieldsプラグインにはファイルアップロード機能と、フォームを繰り返し追加できる機能がありますが、繰り返しfieldの中でファイルアップロードを作っていても、ひとつずつファイルをアップロードすることができます。

    この場合、通常のプラグイン管理画面を作るように一つの更新ボタンではなく繰り返しで追加されたフィールドごとに更新が発生しているように見えます。
    ファイルのアップロードを複数ひとまとめで更新するように設定するよりは、ファイルのアップロードフィールドが更新された場合にその場所のみ更新するようにしてみると複雑な設定が不要になるのでは?とも思えます。

    ご参考になれば。

4件の返信を表示中 - 1 - 4件目 (全4件中)
  • トピック「カスタムフィールドで画像の保存時に他の画像が使えない」には新たに返信することはできません。