【備忘録】【Laravel8】ファン交流サイトを作ってみよう①

Laravel学習中の者です。
実際にアプリを作りながら習得していくことを目的として、
ファン交流サイトを作ってみます。

# ER図
未作成

# 前提

このプログラムは下記環境で作成しています。
+ MacOS
+ ターミナル
+ composer
+ php7.3以上
+ MySQL
が必要になります。

# composerのインストール

composerがインストールされていない場合、インストールが必要です。
▼インストールはこちらから（公式ページ）
https://getcomposer.org/doc/00-intro.md

公式だと難しいと思う方は「Mac composer インストール」で検索すると、インストール記事が見つかります。

ターミナルを開いて
composer --version
を実行してバージョン情報が表示されればOK。

# php7.3 and MySQL

MacにphpとMySQLをインストールするにはMAMPが最も簡単です。
MAMPはMac用のアプリで、PHP, MySQL, Apacheといった開発に必要なサーバー側のソフトウェアを一発でインストールできるスグレモノです。

https://www.mamp.info/en/mamp/mac/

Laravel8はPHP7.3以上でないと動かないのでPHPは7.3以上をインストールする必要があります。

MAMPをMacにインストールしたらターミナルから php -v を実行してPHPのバージョンを確認します。

```php:ターミナル
php -v
PHP 7.4.25 (cli) (built: Oct 23 2021 15:38:15) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.25, Copyright (c), by Zend Technologies
```

# 基本設定

# composerでプロジェクトを作成する

それでは１からファン交流サイトを作っていきます！

ホームディレクトリ直下に新規ディレクトリを作って移動します。

```php:ターミナル
cd ~
mkdir program
cd program
```

以下のcomposerコマンドを実行します。
Laravelのバージョンは8.*の最新
プロジェクト名はfan_siteとしました。

```php:ターミナル
composer create-project laravel/laravel=8.* fan_site
```

composerでプロジェクトが作成されたら、作成されたディレクトリでcomposer installを行いましょう。

```php:ターミナル
cd fan_site 
composer install
```

# ロケールをJPにする

VScodeなどのエディタでconfig/app.phpファイルを変更します。

```
例：
vi config/app.php

70行目近辺
'timezone' => 'Asia/Tokyo',

83行目近辺
'locale' => 'ja',
↑この設定をすることでエラーメッセージなどが日本語に切り替わります。

109行目近辺
'faker_locale' => 'ja_JP',
↑この設定をするとテスト用データを作成するときに人の名前を日本人っぽい名前にしてくれます。
```


# MySQLでデータベースを作成する

データベースはMySQLを使用します。
MySQLにコマンドラインでログインしてユーザーとデータベースを作成します。
MySQLに「fan_site」という名前のユーザーを作成し、fan_siteユーザーがアクセス出来る「fan_site」データベースを作成します。
使用するデータベースの作成方法はご自分の好みの方法で構いませんが、
ここではMAMPのphpMyAdminを使用していきます。

MAMPのユーザアカウント機能を使って、ユーザーとデータベースを一括で作成してしまいましょう。

入力項目は下記の通り。

```
ユーザ名　fan_site
ホスト名　localhost
[パスワードを生成する] ボタンをクリック（生成されたパスワードはどこかにメモしておく）
同名のデータベースを作成してすべての権限を与える。にチェックを入れる
```

一番下にある[実行] ボタンをクリックします。

# .envを設定

.envファイルとはプロジェクトで使用する設定定義ファイルです。秘匿にすべきパスワード情報なども入っています。取り扱い要注意です。

```php:ターミナル
cp -p .env.example .env
```

上記を実行して.envを用意します。

以下の設定は例です。ご自分のMySQLに合わせて設定を行ってください。

```php:.env
変更箇所のみ抜粋
APP_NAME=ファン交流サイト

DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=fan_site
DB_USERNAME=fan_site
DB_PASSWORD=#mypassword#
```

