bonsai's blog

ITベンチャー所属。Webエンジニア兼マーケッターです。python習得しようと頑張ってるごく一般的なサラリーマン。※ブログ初心者の為、至らぬ所は..m(_ _)m

Wordpressのカスタム投稿タイプでカテゴリ別に出力する方法

Wordpress初心者の備忘録です。
よくある質問のようなカスタム投稿タイプでカテゴリ別に出力したい時に行った実装です。

環境

完成イメージ

↓こんな感じの画面を実装します。

完成イメージ図

ソース

▼functions.php

<?php
//カスタム投稿を追加
function add_custom_post(){
    //よくある質問
    register_post_type( 'question',
        array(
            'labels' => array(
                'name'          => 'よくある質問',
                'singular_name' => 'よくある質問',
            ),
            'public' => false,
            'show_ui' => true,
            'menu_position' => 6,
            'has_archive' => true,
            'hierarchical' => false,
            'supports' => array(
                'title',
                'editor',
            ),
        )
    );
    register_taxonomy_for_object_type('category', 'article');
    register_taxonomy_for_object_type('post_tag', 'article');
}
add_action('init', 'add_custom_post', 1);

//カスタムタクソノミーを追加
function add_taxonomy() {
    //よくある質問カテゴリ
    register_taxonomy(
        'faq-cat',
        'question',
        array(
            'label' => 'よくある質問カテゴリ',
            'singular_label' => 'よくある質問カテゴリ',
            'labels' => array(
                'all_items' => 'よくある質問カテゴリ一覧',
                'add_new_item' => 'よくある質問カテゴリを追加'
            ),
        'public' => true,
        'show_ui' => true,
        'show_in_nav_menus' => true,
        'hierarchical' => true
        )
    );
}
add_action( 'init', 'add_taxonomy' );
?>

固定ページで、よくある質問を作ったのでpage.phpを変更します。
該当箇所を抜粋したのが以下のソースです。

▼page.php

<?php 
$taxonomies = 'faq-cat';
$args = array(
    'taxonomy'    => $taxonomies,
    'hide_empty' => true, // 空のカテゴリを出力しない場合はtrue

);
$terms = get_terms($args);

// カテゴリー別に記事を出力する
foreach($terms as $term):
    $args = array(
        'post_type'          => 'question',
        'taxonomy'          => $taxonomies,
        'term'                   => $term->slug,
        'posts_per_page'=> -1
    );
    $faq_posts = get_posts($args);
    echo '<section>';
    echo '<h2>'.$term->name.'</h2>';
    foreach($faq_posts as $faq_post):
        setup_postdata( $faq_post ); ?>
    <div>
        <a>
            <dl>
                <dt>Q.</dt>
                <dd><?php echo $faq_post->post_title; ?></dd>
            </dl>
        </a>
        <ul>
            <li>
                <dl>
                    <dt>A.</dt>
                    <dd>
                        <?php the_content(); ?>
                    </dd>
                </dl>
            </li>
        </ul>
    </div>
<?php
    endforeach;
    echo '</section>';
endforeach;
wp_reset_postdata();
?>

ソースを少し解説します。

get_terms($args)

これでカテゴリの一覧を取得します。
$taxonomies = 'faq-cat';で該当のカスタムタクソノミー (カテゴリー)を指定しています。

foreach($terms as $term):
    echo '<h2>'.$term->name.'</h2>';
endforeach;

でループし、カテゴリーの名前を出力しています。

get_posts($args)

これで投稿記事の一覧を取得します。

 $args = array(
        'post_type'     => 'question',//カスタム投稿タイプ名(スラッグ)指定
        'taxonomy'      => $taxonomies,//タクソノミー名指定
        'term'          => $term->slug,//カテゴリのスラッグを指定
        'posts_per_page'=> -1
    );

この条件で、該当カテゴリの投稿記事の一覧を取得します。
以上です。

文法間違い等あれば、ご指摘をお願いします。
構築の流れをだいぶ分かってきたけど、記事の出力の仕方が複数パターンあるので違いを覚えて行きたいな。

Wordpressのカスタム投稿タイプの詳細(個別)ページを作らない方法

