VSCode PHP MySQL Docker環境構築(Mac OS)　記録用

WEBアプリ開発を少しやってみたかったので独学で
持ち得る知識をフル活用してみた。

MacBook Air(M1)にて環境構築〜ログイン機能が完成するまでの作業メモ


![スクリーンショット 2024-07-15 14.29.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/1fc4a9aa-a805-7077-8ce2-0d9ca67b8f77.png)


```php
<?php
echo "Hello, World!";
?>
```


```yaml

version: '3.8'

services:
  php:
    image: arm64v8/php:8.2-apache
    ports:
      - "8888:80"
    volumes:
      - ./src:/var/www/html
    networks:
      - app-network
  mysql:
    image: mysql:5.7
    platform: linux/amd64
    container_name: mysql-container
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: testdb
      MYSQL_USER: user
      MYSQL_PASSWORD: password
    ports:
      - "3306:3306"
    networks:
      - app-network

networks:
  app-network:
    driver: bridge
```




https://zenn.dev/marumarumeruru/articles/55173a98863d4e

![スクリーンショット 2024-07-15 15.15.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/34917bde-1acb-7c44-bbff-361e271716dc.png)

![スクリーンショット 2024-07-15 15.13.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/66dd69f4-7f01-d852-e167-b0da679ed59a.png)




```
Error response from daemon: Conflict. The container name "/mysql-container" is already in use by container "ab40002890513d6c82e122a8bb052a1de9875a82eff662fdac7c9743980bce56". You have to remove (or rename) that container to be able to reuse that name.
```


このエラーは、すでに同じ名前のコンテナが存在しているため、新しいコンテナを作成できないことが原因です。この問題を解決するためには、既存のコンテナを削除するか、コンテナの名前を変更する必要があります。



```yaml
container_name: php-mysql-container
```

![スクリーンショット 2024-07-15 15.21.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/b429d7ea-c46a-28d9-3652-627664c94c81.png)


```cmake
irochi@irochi-macbook src % docker ps
CONTAINER ID   IMAGE            COMMAND                   CREATED          STATUS          PORTS                               NAMES
cb871d6f5c1f   php:8.2-apache   "docker-php-entrypoi…"   33 seconds ago   Up 31 seconds   0.0.0.0:8888->80/tcp                php-docker-php-1
8b3b6d7fcd16   mysql:5.7        "docker-entrypoint.s…"   33 seconds ago   Up 31 seconds   0.0.0.0:3306->3306/tcp, 33060/tcp   php-mysql-container
irochi@irochi-macbook src % 
```

http://localhost:8888/

![スクリーンショット 2024-07-15 15.23.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/93e7dbdc-51d1-156f-8e20-f34d083f0305.png)


```php5
<?php
$servername = "php-mysql-container";
$username = "user";
$password = "password";
$dbname = "testdb";

// MySQL データベースへの接続
$conn = new mysqli($servername, $username, $password, $dbname);

// 接続をチェック
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully";

// 簡単なクエリの実行
$sql = "SELECT 1";
$result = $conn->query($sql);

if ($result) {
    echo "Query successful.";
} else {
    echo "Error: " . $conn->error;
}

// 接続を閉じる
$conn->close();

?>
```


http://localhost:8888/db_test.php

![スクリーンショット 2024-07-15 17.56.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/dccde250-35ff-9778-6cb8-968cfe3ae2a8.png)







```
/Users/irochi/php-docker
├── docker-compose.yml
├── Dockerfile
├── mysql-init/
│    └── init.sql
└── src/
    ├── index.php
    └── db_test.php
```




```sql
CREATE DATABASE IF NOT EXISTS todoapp;

USE todoapp;

CREATE TABLE IF NOT EXISTS tasks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    description TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO tasks (title, description) VALUES 
('Sample Task 1', 'This is the first sample task'),
('Sample Task 2', 'This is the second sample task')
```


```yaml
version: '3.8'

services:
  php:
    build: .
    ports:
      - "8888:80"
    volumes:
      - ./src:/var/www/html
    networks:
      - app-network
  mysql:
    image: mysql:5.7
    platform: linux/amd64
    container_name: php-mysql-container
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: testdb
      MYSQL_USER: user
      MYSQL_PASSWORD: password
    ports:
      - "3306:3306"
    volumes:
      - ./mysql-init:/docker-entrypoint-initdb.d // New!
    networks:
      - app-network

networks:
  app-network:
    driver: bridge
```