# key generate と laravel/uiのインストール

Laravel8の認証機能を使うために以下のコマンドを実行します。

```php:ターミナル
php artisan key:generate
composer require laravel/ui
composer install
php artisan ui vue --auth
```

# 試しに migration してみる

基本的なデータベース設定が済んだところでちゃんと接続できるか試してみましょう。

以下のコマンドを実行します。

```php:ターミナル
php artisan migrate
```

すると、私の場合はこんなエラーが出ました。

![スクリーンショット 2021-11-24 0.09.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/69e3973d-cc60-bb65-5106-c4846f39015a.png)


エラーメッセージで検索して、こちらの記事を参考に進めたところ解消できました。
https://qiita.com/mineaki27th/items/5effcc2605fe90ff6997

### 修正した箇所

#### ①.env
DB_PASSWORD="パスワード"　←ダブルクォーテーションでパスワードを囲む

#### ②database.php
mysqlのunix_socketの箇所を下記に変更
'unix_socket' => '/Applications/MAMP/tmp/mysql/mysql.sock',

#### ③キャッシュクリア
$ php artisan config:cache
$ php artisan cache:clear

その上で再度以下のコマンドを実行します。

```php:ターミナル
php artisan migrate
```

今度は成功しました！
phpMyAdminを見てみると、usersテーブル等が作成されています。


# データベースアクセスクラスの用意

artisan make:modelコマンドで、データベースにアクセスする用のクラスを作成していきます。

# まずはPostsテーブルを作成

```php:ターミナル
php artisan make:model Post --migration
```

成功すると以下のような表示になります。

```
php artisan make:model Post --migration
Model created successfully.
Created Migration: 2021_11_24_020205_create_posts_table
```

php artisan make:model Role --migrationを実行したときに以下のファイルが作られます。

database/migrations/#YYYY_MM_DD_NNNN#_create_posts_table.php
app/Models/Post.php

YYYY_MM_DD_NNNN#_create_posts_table.phpは、投稿テーブルpostsの定義ファイルです。

Post.phpはpostsテーブルにアクセスするためのEloquentモデルクラスファイルです。
EloquentとはLaravelのデータベースアクセスクラスです。
EloquentとはLaravelの一部で、データベースにアクセスする機能がまとまっているファイルです。データベースの１テーブルに対して１つのファイルを作ります。

# postsテーブルの定義

マイグレーションファイルはartisanコマンドで作った直後にはidとtimestampsカラムしかありません。
postsテーブルにtitleとbodyカラムを追加します。

```php:2021_11_24_020205_create_posts_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title', 8)->comment('タイトル');  // ← 追記 *********
            $table->text('body')->comment('本文');  // ← 追記 *********
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

```
$table->string('title', 8)->comment('タイトル'); の部分がカラムの定義になります。
('title', 8)の「8」は●●●●を意味しています。
Blueprintクラスのインスタンス $tableに対して stringメソッドを呼んで文字列型のカラムを追加しています。

$table->integer('amount');のようにintegerメソッドを呼ぶと数値型を格納するカラムを追加出来ます。

他にもDateなど日付を扱う指定もできます。

# テーブル定義が出来たところで migrate:refreshしてみる

```php:ターミナル
php artisan migrate:refresh
```

マイグレーションが実行されてテーブルが作成されました。

# ファクトリの作成（割愛）
ファクトリはテスト用にダミーデータを作る仕組みです。TDDでの開発が楽に出来るようになりますが、必須ではないので今回は割愛します。

# シーダーの作成

seederというテスト用データ作成クラスを作ります。今回はシーダーを使って会員情報、投稿内容を作っていきます。
以下のコマンドを実行してシーダークラスを作成します。

```
php artisan make:seeder UsersTableSeeder
php artisan make:seeder PostsTableSeeder
```

# UsersTableSeederの編集