Wordpress初心者の備忘録です。 カスタム投稿タイプで詳細(個別)ページが不要な時(よくある質問とか)の時の対処法です。

<?php
//カスタム投稿を追加
function add_custom_post(){
    //よくある質問
    register_post_type( 'question',
        array(
            'labels' => array(
                'name'          => 'よくある質問',
                'singular_name' => 'よくある質問',
            ),
            'public' => false,
            'show_ui' => true,
            'menu_position' => 6,
            'has_archive' => true,
            'hierarchical' => false,
            'supports' => array(
                'title',
                'editor',
                // 'excerpt',
                // 'thumbnail',
                // 'custom-fields',
            ),
        )
    );
}
?>

大事なのは、
'public' => false
'show_ui' => true
です。

vagrant upしようとしたら、VBOX_E_INVALID_OBJECT_STATE (0x80bb0007)のエラーが出た時の対処法

備忘録です。 vagrant upをしようとしたら、以下のエラーがでました。

There was an error while executing `VBoxManage`, a CLI used by Vagrant
for controlling VirtualBox. The command and stderr is shown below.

Command: ["modifyvm", "cab8ae41-fe92-4cf9-b5f4-1a8bae9da524", "--natpf1", "delete", "ssh"]

Stderr: VBoxManage: error: The machine 'vccw.test' is already locked for a session (or being unlocked)
VBoxManage: error: Details: code VBOX_E_INVALID_OBJECT_STATE (0x80bb0007), component MachineWrap, interface IMachine, callee nsISupports
VBoxManage: error: Context: "LockMachine(a->session, LockType_Write)" at line 531 of file VBoxManageModifyVM.cpp

ロックされてる的な事を言っているので、以下のコマンドを入力したら解消されました。

VBoxManage startvm  vccw.test --type emergencystop

vccw.testは仮想マシーンの名前です。UUIDでも大丈夫とのことです。

最近vagrantが不安定なので、一回再構築しようかな。

WebGLは何かをまとめてみた

WebGLとはなんぞ?

WebGLとはGPU(Graphics Processing Unit)へ直接アクセスすることができるブラウザに組み込まれたAPIのこと。
つまり、ブラウザで、3次元CGを表示させるための標準仕様のこと。

WebGLOpenGLというネイティブ(アプリ)で動作するグラフィックスAPIをブラウザから呼び出すためのパイプ役のような存在

f:id:bonsaimasa:20190208091711p:plain
javascriptからOpenGLへ直接アクセスすることはできないがWebGLが間に入ることでOpenGLを間接的に呼び出すことができる

簡潔に表現するなら、WebGLOpenGLを介して直接GPUを利用できるため非常に高速

以下参照させてもらいました。
第1回目 WebGLの概念 - Qiita

WebGLはどんな感じか見てみる

実際に見てみるのが一番実感できます。
良いなと思う参考URLまとめてみました。

3Gアニメーション

alteredqualia.com

まずは3Gアニメーションです。
とても綺麗ですね。HTML,CSS,JavaScriptだけで表現されているとは思えないほど綺麗でダイナミックです。

カメラとの連動

airtightinteractive.com

カメラと連動させるとこんな事もできるらしいです。

3Gゲーム

cwar.de

3Gが表現出来るとの事でこんなゲームも実装可能です。

WebGLのメリット

  • FlashPlayerなどのプラグインが不要
  • 爆速なため、スマホでも動く(ネイティブに負けない)。これはハードがすごい!という要素もあるかと思いますが

WebGLのデメリット

  • レガシーなブラウザは対応していない

まとめ

WebGLをつかえば、ハイブリットアプリの未来ももっと開けそう。
通常のWebサイトでもメインビジュアルに用いるだけでサイトをリッチに出来ますね。
実際の実装が難しそうなので、時間がある時にコーディングしてみようと思います。

PHPMailerでのメールフォーム実装外部メールサーバーでの対応も可

外部メールサーバー使ってのメールフォーム実装

Office 365のSMTP経由での実装が必要となりmb_send_mail関数が使えなかった為、PHPMailerを使用し実装しました。

composerを使わずにPHPMailerを設置

