【PHP】日付の配列で同日は結合してHTMLに出力する

APIをPHPで取得して、HTMLで出力する、というお仕事がありました。

APIで取得できるデータは「日付・タイトル・リンク情報」です。ニュース情報なので同じ日付も入ってきます。出力するHTMLデータはこちら。APIは取得済みですでに配列化されているものとします。

$data = array();
$data[] = array( 'date'=>'2018-8-12', 'title'=>'サンプルタイトル1', 'link'=>'http://momomo.co.jp/' );
$data[] = array( 'date'=>'2018-8-13', 'title'=>'サンプルタイトル2', 'link'=>'http://momomo.co.jp/' );
$data[] = array( 'date'=>'2018-8-14', 'title'=>'サンプルタイトル3', 'link'=>'http://momomo.co.jp/' );
$data[] = array( 'date'=>'2018-8-14', 'title'=>'サンプルタイトル4', 'link'=>'http://momomo.co.jp/' );
$data[] = array( 'date'=>'2018-8-15', 'title'=>'サンプルタイトル5', 'link'=>'http://momomo.co.jp/' );
<ul>
    <li>
        <dl>
            <dt>
                <time>8/13</time>
            </dt>
            <dd>
                <a href="〜">リンク先</a>
            </dd>
        </dl>
    </li>
    <li>
        <dl>
            <dt>
                <time>8/14</time>
            </dt>
            <dd>
                <a href="〜">リンク先</a>
            </dd>
            <dd>
                <a href="〜">リンク先</a>
            </dd>
        </dl>
    </li>
    <li>
        <dl>
            <dt>
                <time>8/15</time>
            </dt>
            <dd>
                <a href="〜">リンク先</a>
            </dd>
        </dl>
    </li>
     ....
</ul>

ネックは、データでは同一の日付も別データなのに、デザインだと同ブロックで表示されていることですね。最初こんな風に書いてみました。

<?php
$prev = new DateTime( ('2018-8-1') );
$html = array();
foreach ( $data as $row ) {
    // 対象の日付を取得
    $today = new DateTime( $row['date'] );

    echo '<li><dl>';
    // 前回の値と同一の日付かチェック。同一の日付でなかった場合
    if ( $prev < $today ) {
        echo '<dt><time>' . $today->format( 'n/j' ) . '</time></dt>';
        echo '<dd><a href="' . $row['link'] . '">リンク先</a></dd>';
    }
    // 同一の日付であった場合
    else {
        echo '<dd><a href="' . $row['link'] . '">リンク先</a></dd>';
    }
    echo '</dl></li>';

    // 次ループの為に確保
    $prev = $today;
}
?>

やっぱりうまく行きません笑
まあ、最初の1回でうまく行くなら誰も仕事で困らない!

まず文字列を扱いやすいように即出力ではなくて配列にしました。
次のループが同一の日付の場合は<li><dl>を閉じたくなくて、次のループが別の日の場合は<li><dl>を閉じたい。。でも、次のループの日付は、次のループに進まないと分からない。。

試行錯誤の末、こんな感じになりました。

<?php
$prev = new DateTime( ('2018-8-1') );
$html = array();
foreach ( $data as $row ) {

    $today = new DateTime( $row['date'] );

    // 前回の値と同一の日付かチェック。同一の日付でなかった場合
    if ( $prev < $today ) {
        $html[] = '</dl></li>'; // ←ポイント
        $html[] = '<li><dl>';
        $html[] = '<dt><time>' . $today->format( 'n/j' ) . '</time></dt>';
        $html[] = '<dd><a href="' . $row['link'] . '">リンク先</a></dd>';
    }
    // 同一の日付であった場合
    else {
        $html[] = '<dd><a href="' . $row['link'] . '">リンク先</a></dd>';
    }

    $prev = $today;
}
// 足りない分補足
$html[] = '</dl></li>';
// 余計な分削除
array_shift( $html );
// 出力
echo '<ul>' . implode( "\n", $html ) . '</ul>' . "\n";
?>

できました〜\(^o^)/
でも、絶妙に「足りない分補足」「余計な分削除」がイケてない。。
もうちょっとごにょごにょして、最終的にこの形になりました。

<?php
$prev = new DateTime( ('2018-8-1') );
$html = array();
foreach ( $data as $row ) {

    $today = new DateTime( $row['date'] );

    // 前回の値と同一の日付かチェック。同一の日付でなかった場合
    if ( $prev < $today ) {
        $html[] = '<li><dl>';
        $html[] = '<dt><time>' . $today->format( 'n/j' ) . '</time></dt>';
        $html[] = '<dd><a href="' . $row['link'] . '">リンク先</a></dd>';
        $html[] = '</dl></li>';
    }
    // 同一の日付であった場合
    else {
        array_pop( $html ); // ←ポイント①
        $html[] = '<dd><a href="' . $row['link'] . '">リンク先</a></dd>';
        $html[] = '</dl></li>'; // ←ポイント②
    }

    $prev = $today;
}
echo '<ul>' . implode( "\n", $html ) . '</ul>' . "\n";
?>

すっきり〜\(^o^)/
PHPとHTMLの結合は、ほんとうにパズルですね。