【独学】はじめてのWebプログラミング – 9.10 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;
}

// ★変更箇所1(ここから)
function getTweets($user, $keyword = null)
// ★変更箇所1(ここまで)
{
    $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;

    // ★変更箇所2(ここから)
    // 検索キーワードが指定された場合
    if (isset($keyword)) {
        // ツイートした人のニックネームとユーザー名と本文から部分一致で検索
        $query .= ' AND CONCAT(users.nickname, users.name, tweets.body) LIKE "%' . $keyword . '%"';
    
        // 新しい順に並び替え
        $query .= ' ORDER BY tweets.created_at DESC';

        // 表示件数は50件とする
        $query .= ' LIMIT 50';
    }
    // ★変更箇所2(ここまで)

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

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

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

    $mysqli->close();

    return $tweets;
}

?>

変更箇所1について解説します。
引数$keywordを追加しています。「$keyword = null」のように書くことで、関数の呼び出し元がこの引数を指定しなかった場合、すなわちgetTweets($user)のように$keywordを指定せずに呼び出した場合、関数内で$keywordの中にはnullが入っていることになります。

変更箇所2について解説します。
$keywordが指定された場合、SQL文字列をさらに結合しています。元々のSQLの「WHERE tweets.status = ‘active’」の後に結合されることになります。
「CONCAT(users.nickname, users.name, tweets.body)」の部分に着目して下さい。COCAT関数はデータベースの関数で、引数で指定した値を結合してくれます。例えば、users.nicknameが「ichiro」、users.nameが「suzuki」、tweets.bodyが「こんにちは」だった場合、それらを結合すると「ichirosuzukiこんにちは」になります。これがCONCAT関数を実行した結果の値です。その値に対し、LIKEを使って指定された$keywordと部分一致があるかを判定しています。例えば、$keywordの値が「suzuki」だった場合、部分一致があることになりますね。

検索コントローラーの作成

Controllersフォルダ内に「search.php」ファイルを作成します。

VSCode

search.phpに以下の内容を書きます。

htdocs/Twitter/Controllers/search.php

<?php

include_once '../util.php';

// ツイートモデルを読み込み
include_once '../Models/tweets.php';

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

$keyword = null;

// 検索キーワードを取得
if (isset($_GET['keyword'])) {
    $keyword = $_GET['keyword'];
}

// ツイート一覧取得
$view_tweets = getTweets($view_user, $keyword);

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

?>

キーワードが指定された場合、$_GET[‘keyword’]でキーワードを取り出し、getTweets関数に渡しています。

ブラウザ

プロフィール編集機能の作成

ユーザーモデルの変更

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

htdocs/Twitter/Models/users.php

<?php

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

// ユーザー登録関数
function createUser($data)
{
    // データベースに接続
    $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);

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

    // ユーザー登録のSQLを作成
    $query = 'INSERT INTO users (email, name, nickname, password) VALUES (?, ?, ?, ?)';

    // セキュリティ上、以下のように書く
    $statement = $mysqli->prepare($query);
    $statement->bind_param('ssss', $data['email'], $data['name'], $data['nickname'], $data['password']);

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

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

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

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

// ログイン関数
function login($email, $password)
{
    // データベースに接続
    $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);

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

    // ユーザー情報取得のSQLを作成
    $query = 'SELECT * FROM users WHERE email = "'.$email.'"';

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

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

    // ユーザー情報を取得
    $user = $result->fetch_array(MYSQLI_ASSOC);
    if ($user == false) {
        echo 'ユーザーが存在しません。<br>';
        $mysqli->close();  // データベースへの接続解除
        return false;
    }

    // パスワードチェック
    if ($password != $user['password']) {
        echo 'パスワードが一致しません。<br>';
        $mysqli->close();  // データベースへの接続解除
        return false;
    }

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

    // ユーザー情報を返す
    return $user;
}

// ★変更箇所1(ここから)
// ユーザー情報取得
function getUser($user_id)
{
    // データベースに接続
    $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);

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

    // ユーザー情報取得のSQLを作成
    $query = 'SELECT * FROM users WHERE id = "'.$user_id.'"';

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

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

    // ユーザー情報を取得
    $user = $result->fetch_array(MYSQLI_ASSOC);
    if ($user == false) {
        echo 'ユーザーが存在しません。<br>';
        $mysqli->close();  // データベースへの接続解除
        return false;
    }

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

    // ユーザー情報を返す
    return $user;
}
// ★変更箇所1(ここまで)

