【独学】はじめてのWebプログラミング – 9.8 Webページ開発 2-⑧

ツイート一覧取得機能の作成

ツイートモデルの変更

tweets.phpを以下のように修正します。

htdocs/Twitter/Models/tweets.php

<?php

// 設定ファイルの読み込み
include_once '../config.php';

// ツイート登録関数
function createTweet(array $data)
{
    // データベースに接続
    $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);

    // 成功/失敗確認
    if ($mysqli->connect_errno) {
        echo 'MySQLの接続に失敗しました。<br>';
        exit;
    }

    // ツイート登録のSQLを作成
    $query = 'INSERT INTO tweets (user_id, body, image_name) VALUES (?, ?, ?)';

    // セキュリティ上、以下のように書く
    $statement = $mysqli->prepare($query);
    $statement->bind_param('iss', $data['user_id'], $data['body'], $data['image_name']);

    // SQLを実行
    $response = $statement->execute();

    // 成功/失敗確認
    if ($response == false) {
        echo 'エラーが発生しました。<br>';
    }

    // データベースへの接続解除
    $statement->close();
    $mysqli->close();

    // result変数には、true(成功)or false(失敗)が入っている
    return $response;
}

// ★変更箇所(ここから)
function getTweets($user)
{
    $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
    
    // 成功/失敗確認
    if ($mysqli->connect_errno) {
        echo 'MySQLの接続に失敗しました。<br>';
        exit;
    }

    // ログインしているユーザーのユーザーIDを取得
    $user_id = $user['id'];

    // ツイート一覧を取得するSQLを作成
    $query = <<<SQL
        SELECT
            tweets.id AS tweet_id,
            tweets.status AS tweet_status,
            tweets.body AS tweet_body,
            tweets.image_name AS tweet_image_name,
            tweets.created_at AS tweet_created_at,
            users.id AS user_id,
            users.name AS user_name,
            users.nickname AS user_nickname,
            users.image_name AS user_image_name,
            likes.id AS like_id,
            (SELECT COUNT(*) FROM likes WHERE status = 'active' AND tweet_id = tweets.id) AS like_count
        FROM
            tweets
            INNER JOIN users
            ON users.id = tweets.user_id AND users.status = 'active'
            
            LEFT JOIN
            likes
            ON likes.tweet_id = tweets.id AND likes.status = 'active' AND likes.user_id = '$user_id'
        WHERE
            tweets.status = 'active'
    SQL;

    // SQLを実行
    $result = $mysqli->query($query);

    // 成功/失敗確認
    if ($result == false) {
        echo 'エラーが発生しました。<br>';
        $mysqli->close();  // データベースへの接続解除
        return false;
    }

    // ツイート一覧を取得
    $tweets = $result->fetch_all(MYSQLI_ASSOC);

    $mysqli->close();

    return $tweets;
}
// ★変更箇所(ここまで)

?>

まずは「$query = <<<SQL … SQL;」の箇所から解説します。
一度に複数行のSQLを書きたい場合、「<<<SQL」と「SQL」で囲んだ部分にSQLを書くことができます。PHPはその部分をSQLの文字列と認識してくれます。
文字列をダブルクォーテーションで囲んだ場合と同様、その中に変数を含めれば、その値を抽出してくれます。ここでは、$user_idが含まれており、$user_idの値(1など)が取り出されて、「likes.user_id = ‘1’」のようになるわけです。

「<<<SQL」と「SQL」の中に書かれたSQLを見ていきます。
「SELECT * FROM テーブル名」は以前解説しました。
今回は「SELECT ~ FROM ~ WHERE ~」となっています。
WHEREは条件です。「WHERE ~」は、「~という条件に合致したデータのみを取得する」という意味になります。
「SELECT ~ FROM ~ WHERE ~」は、①「FROM ~」 ②「WHERE ~」 ③「SELECT ~」の順に実行されます。①~③の内容を順にみていきます。

「FROM ~」は、「~で指定したテーブルからデータを取得する」という意味でした。
まずは「tweets」が指定されていますね。
次に「INNER JOIN users」と書かれています。直訳すると、「(tweetsテーブルと)usersテーブルを内部結合する」という意味になります。文字通り、テーブルどうしを結合して1つのテーブルにするということで、下図のイメージになります。

データベース

次の「ON users.id = tweets.user_id AND users.status = ‘active’」は結合条件になります。「usersテーブルのid(ユーザーID)の値と、tweetsテーブルのuser_idの値が等しい」場合のみテーブルを結合する、という意味になります。下図のイメージになります。

データベース

かつ、「users.status = ‘active’」条件により、usersテーブルのstatusカラムの値が「active」のデータのみが抽出されます。

さらに「LEFT JOIN」と書かれており、その結合条件として「likes.tweet_id = tweets.id AND likes.status = ‘active’ AND likes.user_id = ‘$user_id’」が指定されています。
「LEFT JOIN」もテーブルの結合を指しますが、「LEFT」とあるように「左側のテーブルのデータはすべて残す」という意味になります。下図のイメージになります。

データベース

かつ、「likes.status = ‘active’ AND likes.user_id = ‘$user_id’」の結合条件が指定されています。下図のイメージになります。

データベース

以上が「FROM ~」内で行われていることです。
次に「WHERE ~」内が実行されます。「tweets.status = ‘active’」は、tweetsテーブルのstatusカラムの値が「active」のデータのみを取得する、という意味ですね。
よって、上図の(結合前のtweetsテーブルのstatusの値が)activeであれば、当該データを取得できることになります。逆にactiveでなければ、当該データは取得できません。