composerの環境がなく、使わずにセットアップする人のために
※公式ではcomposerでのインストールが推奨されています

  1. 公式よりzipファイルをダウンロード
    公式サイトはこちら

  2. ファイルを設置 zipファイルを解凍し、以下のファイルを設置する。
    src/. ※ディレクトリ配下のファイル一式
    language/phpmailer.lang-ja.php

  3. 設置箇所 僕の場合は以下の様に設置しました。

f:id:bonsaimasa:20190326215319p:plain

ディレクトリ図
━index.php

┗━ PHPMaile ━ language ━ phpmailer.lang-ja.php
       ┗ src ━┓━━ PHPMailer.php
           ┣━━ SMTP.php
           ┣━━ POP3.php
           ┣━━ OAuth.php
           ┣━━ Exception.php

記述ソース

問合せフォームで入力すると/php/index.phpに遷移し、問合せ者と管理者にそれぞれ1通ずつメールを送信する機能。

html(一部抜粋)

<form class="form" method="POST" action="/php/index.php">
    <div class="formRow">
        <label class="formRow__label" for="name">お名前</label>
        <input class="formRow__inputTxt" type="text" id="name" name="user_name" value="" />
    </div>
    <div class="formRow">
        <label class="formRow__label" for="name_kana">フリガナ</label>
        <input class="formRow__inputTxt" type="text" id="name_kana" name="user_name_kana" value="" />
    </div>
    <div class="formRow">
        <label class="formRow__label" for="company">会社名</label>
        <input class="formRow__inputTxt" type="text" id="company" name="user_company" value="" />
    </div>
    <div class="formRow">
        <label class="formRow__label" for="zip">郵便番号</label>
        <input class="formRow__inputTxt" type="text" id="zip" name="user_zip" value="" />
    </div>
    <div class="formRow">
        <label class="formRow__label" for="address">住所</label>
        <input class="formRow__inputTxt" type="text" id="address" name="user_address" value="" />
    </div>
    <div class="formRow">
        <label class="formRow__label" for="tel">電話番号</label>
        <input class="formRow__inputTxt" type="tel" id="tel" name="user_tel" value="" />
    </div>
    <div class="formRow">
        <label class="formRow__label" for="mail">メールアドレス</label>
        <input class="formRow__inputTxt" type="mail" id="mail" name="user_mail" value="" placeholder="mail@example.com" />
    </div>
    <div class="formRow">
        <label class="formRow__label" for="content">お問い合わせ内容</label>
        <textarea class="formRow__inputArea" id="content" name="user_content" value=""></textarea>
    </div>
    <div class="formSubmitRow">
        <button class="formSubmitRow__submit" type="submit">送信する</button>
    </div>
</form>

php

<?php
mb_language("ja");
mb_internal_encoding("UTF-8");

//PHPMailerの読み込み
//自分の置いたファイルのディレクトリ先を指定
require 'PHPMailer/src/PHPMailer.php';
require 'PHPMailer/src/SMTP.php';
require 'PHPMailer/src/POP3.php';
require 'PHPMailer/src/Exception.php';
require 'PHPMailer/src/OAuth.php';
require 'PHPMailer/language/phpmailer.lang-ja.php';

//公式サイトにならってそのまま記述
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

/*=======認証情報=======*/
$host = "smtp.office365.com";
$smtp_user = "dummy@bonsai.com";
$smtp_password = "xxxxxxx";
$from_address = "dummy@bonsai.com";
$from_name = "株式会社ホゲ";

/*=======POST情報=======*/
$name = $_POST["user_name"];//氏名
$name_kana = $_POST["user_name_kana"];//フリガナ
$company = $_POST["user_company"];//会社名
$zip = $_POST["user_zip"];//郵便番号
$address = $_POST["user_address"];//住所
$tel = $_POST["user_tel"];//電話番号
$mail = $_POST["user_mail"];//メールアドレス
$content = $_POST["user_content"];//問い合わせ内容
$url = "/form_finish.html";