```
docker exec -it php-mysql-container mysql -u user -p
```

```
irochi@irochi-macbook src % docker exec -it php-mysql-container mysql -u user -p

Enter password:  ←passwordと入力
```


```sql
ERROR 1044 (42000): Access denied for user 'user'@'%' to database 'todoapp'
```



![スクリーンショット 2024-07-15 18.40.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/fbf42572-044b-5405-c2cc-c8213824500d.png)


init.sql
`GRANT ALL PRIVILEGES ON todoapp.* TO 'user'@'%';  -- 権限付与`



![スクリーンショット 2024-07-15 18.51.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/443e314a-512a-37d9-49a7-f4a4f2259d4a.png)


```php5
<?php
// データベース接続情報
$servername = "php-mysql-container"; // MySQLコンテナのホスト名
$username = "user"; // MySQLのユーザー名
$password = "password"; // MySQLのパスワード
$dbname = "todoapp"; // 使用するデータベース名

// MySQL データベースへの接続
$conn = new mysqli($servername, $username, $password, $dbname);

// 接続をチェック
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error); // 接続エラーの場合、エラーメッセージを表示してスクリプトを終了
}

// SQLクエリを準備
$sql = "SELECT id, title, description, created_at FROM tasks";

// クエリを実行
$result = $conn->query($sql);

// 結果を確認
if ($result->num_rows > 0) {
    // データが存在する場合、各行を出力
    echo "<h1>ToDo List</h1>";
    echo "<table border='1'>";
    echo "<tr><th>ID</th><th>Title</th><th>Description</th><th>Created At</th></tr>";
    while($row = $result->fetch_assoc()) {
        echo "<tr>";
        echo "<td>" . $row["id"] . "</td>";
        echo "<td>" . $row["title"] . "</td>";
        echo "<td>" . $row["description"] . "</td>";
        echo "<td>" . $row["created_at"] . "</td>";
        echo "</tr>";
    }
    echo "</table>";
} else {
    // データが存在しない場合、メッセージを表示
    echo "No tasks found";
}

// 接続を閉じる
$conn->close();
?>
```




![スクリーンショット 2024-07-15 18.54.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/d2dba897-c3d3-d835-fe5d-acfa0211c66f.png)



データベースから値の取得を完了

ログイン機能を作ってみる

```sql
CREATE DATABASE IF NOT EXISTS todoapp;

GRANT ALL PRIVILEGES ON todoapp.* TO 'user'@'%';

USE todoapp;

CREATE TABLE IF NOT EXISTS tasks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    description TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO tasks (title, description) VALUES 
('Sample Task 1', 'This is the first sample task'),
('Sample Task 2', 'This is the second sample task');

CREATE TABLE IF NOT EXISTS User (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    mail_address VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL
);

INSERT INTO User (name, mail_address, password) VALUES
('佐藤　　　一郎', 'sato@example.com', MD5('password123')),
('木下　　　たかし', 'kinoshita@example.com', MD5('password456'));
```


ログイン画面

```php5
<!DOCTYPE html>
<html>
<head>
    <title>Todoログイン</title>
</head>
<body>
    <h2>ログイン</h2>
    <form method="post" action="authenticate.php">
        <label for="mail_address">メールアドレス:</label>
        <input type="email" name="mail_address" required><br><br>
        <label for="password">パスワード:</label>
        <input type="password" name="password" required><br><br>
        <input type="submit" value="Login">
    </form>
</body>
</html>

```

ログイン認証
```php5
<?php
session_start();

// データベース接続情報
$servername = "php-mysql-container";
$username = "user";
$password = "password";
$dbname = "todoapp";

// MySQL データベースへの接続
$conn = new mysqli($servername, $username, $password, $dbname);

// 接続をチェック
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

// POSTデータの取得
$mail_address = $_POST['mail_address'];
$password = md5($_POST['password']); // パスワードをMD5でハッシュ化

// SQLクエリを準備
$sql = "SELECT * FROM User WHERE mail_address = '$mail_address' AND password = '$password'";
$result = $conn->query($sql);

// 結果を確認
if ($result->num_rows > 0) {
    // ユーザーが存在する場合
    $row = $result->fetch_assoc();
    $_SESSION['username'] = $row['name'];
    header("Location: welcome.php");
} else {
    // ユーザーが存在しない場合
    echo "メールアドレスまたはパスワードが違います。";
}

// 接続を閉じる
$conn->close();
?>
```


