40 代おっさん chatGPTを使ったアプリを作る②！

## 本記事について

本記事は私が学習していく中でわからない単語や概要をなるべくわかりやすい様にまとめたものです。
もし誤りなどありましたらコメントにてお知らせいただけるとありがたいです。

## 概要

chatAPIを使用してアプリを作る。
laravel10,php8.2

## 前回の記事

https://qiita.com/kou1121/items/4c12ceb9e448171955d8

## 今回やること

・SQLにつなげる
・質問、回答を保存
・一覧表示といいねを押せるようにする

## SQLにつなげる

### `.env` ファイルにデータベースの設定を追加

```
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=chatgpt_db
DB_USERNAME=root
DB_PASSWORD=password
```

username,passworoは設定してあるもので

## 質問、回答を保存

### マイグレーションの作成

マイグレーションとは
データベースのバージョン管理のようなものです！ マイグレーションを使用してデータベースの変更を行うことにより、データベースの構造変更やデータの追加、変更、削除などの情報がファイルに記録されます。 そのため、変更の履歴や内容を共有することができ、誰が見ても変更の履歴が明確になります！

参考

https://zenn.dev/nenenemo/articles/10a3605c037ab6

```sh
php artisan make:migration create_questions_table
php artisan make:migration create_answers_table
```

`database/migrations/create_questions_table.php`

```php
public function up() {
    Schema::create('questions', function (Blueprint $table) {
        $table->id();
        $table->string('question');
        $table->timestamps();
    });
}
```

upメソッドは、migrationを実行したDBのスキーマを変更するための手順を記述します。

id 符号なしBIGINT
string 可変長の文字列
timestamp 現在の日時

`database/migrations/create_answers_table.php`

```php
public function up() {
    Schema::create('answers', function (Blueprint $table) {
        $table->id();
        $table->foreignId('question_id')->constrained();
        $table->text('answer');
        $table->integer('likes')->default(0);
        $table->timestamps();
    });
}
```

`foreignId` 外部キー列を定義するために使われます
`constrained` 
・外部キー制約の追加：指定されたカラムが参照する他のテーブルのカラムに対する外部キー制約を追加します。デフォルトでは、参照先のテーブルはカラム名から推測されます。
・参照するテーブルの推測：foreignId('user_id')と書いた場合、自動的にusersテーブルを参照するようになります。
・参照カラムのデフォルト設定：参照するカラムはデフォルトでidカラムになります。
`text` 大きなテキストデータを格納する
`integer` 整数

### モデルの作成

Laravelで使うモデルは主にDBとの連携を行う機能を持っています。
DBのデータの追加や登録、データの取得や並び替えなどDBの全般的な操作が可能です。

基本的には１つのテーブルにつき１つのモデルが存在します。
テーブルとモデルが対応することで、モデルからDBの操作が可能になるということになります。

参考

https://qiita.com/MiyakeNatsuho/items/41ff0654ddf31e148ebf

`Question` および `Answer` モデルを作成

```sh
php artisan make:model Question
php artisan make:model Answer
```

`app/Models/Question.php`

```php
class Question extends Model {
    use HasFactory;
    protected $fillable = ['question'];

    public function answers() {
        return $this->hasMany(Answer::class);
    }
}
```

`use HasFactory;`　Laravelのモデルに対してファクトリー機能を使用するためのメソッドを提供します。ファクトリーは、テストデータやダミーデータを簡単に生成するための仕組みです。

`protected $fillable = ['question'];`は、LaravelのEloquentモデルにおいて、マスアサインメント（Mass Assignment）を許可する属性を指定するために使用されます。

マスアサインメントとは
データベースに保存するためにモデルに大量のデータを一度に割り当てる操作のことです。例えば、リクエストから受け取ったデータをそのままモデルに割り当てる場合などです。

`$fillable` プロパティ
$fillableプロパティは、マスアサインメントを許可する属性（カラム）を指定します。これにより、指定した属性のみがマスアサインメントによって更新されることが保証されます。

`return $this->hasMany(Answer::class);`は、LaravelのEloquentモデルでリレーションシップを定義するためのメソッドです。この場合、一つのモデル（例：Question）が多数の関連するモデル（例：Answer）を持つ「1対多」のリレーションシップを定義しています。

