Jao's Blog

プログラミング備忘録

日記のプレビュー表記を作成する #12

 プレビュー表記とは、カレンダーページから遷移できるページです。

f:id:Kyokuya_jao:20200608143101p:plain
 少し前に撮ったスクショなので、立ち絵が僕のままですが、サンプルとしてはこの画像のような感じです。
 その月に書いた日記一覧をプレビューできるページ、ということです。
 ちなみに今回は、めちゃめちゃ苦戦しました。

日記を取得する

 まず、データベースから日記を取得します。いつものように、URLから$_GET['date']となっている日付のパラメータを取得します。
 パラメータは、例えば「2020-06」「1994-12」のように「年-月」という構成になっています。日記のテーブルの、dateカラムが、この年月から始まっているものを検索し、引っ張り出します。where句のlike文ですね。

しかし、うまくいかず……

 $sql = "select good_1, good_2, good_3 from myDiary where userId = :id and date like ':date%'";
    $stmt = $this->db->prepare($sql);
    $stmt->bindvalue(':date',$date,\PDO::PARAM_STR);

 このように、%のワイルドカードとbindvalue関数を使って検索をかけますが、エラーが出てしまいます。調べれば、原因はすぐにわかりました。

 $sql = "select good_1, good_2, good_3 from myDiary where userId = :id and date like :date ";
    $stmt = $this->db->prepare($sql);
    $stmt->bindvalue(':date',$date . '%',\PDO::PARAM_STR);

 $sqlの文字列の中ではなく、bindvalueの引数の中で%をつけることで、うまくいきました。勉強になった!

空のデータを抹消する

 無事に該当する月の日記を取り出すことに成功しました。しかし、前回にも少し触れましたが、データの中には1文字も書かれていない日記も登録されています。
 取り出したデータを愚直に全て表示してしまうと、空の日記まで表示されることとなってしまいます。

悪戦苦闘

 日記のデータは多次元配列となっているため、配列の消去にかなり苦労しました。

// 多次元配列の子要素が空の場合削除する(親要素は残る)
    foreach($data as $value){
      $res[] = array_filter($value, 'strlen');
    }
    // 子要素が消え去った親要素を消し去る
    foreach($res as $key => $value){
      if(empty($value) ){
        unset($res[$key]);
      }
    }

 まず、多次元配列の子要素が空だった場合、それらを全てarray_filterで抹消します。
 そうすると、中身ががらんどうになった親要素が残ります。
 それらをempty()による条件分岐とunset()によって、完全デリートするのです。

ここでミスに気が付く

 完璧だと思われた作戦は、大敗を喫しました。日記の中身は空でも、その空の日記の日付のデータは残っているという点を失念していたのです。
 つまり、子要素が完全に消え去ることはありません。
 これに対抗すべく、子要素の数が一個の場合、親要素を削除するという処理を攻撃表示で召喚します。子要素が一個ということは、そこに日付のデータしかない=日記が空っぽということだからです。

 この処理の実装にも、悩まされました。
 for($i = 0; $i < count($data) ; $i++)というfor文を回し、該当した要素を削除していたのですが、条件を満たしていても削除される要素とされない要素があるのです。
 原因は、for文を回す回数をcount関数で決定していたことにありました。要素を削除する処理をしているので、要素の数が途中で変わるということをすっかり忘れていたのです。この部分を、あらかじめ設定した定数にすることで解決しました。

取り出したデータを表示する

 苦労して取り出したこれらのデータを、htmlに出力します。  sprintf関数を使い、html文にデータを組み込みます。

f:id:Kyokuya_jao:20200618212252p:plain
 うまくいきました!最新の日記ほど上に表示されるようになっており、スクロールで下の日記を見ることができます。

うまくいったと思ったのも束の間

 さて、ちょっと左の矢印を押して、5月の日記を表示してみましょう。

f:id:Kyokuya_jao:20200618212527p:plain
 心を抉るエラー文です。
 その月の日記を一つも書いていない場合は、データが存在しないので、データベースに検索をかけたときにエラーが出ることが原因です。
 データを一つも取り出せなかった時は、空文字を返すことで万事解決……な、筈はありませんでした。
 これまでに様々な処理を行ってきましたが、それらに渡るデータが空文字なら、エラーが起きてしまいます。故にデータベースから取り出したモノが空文字なら、そもそもそれらの処理を行わず、空文字を返すという処理を行いましょう。

    if(!empty($tempDiaryData)){
      // 空の日記を消す処理
      $tempDiaryList = $this->_eraseEmptyDiary($tempDiaryData);
      // 番号を降り直す
      $diaryList = array_values($tempDiaryList);
      return $diaryList;
    }else{
      return '';
    }

 html部分に表示させるものが空文字であっても、エラーが起きてしまいます。故にここでも、データが空文字なら、そもそも処理を行わないようにしました。emtpy関数サマサマです。

ダメ押しのエラー

 

f:id:Kyokuya_jao:20200618212823p:plain
 三つのいいこと記入欄を全て埋めていないとエラーになることが判明しました。単純に、日記の書かれていない空のデータがエラーを引き起こしているようですね。
 日記を表示させる関数にて、データを直接表示させていましたが、ワンクッション処理を挟みましょう。変数を用意し、データが空なら空文字を、そうでなければデータの文字列を代入します。
f:id:Kyokuya_jao:20200618213255p:plain
 うまくいきました!

表示させる文字数を限定する

 ここまでくれば、あと一歩です。今のままでは、以下の画像のように、日記の文字数が長いとレイアウトが崩れてしまいます。

f:id:Kyokuya_jao:20200622093921p:plain
 mb_strlen関数で文字数を測り、一定以上なら、mb_substr関数でその先を切り捨てるという処理を放ちます。その後、文字列に「……」を連結し、文章が続くことを視覚的に示唆しましょう。  
f:id:Kyokuya_jao:20200622094622p:plain

 完成です!慣れない処理やエラーの連続で、とても大変でした。しかし、それだけにやりがいのある部分だったと思います。

気がついたこと

 面倒な処理の大半が空っぽの日記が含まれることに起因するものでした。
 ということは、つまり……最初から空っぽの日記はデータベースに登録しないという風にすれば、全てが解決したのではないでしょうか……?
 完全に盲点でした。次回までに修正します。