```php5
<?php
session_start();

// セッションにユーザー名が設定されているか確認
if (!isset($_SESSION['username'])) {
    header("Location: login.php");
    exit();
}

$username = $_SESSION['username'];
?>

<!DOCTYPE html>
<html>
<head>
    <title>Welcome</title>
</head>
<body>
    <h2>ようこそ、<?php echo htmlspecialchars($username); ?>さん</h2>
</body>
</html>
```

![スクリーンショット 2024-07-17 6.59.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/e17805fb-6098-894e-af87-4e0b1696ed69.png)


![スクリーンショット 2024-07-17 7.00.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/97395eae-a286-99bb-1fca-d57486d80393.png)


![スクリーンショット 2024-07-17 7.00.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/569eec8c-4d61-7e3c-3166-e48ca2dccad7.png)






login.php
```php5
<!DOCTYPE html>
<html>
<head>
    <title>Todoログイン</title>
    <style>
        .error { color: red; }
    </style>
</head>
<body>
    <h2>ログイン</h2>
    <?php if (isset($_GET['error'])): ?>
        <p class="error">メールアドレスまたはパスワードが違います。</p>
    <?php endif; ?>
    <form method="post" action="authenticate.php" onsubmit="return validateForm()">
        <label for="mail_address">メールアドレス:</label>
        <input type="email" name="mail_address" id="mail_address" required><br><br>
        <label for="password">パスワード:</label>
        <input type="password" name="password" id="password" required><br><br>
        <input type="submit" value="Login">
    </form>
    <script>
        function validateForm() {
            var mail_address = document.getElementById("mail_address").value;
            var password = document.getElementById("password").value;
            if (mail_address === "" || password === "") {
                alert("メールアドレスとパスワードを入力してください。");
                return false;
            }
            return true;
        }
    </script>
</body>
</html>
```


authenticate.php
```php5
<?php
session_start();

// データベース接続情報
$servername = "php-mysql-container";
$username = "user";
$password = "password";
$dbname = "todoapp";

// MySQL データベースへの接続
$conn = new mysqli($servername, $username, $password, $dbname);

// 接続をチェック
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

// POSTデータの取得
$mail_address = $_POST['mail_address'];
$password = md5($_POST['password']); // パスワードをMD5でハッシュ化

// SQLクエリを準備
$sql = "SELECT * FROM User WHERE mail_address = '$mail_address' AND password = '$password'";
$result = $conn->query($sql);

// 結果を確認
if ($result->num_rows > 0) {
    // ユーザーが存在する場合
    $row = $result->fetch_assoc();
    $_SESSION['username'] = $row['name'];
    header("Location: welcome.php");
} else {
    // ユーザーが存在しない場合
    header("Location: login.php?error=1");
    exit();
}

// 接続を閉じる
$conn->close();
?>
```


welcome.php
```php5
<?php
session_start();

// セッションにユーザー名が設定されているか確認
$username = isset($_SESSION['username']) ? $_SESSION['username'] : null;
?>

<!DOCTYPE html>
<html>
<head>
    <title>Welcome</title>
</head>
<body>
    <?php if ($username): ?>
        <h2>ようこそ、<?php echo htmlspecialchars($username); ?>さん</h2>
        <form method="post" action="logout.php">
            <input type="submit" value="ログアウト">
        </form>
    <?php else: ?>
        <h2>ログインしてください。</h2>
        <form method="post" action="login.php">
            <input type="submit" value="ログイン画面に戻る">
        </form>
    <?php endif; ?>
</body>
</html>
```


logout.php
```php5
<?php
session_start();
session_destroy();
header("Location: login.php");
exit();
?>
```



機能面は終了

BootStrapの適用（CDN）


![スクリーンショット 2024-07-17 7.23.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/6dcb174c-5154-2a2f-d08f-1f27de15dfb1.png)


![スクリーンショット 2024-07-17 7.24.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/235faab4-bd44-c3a7-38f3-7ab0bdbed407.png)


![スクリーンショット 2024-07-17 7.24.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/55ac7c28-094e-ec68-f840-b241bcd77636.png)

![スクリーンショット 2024-07-17 7.25.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838575/90f1b98a-cf88-e2fb-7899-8c00fad00af1.png)