/*=======問い合わせ側:メール情報=======*/
$customer_to = $mail;//送信先メール
$customer_title = "お問い合わせありがとうございます。";//件名
$customer_content = "";//本文
$customer_content .= $name . "\r\n";
$customer_content .= "\r\n";
$customer_content .= "この度は、お問い合わせいただき誠にありがとうございます。\r\n";
$customer_content .= "\r\n";
$customer_content .= "お送りいただきました内容をご確認の上、担当者より折り返しご連絡させていただきますので、お待ちください。\r\n";

$customer_content .= "以下の内容が送信されました。\r\n";
$customer_content .= "-------------------------------------------------------------------------------------------------------- \r\n";
$customer_content .= "お名前 : " . $name . " \r\n";
$customer_content .= "フリガナ : " . $name_kana . " \r\n";
$customer_content .= "メール  : " . $customer_to . " \r\n";
$customer_content .= "会社名 : " . $company . " \r\n";
$customer_content .= "郵便番号 : " . $zip . " \r\n";
$customer_content .= "住所 : " . $address . " \r\n";
$customer_content .= "電話番号 : " . $tel . " \r\n";
$customer_content .= "メールアドレス : " . $mail . " \r\n";
$customer_content .= "お問い合わせ内容 : \r\n";
$customer_content .= $content . " \r\n";
$customer_content .= "-------------------------------------------------------------------------------------------------------- \r\n";
$customer_content .= "\r\n";

/*=======問い合わせ側:メール送信準備=======*/
$customer_mail = new PHPMailer();
$customer_mail->Debugoutput = function($str, $level) { syslog(LOG_ERR, "PHP Mailer:" . $str); };
$customer_mail->IsSMTP();
$customer_mail->SMTPAuth = true;
$customer_mail->SMTPDebug = 2;
$customer_mail->CharSet = "utf-8";
$customer_mail->SMTPSecure = "tls";
$customer_mail->Host = $host;
$customer_mail->Port = 587;
$customer_mail->IsHTML(false);
$customer_mail->Username = $smtp_user;
$customer_mail->Password = $smtp_password; 
$customer_mail->SetFrom($smtp_user);
$customer_mail->From = $from_address;
$customer_mail->FromName = $from_name;
$customer_mail->Subject = $customer_title;
$customer_mail->Body = $customer_content;
$customer_mail->AddAddress($customer_to);

/*=======管理者側:メール情報=======*/
$admin_to = "dummy@bonsai.com";//送信先メール
$admin_title = "【要確認】お問い合わせがありました";//件名
$admin_content = "";//本文
$admin_content .= "ご担当者様 \r\n";
$admin_content .= "\r\n";
$admin_content .= "問い合わせがありました。 \r\n";
$admin_content .= " \r\n";
$admin_content .= "以下の内容が送信されました。 \r\n";
$admin_content .= "-------------------------------------------------------------------------------------------------------- \r\n";
$admin_content .= "お名前 : " . $name . " \r\n";
$admin_content .= "フリガナ : " . $name_kana . " \r\n";
$admin_content .= "メール  : " . $admin_to . " \r\n";
$admin_content .= "会社名 : " . $company . " \r\n";
$admin_content .= "郵便番号 : " . $zip . " \r\n";
$admin_content .= "住所 : " . $address . " \r\n";
$admin_content .= "電話番号 : " . $tel . " \r\n";
$admin_content .= "メールアドレス : " . $mail . " \r\n";
$admin_content .= "お問い合わせ内容 : \r\n";
$admin_content .= $content . " \r\n";
$admin_content .= "-------------------------------------------------------------------------------------------------------- \r\n";
$admin_content .= " \r\n";
$admin_content .= "ご確認の程、宜しくお願いいたします。 \r\n";

/*=======管理者側:メール送信準備=======*/
$admin_mail = new PHPMailer();
$admin_mail->Debugoutput = function($str, $level) { syslog(LOG_ERR, "PHP Mailer:" . $str); };
$admin_mail->IsSMTP();
$admin_mail->SMTPAuth = true;
$admin_mail->SMTPDebug = 2;
$admin_mail->CharSet = "utf-8";
$admin_mail->SMTPSecure = "tls";
$admin_mail->Host = $host;
$admin_mail->Port = 587;
$admin_mail->IsHTML(false);
$admin_mail->Username = $smtp_user;
$admin_mail->Password = $smtp_password; 
$admin_mail->SetFrom($smtp_user);
$admin_mail->From = $from_address;
$admin_mail->FromName = $from_name;
$admin_mail->Subject = $admin_title;
$admin_mail->Body = $admin_content;
$admin_mail->AddAddress($admin_to);