```php:UsersTableSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB; // ← 追記 *********

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // ↓追記 ***メールアドレスを受信可能なものに設定すると通知メール試験でメールを受け取れます***
        DB::table('users')->insert(['id' => 1, 'name' => '山田花子', 'email' => 'sute1@example.com', 'email_verified_at' => "2021-09-15 15:22:10", 'password' => bcrypt('password')]);
        DB::table('users')->insert(['id' => 2, 'name' => '畠山まさみ', 'email' => 'sute2@example.com', 'email_verified_at' => "2021-10-12 16:22:10", 'password' => bcrypt('password')]);
        DB::table('users')->insert(['id' => 3, 'name' => '伊藤順子', 'email' => 'sute3@example.com', 'email_verified_at' => "2021-11-14 17:22:10", 'password' => bcrypt('password')]);
        DB::table('users')->insert(['id' => 4, 'name' => '西岡京子', 'email' => 'sute4@example.com', 'email_verified_at' => "2021-11-22 18:22:10", 'password' => bcrypt('password')]);
    }
}
```

# PostsTableSeederの編集

```php:PostsTableSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB; // ← 追記 *********

class PostsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // ↓ 追記 *********
        DB::table('posts')->insert(['id'=>1,'title'=>'大橋担','body'=>'お友達になりませんか？']); // ← 追記 *********
        DB::table('posts')->insert(['id'=>2,'title'=>'高橋担','body'=>'語り合いましょう']); // ← 追記 *********
        DB::table('posts')->insert(['id'=>3,'title'=>'西畑担','body'=>'お話ししましょ']); // ← 追記 *********
        DB::table('posts')->insert(['id'=>4,'title'=>'大西担','body'=>'よろしくお願いします！']); // ← 追記 *********
    }
}

```

# デフォルトで定義されているDatabaseSeeder.phpに、実行するシーダーを登録

DatabaseSeederクラスのrun関数にある $this->callの引数に実行するシーダークラスを追記します。
それによりシードの実行順序を制御できます。

```php:DatabaseSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        // $this->call(UsersTableSeeder::class);
        $this->call([
        UsersTableSeeder::class,
        PostsTableSeeder::class,
    ]);
    }
}
```

# 試しにマイグレーション

```php:ターミナル
php artisan migrate:refresh
```

# シーダーを実行してみる

```php:ターミナル
php artisan db:seed
```

phpMyAdminを確認すると、こんな感じでシーダーで設定したデータが登録されているはずです。

![スクリーンショット 2021-11-24 14.05.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/c63e0aa7-eb2b-a5da-b753-cca5b826f002.png)


# ブラウザで表示してみる

ポート番号を指定できるので指定します。省略すると8000ポートが使われます。

```php:ターミナル
php artisan serve --port=9999
```

![スクリーンショット 2021-12-04 17.56.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/735d313e-27c0-1bd6-cd71-f10426a2b902.png)


デフォルトのWelcome画面が表示されます。

Log inを押すと、Bootstrapが反映されておらず質素なページになっています。

![スクリーンショット 2021-12-04 22.27.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/2ac166d7-1a78-e534-125b-bf9a03672b14.png)

なので反映させていきます。
下記コマンドを実施してください。

```php:ターミナル
npm install
npm run dev　//1回目
npm run dev　//2回目
```

確認してみると、今度はちゃんと反映されました。
![スクリーンショット 2021-12-04 22.36.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/7405fad0-911e-7c89-4352-4092b7270144.png)

前もって作っておいたid 1番の人のemailとpasswordを入れて、ログインしてみると...
![スクリーンショット 2021-12-04 22.40.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/839f3bc6-3e06-b771-8440-f9a907bf9442.png)

ログインできました！

# 新規投稿ページの作成

# まずはルーティング

では、次に新規投稿ページを設定していきましょう。
まずはweb.phpを開き、こちらを追加します。