// ★変更箇所2(ここから)
// ユーザー情報更新
function updateUser($data)
{
    // データベースに接続
    $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);

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

    // ユーザー情報更新のSQLを作成
    $dataset = '';
    $dataset = $dataset.'name="'.$data['name'].'"';
    $dataset = $dataset.',';
    $dataset = $dataset.'nickname="'.$data['nickname'].'"';
    $dataset = $dataset.',';
    $dataset = $dataset.'email="'.$data['email'].'"';
    if (isset($data['image_name'])) {
        $dataset = $dataset.',';
        $dataset = $dataset.'image_name="'.$data['image_name'].'"';
    }
    if (isset($data['password'])) {
        $dataset = $dataset.',';
        $dataset = $dataset.'password="'.$data['password'].'"';
    }
    $query = 'UPDATE users SET '. $dataset;
    $query = $query.' WHERE id = "'.$data['id'].'"';

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

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

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

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

?>

ユーザー情報を取得する「getUser関数」と、ユーザー情報を更新する「updateUser関数」を追加しました。

プロフィールコントローラーの作成

Controllersフォルダ内に「profile.php」ファイルを作成します。

VSCode

profile.phpに以下の内容を書きます。

htdocs/Twitter/Controllers/profile.php

<?php

include_once '../util.php';

// ユーザーモデルを読み込み
include_once '../Models/users.php';

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

// ユーザー情報の更新
if (isset($_POST['nickname']) && isset($_POST['name']) && isset($_POST['email'])) {
    $data = [
        'id' => $view_user['id'],
        'name' => $_POST['name'],
        'nickname' => $_POST['nickname'],
        'email' => $_POST['email'],
    ];

    // パスワードが入力されている場合
    if (isset($_POST['password'])) {
        $data['password'] = $_POST['password'];
    }

    // ファイルがアップロードされている場合
    if (isset($_FILES['image']) && is_uploaded_file($_FILES['image']['tmp_name'])) {
        $data['image_name'] = saveFile($view_user, $_FILES['image'], 'user');
    }

    // ユーザー情報を更新
    $result = updateUser($data);
    if ($result) {
        // 成功したらセッション情報も更新
        $view_user = getUser($view_user['id']);
        saveSession($view_user);

        // 成功したらプロフィール画面に遷移(リロード)
        header('Location: ../Controllers/profile.php');
        exit;
    }
}

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

?>

ユーザー情報の更新をしたら、再度ユーザー情報を取得してセッション情報も更新しています。

プロフィールビューの変更

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

htdocs/Twitter/Views/profile.php

<!DOCTYPE html>
<html>
    <lang="ja">
    <head>
        <?php include_once('../Views/common/header.php'); ?>
        <title>プロフィール</title>
    </head>

    <!-- ★変更箇所(ここから)-->
    <body class="home profile">
        <div class="container">
            <?php include_once('../Views/common/side.php'); ?>
            <div class="main">
                <div class="main-header">
                    <h1><?php echo $view_user['nickname']; ?></h1>
                </div>
                <!-- プロフィールエリア -->
                <div class="profile-area">
                    <div class="top">
                        <div class="user"><img src="<?php echo $view_user['image_name']; ?>" alt=""></div>
                        <?php if (isset($_GET['user_id'])) : ?>
                            <!-- 他のユーザーのWebページ -->
                            <?php if (isset($_GET['case'])) : ?>
                                <button class="btn btn-sm">フォローをやめる</button>
                            <?php else : ?>
                                <button class="btn btn-sm btn-reverse">フォローする</button>
                            <?php endif; ?>
                        <?php else : ?>
                            <!-- 自分のWebページ -->
                            <button class="btn btn-reverse btn-sm" data-bs-toggle="modal" data-bs-target="#js-modal">プロフィール編集</button>
                        <?php endif; ?>
                    </div>

                    <div class="name"><?php echo $view_user['nickname']; ?></div>
                    <div class="text-muted">@<?php echo $view_user['name']; ?></div>

                    <div class="follow-follower">
                        <div class="follow-count">1</div>
                        <div class="follow-text">フォロー中</div>
                        <div class="follow-count">1</div>
                        <div class="follow-text">フォロワー</div>
                    </div>
                </div>

                <!-- 仕切りエリア -->
                <div class="ditch"></div>

                <!-- ツイート一覧エリア -->
                <?php if (empty($view_tweets)): ?>
                    <p class="p-3">ツイートがありません</p>
                <?php else: ?>
                    <div class="tweet-list">
                        <?php foreach ($view_tweets as $view_tweet) : ?>
                            <?php include('../Views/common/tweet.php'); ?>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
            </div>
        </div>

        <!-- モーダル -->
        <div class="modal" id="js-modal">
            <div class="modal-dialog">
                <div class="modal-content">
                    <form action="profile.php" method="post" enctype="multipart/form-data">
                        <div class="modal-header">
                            <h5 class="modal-title">プロフィール編集</h5>
                            <button class="btn-close" type="button" data-bs-dismiss="modal" aria-label="Close"></button>
                        </div>
                        <div class="modal-body">
                            <div class="user">
                                <img src="<?php echo $view_user['image_name']; ?>" alt="">
                            </div>
                            <div class="mb-3">
                                <label for="" class="mb-1">プロフィール写真</label>
                                <input type="file" class="form-control form-control-sm" name="image">
                            </div>

                            <input type="text" class="form-control mb-4" name="nickname" value="<?php echo $view_user['nickname']; ?>" placeholder="ニックネーム" maxlength="50" required>
                            <input type="text" class="form-control mb-4" name="name" value="<?php echo $view_user['name']; ?>" placeholder="ユーザー名" maxlength="50" required>
                            <input type="email" class="form-control mb-4" name="email" value="<?php echo $view_user['email']; ?>" placeholder="メールアドレス" maxlength="254" required>
                            <input type="password" class="form-control mb-4" name="password" value="<?php echo $view_user['password']; ?>" placeholder="" minlength="4" maxlength="128">
                        </div>
                        <div class="modal-footer">
                            <button class="btn btn-reverse" data-bs-dismiss="modal">キャンセル</button>
                            <button class="btn" type="submit">保存する</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </body>
    <!-- ★変更箇所(ここまで)-->