詳細な説明
1.リレーションシップのタイプ

1対多（One-to-Many）：一つの親モデル（Question）が多数の子モデル（Answer）を持つリレーションシップ。

2.hasManyメソッド

hasManyメソッドは、親モデルが多数の子モデルを持つことを示します。
親モデルの中で、このリレーションシップを定義します。

`app/Models/Answer.php`

```php
class Answer extends Model {
    use HasFactory;
    protected $fillable = ['question_id', 'answer', 'likes'];

    public function question() {
        return $this->belongsTo(Question::class);
    }
}
```

`belongsTo`メソッド

belongsToメソッドは、子モデルが親モデルに属することを示します。
子モデルの中で、このリレーションシップを定義します。

### コントローラーの更新

`ChatGPTController` を更新して、質問と回答を保存：

```php
use App\Models\Question;
use App\Models\Answer;

public function ask(Request $request) {
    $questionText = $request->input('question');
    $question = Question::create(['question' => $questionText]);

    // (API呼び出しのコードは同じ)
    $body = json_decode((string) $response->getBody(), true);
    $answerText = $body['choices'][0]['message']['content'];

    $answer = $question->answers()->create(['answer' => $answerText]);

    return view('answer', ['answer' => $answerText, 'question' => $questionText]);
}
```

`$questionText = $request->input('question');`は、LaravelでHTTPリクエストからデータを取得するためのコードです。この例では、questionという名前の入力フィールドの値を取得して、$questionTextという変数に格納しています。

詳細な説明
`$request`オブジェクト:

コントローラーのメソッドでインジェクトされるインスタンスで、現在のHTTPリクエストに関する情報を保持しています。
ルート、クエリパラメータ、ポストデータ、ファイル、クッキー、セッション情報などにアクセスできます。

`input`メソッド:

HTTPリクエストの入力データを取得するために使用されます。
フォームの入力フィールドやクエリパラメータの値を取得するのに便利です。

`$question = Question::create(['question' => $questionText]);`は、LaravelのEloquent ORMを使って、新しいレコードをデータベースに挿入するためのコードです。この場合、Questionモデルに対して新しい質問を作成しています。

詳細な説明
`Question`モデル:

データベースのquestionsテーブルに対応するEloquentモデルです。
モデルを使用することで、データベースとのやり取りが簡単になります。

`create`メソッド:

Eloquentモデルのcreateメソッドは、新しいレコードをデータベースに挿入します。
createメソッドは、引数として連想配列を受け取り、その配列のキーがテーブルのカラム名に対応し、値が挿入されるデータになります。

`$questionText`:

フォームから送信された質問のテキストです。
先ほどの例でリクエストから取得されたものです。

## 一覧表示と「いいね」機能の実装

`web.php` にルートを追加

```php
Route::get('/questions', [ChatGPTController::class, 'index']);
Route::post('/like/{answer}', [ChatGPTController::class, 'like']);
```

`ChatGPTController` にメソッドを追加

```php
public function index() {
    $questions = Question::with('answers')->get();
    return view('questions.index', compact('questions'));
}

public function like(Answer $answer) {
    $answer->increment('likes');
    return redirect()->back();
}
```

`resources/views/questions/index.blade.php`

```php
@foreach($questions as $question)
    <h3>{{ $question->question }}</h3>
    @foreach($question->answers as $answer)
        <p>{{ $answer->answer }}</p>
        <form action="/like/{{ $answer->id }}" method="post">
            @csrf
            <button type="submit">いいね ({{ $answer->likes }})</button>
        </form>
    @endforeach
@endforeach
```

`Route::post('/like/{answer}', [ChatGPTController::class, 'like']);`
このルート設定により、POSTリクエストが `/like/{answer}` に送信されたときに、`ChatGPTController` の `like` メソッドが呼び出されます。

```php
public function like(Answer $answer)
{
    $answer->increment('likes');
    return redirect()->back();
}
```

このメソッドは、特定の回答の「いいね」数を1増やし、元のページにリダイレクトします。
`increment` フィールド値を１増やす