```php:web.php
Route::get('/post/create', [App\Http\Controllers\PostController::class, 'create'])->name('create');
```

localhost:9999/post/createにアクセスすると、PostControllerのcreateメソッドを呼び出す設定になります。
name('create')とすることで、任意のnameを設定できますが、これは後ほどコントローラーやビューで活かされます。

# 次はコントローラー

PostController.phpファイルを作成します。

```php:ターミナル
php artisan make:controller PostController --resource --model=Post
```

--resource オプションを入れることで PostControllerに予めCRUD処理を行う関数が作成されます。
--modelはこのコントローラーで操作するModelを指定します。投稿(Post)を扱うため Postモデルを指定します。

PostControllerを開いてみましょう。
下記のように記述されているはずです。

```php:PostController.php
<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function show(Post $post)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function edit(Post $post)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Post $post)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function destroy(Post $post)
    {
        //
    }
}

```

# createメソッド編集
試しに下記のようにcreateメソッドの箇所を編集して、

```php:PostController.php
public function create()
    {
        {
            return '新規投稿ページです';
        };
    }
```

localhost:9999/post/createにアクセスしてみてください。↓
![スクリーンショット 2021-12-04 23.30.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/8124de1a-62db-4b10-9372-1a15eb37a4ad.png)

後ほど、入力フォームを追加していきます。

# indexメソッド
次にindexメソッドを編集して、投稿が一覧表示されるようにしていきます。

# まずはルーティングから

```php:web.php
Route::get('/post/index', [App\Http\Controllers\PostController::class, 'index'])->name('index');
```

# 次にコントローラー

Postモデルを使いたいので、頭のほうで

```php:PostController.php
use App\Model\Post;
```

としておいてください。

```php:PostController.php
public function index()
    {
        $query = Post::query();

        //全件取得
        $posts = $query->get();

        //ページネーション
        $posts = $query->orderBy('id','desc')->paginate(10);

　　　　　//post/index.blade.phpに$postsという変数を渡す場合
        return view('post/index', ['posts' => $posts]);
    }
```

view(ビューファイル名,ビューに渡したいもの)とすることでコントローラからビューへ指示を出すことができます。
postsがビューで使うときの変数名、$postsはコントローラ内で定義した変数になります。
コントローラの中では、ビューで使う用の変数名に＄をつけませんが、viewファイル内(bladeファイル)では変数の＄をつけたものを使います。ややこしいので注意！

下記のような書き方も可能です。

```php:PostController.php
public function index()
    {
        $query = Post::query();

        //全件取得
        $posts = $query->get();

        //ページネーション
        $posts = $query->orderBy('id','desc')->paginate(10);

　　　　　//post/index.blade.phpに$postsという変数を渡す場合
        return view('post.index')->with('posts',$posts);
    }
```


一覧だけなら、posts=Post::all();とかでもいいのですが、ここではqueryオブジェクトを生成して対応しています（その理由は検索機能の実装等で便利だからですが、ここでは触れません）。

またせっかくなので、orderByで最新登録が先頭に来るようにしているのと、10行毎にページ処理をしています。

取得したデータはcompact()等で返してもいいのですが、わかりやすく-with()を利用しています。

view('view名')-with('viewでの変数名','実データ'); という形式になります。

# ビューの編集

postディレクトリを作成して、その中にindexファイルを作成します。
下記のように編集してみましょう。

```php:post/index.blade.php
@extends('layouts.app')
@section('content')
   <h1>一覧表示</h1>

    <table class="table table-striped">
    @foreach($posts as $post)
        <tr>
            <td>{{$post->title}}</td>
            <td>{{$post->body}}</td>
        </tr>
    @endforeach
    </table>

    <!-- page control -->
    {!! $posts->render() !!}
@endsection
```

localhost:9999/post/indexを開いてみましょう。
予めDBに登録しておいたpostデータが表示されたはずです。