</html>

各プロフィール情報を、PHPの$view_userの値を出力するようにしています。

ここまで出来たら、以下のようにプロフィール編集ができるようになっていますので確認して下さい。

ブラウザ

ホームビューの変更

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

htdocs/Twitter/Views/home.php

<!DOCTYPE html>
<html>
    <lang="ja">
    <head>
        <?php include_once('../Views/common/header.php'); ?>
        <title>Twitter</title>
    </head>
    <body class="home">
        <div class="container">
            <?php include_once('../Views/common/side.php'); ?>
            <div class="main">
                <div class="main-header">
                    <h1>ホーム</h1>
                </div>
                <div class="tweet-post">
                    <div class="my-icon">
                        <!-- ★変更箇所(ここから)-->
                        <img src="<?php echo $view_user['image_name']; ?>" alt="">
                        <!-- ★変更箇所(ここまで)-->
                    </div>
                    <div class="input-area">
                        <form action="post.php" method="post" enctype="multipart/form-data">
                            <textarea name="body" placeholder="Hello" maxlength="140"></textarea>
                            <div class="bottom-area">
                                <div class="mb-0">
                                    <input type="file" name="image" class="form-control form-control-sm">
                                </div>
                                <button class="btn" type="submit">つぶやく</button>
                            </div>
                        </form>
                    </div>
                </div>
                <div class="ditch"></div>
                <?php if (empty($view_tweets)): ?>
                    <p class="p-3">ツイートがありません</p>
                <?php else: ?>
                    <div class="tweet-list">
                        <?php foreach ($view_tweets as $view_tweet) : ?>
                            <?php include('../Views/common/tweet.php'); ?>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
            </div>
        </div>
    </body>
</html>

サイド領域の変更

サイド領域を以下のように修正します。

htdocs/Twitter/Views/common/side.php

<div class="side">
    <div class="side-inner">
        <ul class="nav flex-column">
            <li class="nav-item"><a href="home.php" class="nav-link"><img src="../Views/img/1.svg" alt="" class="icon"></a></li>
            <li class="nav-item"><a href="home.php" class="nav-link"><img src="../Views/img/2.svg" alt=""></a></li>
            <li class="nav-item"><a href="search.php" class="nav-link"><img src="../Views/img/3.svg" alt=""></a></li>
            <li class="nav-item"><a href="notification.php" class="nav-link"><img src="../Views/img/4.svg" alt=""></a></li>
            <li class="nav-item"><a href="profile.php" class="nav-link"><img src="../Views/img/5.svg" alt=""></a></li>
            <li class="nav-item"><a href="post.php" class="nav-link"><img src="../Views/img/6.svg" alt="" class="icon"></a></li>
            <!-- ★変更箇所(ここから)-->
            <li class="nav-item"><img src="<?php echo $view_user['image_name']; ?>" alt="" class="my-icon"></li>
            <!-- ★変更箇所(ここまで)-->
            <li><a href="sign-out.php" class="nav-link">logout</a></li>
        </ul>
    </div>
</div>
ブラウザ

これで、プロフィール編集結果が、ホーム画面およびサイド領域に反映されるようになりました。

通知機能の作成

通知機能とは、他者からいいねやフォローをされたことを表示する機能です。
ここでは通知コントローラーのひな形を追加するだけに留めます。
余力がある方は、これまでの学習内容を参考に、自分なりに機能を追加してみて下さい。

通知コントローラーの作成

Controllersフォルダ内に「notification.php」ファイルを作成します。

VSCode

notification.phpに以下の内容を書きます。

htdocs/Twitter/Controllers/notification.php

<?php

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

?>
ブラウザ

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

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

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

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