最後に、「SELECT ~」内が実行されます。
「*」は、テーブルの全カラムの値を取得する、という意味になります。
一方、特定のカラム名を書くと、そのカラムの値のみを取得することができます。
例えば、「SELECT name from users」と書くと、usersテーブルのnameカラムの値だけが取得できます。複数カラムの値がほしい場合は、カンマ区切りで複数書きます。

ここでは「tweets.id」のようなカラムの指定の仕方をしています。
これは「tweetsテーブルのidカラム」を指定している、という意味になります。
「テーブル名.」を付けているのは、少し下に「users.id」があるためです。tweetsテーブルもusersテーブルも、同じカラム名の「id」をもっています。このような場合は、どちらのテーブルかを示すために、「テーブル名.」で教えてあげているのです。

「tweets.id AS tweet_id」とは何でしょうか。
これは、tweets.idを、別名の「tweet_id」というカラム名にリネームして取得する、という意味になります。ASを直訳すると「~として」なので、カラム名を「tweet_idとして(取得する)」ということですね。

次に「(SELECT COUNT(*) FROM likes WHERE status = ‘active’ AND tweet_id = tweets.id) AS like_count」に着目して下さい。「SELECT COUNT(*) FROM likes WHERE status = ‘active’ AND tweet_id = tweets.id」を括弧()で囲うことで一括りの処理としており、その処理結果を、like_countカラムの値としています。
では、「SELECT COUNT(*) FROM likes WHERE status = ‘active’ AND tweet_id = tweets.id」は何でしょうか。

「FROM likes WHERE status = ‘active’」の部分は、likesテーブルのデータ、かつstatusカラムの値が「active」であるデータ、という意味でしたね。
次の「tweet_id = tweets.id」に着目して下さい。
tweet_idはlikesテーブルのカラムです。tweets.idはtweetsテーブルの(ツイート)IDです。likesテーブルにあるtweet_idと同じ値を、tweetsテーブルから探しています。
「COUNT(*)」は数をカウントしてくれるデータベースが持つ関数です。すなわち、「likesテーブルにあるtweet_idと同じ値を、tweetsテーブルから探して、その数をカウントする」という意味になります。その値をlike_countとしています。

「SELECT ~」内が実行された結果、以下のような形でデータを取得できます。

データベース

SQLの実行および成否確認は以前解説した通りです。

「$tweets->fetch_all(MYSQLI_ASSOC)」でSQLの実行結果を、配列として取得して変数$tweetsに入れています。fetch_all関数はPHPの関数、MYSQLI_ASSOCはPHPの変数です。

ホームコントローラーの変更

ホームコントローラーを以下のように修正します。

htdocs/Twitter/Controllers/home.php

<?php

include_once '../util.php';

// ★変更箇所1(ここから)
// ツイートモデルを読み込み
include_once '../Models/tweets.php';
// ★変更箇所1(ここまで)

// ログインチェック
$view_user = getSession();
if ($view_user == false) {
    // ログインしていない
    header('Location: ../Controllers/sign-in.php');
    exit;
}

// ★変更箇所2(ここから)
// ツイート一覧取得
$view_tweets = getTweets($view_user);
// ★変更箇所2(ここまで)

// 画面表示
include_once '../Views/home.php';

?>

変更箇所1では、「include_once ‘../Models/tweets.php’」により、tweets.phpファイルを読み込んでいます(埋め込んでいます)。これにより、同ファイル内のgetTweets関数が使えるようになります。

変更箇所2では、getTweet関数を呼び出して、ツイート一覧を取得しています。
引数には、セッションのユーザー情報を渡しています。
取得したツイート一覧($view_tweets)は、Views/home.phpの中で使われることになります。

ツイート領域の変更

ツイート領域を以下のように修正します。

htdocs/Twitter/Views/common/tweet.php

<div class="tweet">
    <div class="user">
        <a href="profile.php?user_id=<?php echo $view_tweet['user_id']; ?>">
            <img src="img_uploaded/user/<?php echo $view_tweet['user_image_name']; ?>" alt="">
        </a>
    </div>
    <div class="content">
        <div class="name">
            <a href="profile.php?user_id=<?php echo $view_tweet['user_id']; ?>">
                <span class="nickname"><?php echo $view_tweet['user_nickname']; ?></span>
                <span class="user-name">@<?php echo $view_tweet['user_name']; ?> ・<?php echo $view_tweet['tweet_created_at']; ?></span>
            </a>
        </div>
        <p><?php echo $view_tweet['tweet_body'] ?></p>

        <?php if (isset($view_tweet['tweet_image_name'])) : ?>
            <!-- ★変更箇所(ここから) -->
            <img src="<?php echo $view_tweet['tweet_image_name']; ?>" alt="" class="post-image">
            <!-- ★変更箇所(ここまで) -->
        <?php endif; ?>

        <div class="icon-list">
            <div class="like js-like" data-like-id="<?php echo $view_tweet['like_id']; ?>">
                <?php
                    if (isset($view_tweet['like_id'])) {
                        // いいね!がある場合
                        echo '<img src="../Views/img/heart.svg" alt="">';
                    } else {
                        // いいね!がない場合
                        echo '<img src="../Views/img/heart2.svg" alt="">';
                    }
                ?>
            </div>
            <div class="like-count js-like-count"><?php echo $view_tweet['like_count']; ?></div>
        </div>
    </div>
</div>

微調整を行っています。

ログインすると、ホーム画面に投稿したツイートが表示されることを確認して下さい。

ブラウザ

ここまで出来たら、変更内容をコミットし、GitHubにプッシュして下さい。

本節の説明は以上になります。

トップページ <<前のカリキュラムへ戻る 次のカリキュラムへ進む>>

タイトルとURLをコピーしました