# 投稿の保存
ここからは、入力フォームの内容をDBに保存できるように実装していきたいと思います。
▼参考記事
https://nodoame.net/archives/11612



# まずはルーティングから

```web.php
Route::post('/post/store', [App\Http\Controllers\PostController::class, 'store'])->name('store');
```
※storeメソッドはPOST送信になります。


# コントローラー

createメソッドとstoreメソッドを使用することになりますが、storeメソッドの方は最も重要な箇所です。
まずはcreateメソッド。

```php:PostController.php
public function create()
    {
        //createに転送
        return view('post.create');
    }
```

基本的に、postディレクトリのcreate.blade.phpファイルに処理を転送しているだけになります。


そして、store。
createが投げてきた値を受け取り、DBに保存。そして、一覧表示へリダイレクトさせています。

```php:PostController.php
public function store(Request $request)
    {
        //postオブジェクト生成
        $post = Post::create();

        //値の登録。
　　　　 //右側はviewのnameから持ってきたもの
　　　　 //左側はモデルのカラム名
        $post->title = $request->title;
        $post->body = $request->body;

        //保存
        $post->save();

        //一覧にリダイレクト
        return redirect()->to('/post/index');
    }
```

# 入力フォームを作って見た目を整える
createアクション用のビューを作っていきます。
post/create.blade.phpを作成し、下記のように編集してみましょう。

```php:post/create.blade.php
@extends('layouts.app')

@section('content')

    <h1>新規投稿</h1>

    <div class="row">
        <div class="col-sm-12">
            <a href="/post/index" class="btn btn-primary" style="margin:20px;">一覧に戻る</a>
        </div>
    </div>

    <!-- form -->
    <form method="post" action="/post/store">

        <div class="form-group">
            <label>タイトル</label>
            <input type="string" name="title" value="" class="form-control">
        </div>

        <div class="form-group">
            <label>本文</label>
            <input type="text" name="body" value="" class="form-control">
        </div>

        <input type="hidden" name="_token" value="{{csrf_token()}}">

        <input type="submit" value="投稿" class="btn btn-primary">

    </form>

@stop
```

post先(action)はstore(/post/store)。methodはpost。
hiddenでLaravelでpostするときに原則必要となるcsrf_tokenを送っています。




▼参考記事

https://qiita.com/zaburo/items/9fefa3f6834b2e79b734#%E3%83%93%E3%83%A5%E3%83%BC-1



動作確認として、入力フォームに値を入れて投稿してみます。
残念。エラーです。

![スクリーンショット 2021-12-05 13.56.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/ff60351d-538d-dfb2-0ae8-c3ecfe824edf.png)

解決するためには、DBの厳密モードを変更する必要があります。無効にするには、以下の手順に従ってください。

```
①Config/database.phpを開きます
②find 'strict'値をtrueからfalseに変更して、再試行します
```

これで無事にDBに入力フォームの値が保存されました。

今回の記事ではリレーションの設定については割愛しました。
また別の記事にて投稿したいと思います。

-------------------------------------------
# その他学習したこと

フォームから受け取った値を一度加工してからDBに格納するためにはEloquentが必要。

#### Eloquentとは
DB のデータを操作する機能のこと。
▼参考記事
https://laraweb.net/knowledge/2324/


DBに保存する方法として、
fillメソッドとsaveメソッドがありますが、
メソッドチェーンで連結しています。

#### fillメソッド
実行するSQL文のプレースホルダに値をバインドする処理
#### saveメソッド
insertする処理
です。

#### プレースホルダとは
実際の内容を後から挿入するために、とりあえず仮に確保した場所のこと。 また、そのことを示す標識などのこと。

#### バインドとは
束縛（する）、拘束（する）、結びつける、関連付ける、などの意味を持つ英単語。 ITの分野では、何らかの要素やデータ、ファイルなどが相互に関連付けられている状態や、そのような状態を実現する機能などのことを指すことが多い。