/*=======メール送信実行=======*/
if($admin_mail->Send() && $customer_mail->Send()){
header('Location:' . $url);
exit;
}else{
    echo "customer Error : " . $customer_mail->ErrorInfo . "\r\nadmin Error : " . $admin_mail->ErrorInfo;
}
?>

POINT

  • point1
    $admin_mail->Debugoutput = function($str, $level) { syslog(LOG_ERR, "PHP Mailer:" . $str); };
    この記述がないと、ログが画面に表示されてしまいます。
    ユーザーに見えてしまうのとheader('Location:' . $url);が正常に動作しなくなってしまいます。

まとめ

普段PHPを使っていないのもあり、記述をあまり理解出来せずにやってる箇所もあるので今度しっかり理解しようと思います。
ともかくPHPMailerはすごい便利なようなのでとても汎用性が高そうです。
ソースがおかしいやもっとこうした方がいいなどあればご指摘お願いします。

slickでレスポンシブ対応のカルーセルを設置してみた

概要

レスポンシブ対応のカルーセルを作りたいならおすすめはjQueryプラグインのslickです。
デザインと機能のカスタマイズ性も高いのでとても使いやすいです。
その設置方法から簡単なカスタマイズまで記載します。

設置作業

まずは以下のサイト(slick公式)よりダウンロード
※2019/02/08時点での最新版は1.8.1
slick - the last carousel you'll ever need

最低限の機能を使いたいのであれば
slick.min.js slick.css slick-theme.cssのみ設置で大丈夫です。

僕の場合はCSSファイルはheadタグ内に、JSファイルはbodyタグの下に設置しました。
jQueryの読み込みも忘れずに  

headの中

<link rel="stylesheet" type="text/css" href="css/plugin/slick.css">
<link rel="stylesheet" type="text/css" href="css/plugin/slick-theme.css">

bodyの中

<script src="js/plugin/jquery-2.1.4.min.js"></script>
<script src="js/plugin/slick.min.js"></script>

slickの基本な使い方

HTML

<div class="slick-wrp">
  <div class="slick-box">
    <div class="slick-imgWrp">
      <img class="slick-img" src="/img/img_slide_01_dummy.png" alt="" />
    </div>
    <h3 class="slick-ttl">カルーセル見出し01</h3>
    <p class="slick-txt">カルーセルテキスト01カルーセルテキスト01カルーセルテキスト01</p>
  </div>
  <div class="slick-box">
    <div class="slick-imgWrp">
      <img class="slick-img" src="/img/img_slide_01_dummy.png" alt="" />
    </div>
    <h3 class="slick-ttl">カルーセル見出し02</h3>
    <p class="slick-txt">カルーセルテキスト02カルーセルテキスト02カルーセルテキスト02</p>
  </div>
</div>

今回は画像の下にテキストを入れ込むパターンで組んでみました。 .slick-box一つ一つがスライドになります。

JavaScript

$(function(){
    $('.slick-wrp').slick(); 
});

これだけで基本スライドは実装できます。
必ずjqueryslick.min.jsを読み込んだ後に記述しましょう。

Result

f:id:bonsaimasa:20190208162331p:plain
しっかり表示されましたね。 prev と next のアイコンはdefaultで表示されるのですが白なので背景が白だと一瞬おや?と思うかもしれません。
背景色を代えてやるとしっかり表示されるのが確認できます。
f:id:bonsaimasa:20190208162742p:plain

slickのオプションを使ってみる

slickは色々なオプションがついています。

オートプレイ

javaScript

$('.slick-wrp').slick({
    autoplay:true, //オートプレイ機能ON
    autoplaySpeed:5000  //オートプレイ時のスピードを指定
}); 

インジケーターを表示

$('.slick-wrp').slick({
    dots:true  //インジケーターを表示
}); 

prev、nextボタンの非表示

$('.slick-wrp').slick({
    arrows: false //prev、nextボタンの非表示
}); 

dotsのカスタマイズ

インジケーターもdefaultのものも簡単にカスタマイズ出来るようにオプションが用意されています。 dotsで指定するclassを指定のものに変更できるというものです。

$('.slick-wrp').slick({
         dots: true, //dotsを有効化
    dotsClass: 'slide-dots'    //dotsのクラスを任意のものに変更
}); 

そこで基本のdotsのコードを見ていきましょう。
dotsオプションを指定すると以下のようなコードが自動生成されます。

<ul class="slick-dots" style="display: block;" role="tablist">
    <li class="" role="presentation">
        <button type="button" role="tab" id="slick-slide-control00" aria-controls="slick-slide00" aria-label="1 of 2" tabindex="-1">1</button>
    </li>
    <li role="presentation" class="slick-active">
        <button type="button" role="tab" id="slick-slide-control01" aria-controls="slick-slide01" aria-label="2 of 2" tabindex="0" aria-selected="true">2</button>
    </li>
</ul>

この.slick-dots.slide-dotsになったと考えてCSSを当てていきましょう

.slide-dots{
    text-align: center;
}

.slide-dots li{
    display: inline-block;
    width: 30px;
    margin: 0 15px;
}

.slide-dots li button{
    position: relative;
    width: 100%;
    text-indent: -9999px;
    border: none;
}

.slide-dots li button:before{
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: 20px;
    height: 20px;
    margin: auto;
    content: '';
    text-indent: 0;
    background-image: url(/img/icon_carousel_on.svg);
    background-repeat: no-repeat;
}

今後、オプションについてもっとまとめていきますがひとまずここまでとします。

190226追加|レスポンシブで文字が大きくなる

slickを使ってテキストありのスライダーを表示すると、スマホを縦向き (Portrate mode) と横向き (Landscape mode)にしたりしてると、文字が大きくなってしまう現象が起きました。

対応方法

以下のCSSを当ててあげるだけで回避できました。

css

body {
    -webkit-text-size-adjust: 100%;
}

原因

原因ですが、スライダーのように要素が横に並んで幅が広くなってしまう場合、-webkit-text-size-adjust: 100%;を設定していないとiOSが自動でフォントサイズを調整してしまうようでした。

スマホでも使えるformでのカレンダー表示(pickadate.js)

初めに

form実装でdateを使うもスマホでカレンダーは表示されないので困った時に使えるjQueryプラグインのpickadate.js。
導入から実装手順を備忘録。

導入手順

ダウンロード

以下のURLよりzipをダウンロード。※2019.1.31時点で最新バージョン(v.3.5.6)
https://amsul.ca/pickadate.js/

設置

zipファイル解答後、libディレクトリ直下のjsファイル全てとlib/themesディレクトリ直下のCSSを格納
※日付だけの場合はpicker.time.jsは読み込まなくてもいい

僕の場合はjsファイルはjs/plugin直下に
     cssファイルはcss/plugin直下に

js、cssファイルの読み込み

<!-- pickadate用スタイル -->
    <link rel="stylesheet" type="text/css" href="css/plugin/default.css" id="theme_base">
    <link rel="stylesheet" type="text/css" href="css/plugin/default.date.css" id="theme_date">
<!-- jQuery -->
    <script src="js/plugin/jquery-2.1.4.min.js"></script>
<!-- pickadate本体 -->
    <script src="js/plugin/picker.js"></script>
    <script src="js/plugin/picker.date.js"></script>
    <script src="js/plugin/picker.time.js"></script>
<!-- レガシーブラウザへの対応用ファイル -->
    <script src="js/plugin/legacy.js"></script>

実装

htmlの記述

textのinputタグを設置する

<input class="formRow_date" type="text" name="user_hope_date1" id="hope_date1" />

jsの記述

pickadateに対応させたいidを指定する

   $('#hope_date1').pickadate({
        format: 'yyyy/mm/dd'
    });

複数対応させたければ、class指定が良い

   $('.formRow_date').pickadate({
        format: 'yyyy/mm/dd'
    });

PC版とSP版両方で表示された。

PC
PC版カレンダー

SP
SP版カレンダー