---
# System prepended metadata

title: 勉強会

---

# 勉強会

# 今回作るプロジェクトの概要
## 使用技術
Docker
Laravel
MySQL
Blade
Livewire
Jetstream
Tailwind

# 環境構築

以下サイトからDocker-Desctopをインストール
https://www.docker.com/get-started/

gitからコードを取得
```
$ git clone https://github.com/murata0531/Laravel-CI.git
```

Dockerを使用してアプリケーションを立ち上げる
```
$ docker-compose up -d --build
```

Dockerコンテナが立ち上がっているか確認する
```
$ docker ps
```

Apacheコンテナに入る
```
$ docker-compose run --rm app bash
```

appディレクトリに入り、Laravelのファイルがあることを確認する
```
$ cd app && ls
```

Laravelを動かすための初期設定
```
$ cp .env.example .env

$ composer install

$ php artisan key:generate

```

# アプリケーションを作成する
## 機能
タスクをCRUDするだけ
## テーブル構成

Tasksテーブル

| 列名 | 型 | 概要 |
| -------- | -------- | -------- |
| id     | int    | タスクを特定する主キー     |
| name | varchar | タスクの名前 |
| description | text | タスクの詳細 |
| status| int | statusテーブルへの外部キー |
| created_at | timestamp | 作成日時 |
| updated_at | timestamp | 更新日時 |

statusesテーブル
| 列名 | 型 | 概要 |
| -------- | -------- | -------- |
| id     | int    | 状態を特定する主キー     |
| name | varchar | 状態の名前 |

statusesテーブルに投入するパラメータ
| id | name |
|----|------|
| 1 | 未処理 |
| 2 | 処理中 |
| 3 | 完了 |

statusesテーブルとtasksテーブルはstatus_idを基準に1対多のリレーションシップを張る

## 今回利用するLaravelのテーブルでのデータ型の扱い
| MySQL | Laravel | 役割 |
|-------|---------|-----|
| unsigned bigInt Auto Increment primary | unsignedBigInteger primary、id | 主キー |
| int | integer | 整数 |
| varchar | string | 文字列 |
| timestamp | timestamp | 日付時刻 |

今回のgitブランチモデル

| ブランチ名 | 役割 |
| -------- | -------- |
| mein     | リリース用    |
| develop | 開発時のマスター |
| feature | 機能ごとに分けたブランチ |

## ここから作業
作業用のブランチを作成し、現在のブランチを作成したブランチに変更
```
$ git checkout -b feature/create_tables
```

## テーブルを作成
Apacheコンテナに入る
```
$ docker-compose run --rm app bash

$ cd app
```
テーブル作成用のマイグレーションファイルとモデルを作成する
```
$ php artisan make:model -m Status
```

上記のコマンドを入力すると、
` database/migrations`フォルダに`2022_08_11S_145315_create_statuses_table.php`ファイル、
`app/Models`フォルダに`Status.php`ファイルが作成されている


```
$ php artisan make:model -m Task
```

上記のコマンドを入力すると、
` database/migrations`フォルダに`2022_08_10_145315_create_tasks_table.php`ファイル、
`app/Models`フォルダに`Task.php`ファイルが作成されている

作成された`create_statuses.php`ファイルに以下を記述
```php
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('statuses', function (Blueprint $table) {
            $table->id();
            $table->string('name')->comment('状態');
            $table->timestamps();
        });
    }

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

```
作成された`create_tasks.php`ファイルに以下を記述
```php
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->id();
            $table->string('name')->comment('タスク名');
            $table->text('description')->comment('詳細');
            $table->unsignedBigInteger('status_id')->default(1)->comment('タスクの状態');
            $table->timestamps();

            $table->foreign('status_id')->references('id')->on('statuses');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('tasks');
    }
};
```
作成された`Status.php`ファイルに以下を記述
```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Status extends Model
{
    use HasFactory;

    protected $guarded = [];

    public function tasks() {
        return $this->hasOne('App\Models\Task');
    }
}

```
作成された`Task.php`ファイルに以下を記述
```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    use HasFactory;

    protected $guarded = [];

    public function statuses() {
        return $this->belongsTo('App\Models\Status');
    }
}
```

以上の作業が終わったらテーブルを作成する
```
$ php artisan migrate
```
データベースを確認する
新しくターミナルを立ち上げて以下のコマンドでdbコンテナに入る
```
$ docker-compose exec db bash
```
コンテナ内のMySQLに入る
```
$ mysql -u test -p
```
パスワードを聞かれるので「test」と入力

MySQLに入ったら
以下のコマンドで`statusesテーブル`と`tasksテーブル`の構造を確認
```
$ use test

$ desc statuses;

$ desc tasks;
```

gitにコミットしておく
ファイル名の「2022_08_11_112051」は人によって違うので読み変えること
```
$ git add src/app/database/migrations/2022_08_11_112051_create_statuses_table.php

$ git commit -m "statusesテーブルを追加"

$ git add src/app/database/migrations/2022_08_11_112051_create_tasks_table.php

$ git commit -m "tasksテーブルを追加"

$ git add src/app/app/Models/Status.php 

$ git commit -m "Statusモデルを追加"

git add src/app/app/Models/Task.php 

$ git commit -m "Taskモデルを追加"

$ git push origin feature/create_tables
```

git hubを覗くと緑色のボタンが出てるのでプルリクエストを送り、レビューする
レビューが終わったらマージする

gitのローカルブランチを掃除
```
$ git checkout develop

$ git branch -D feature/create_tables
```
リモートリポジトリから変更したコードを取得
```
$ git pull origin develop
```
## statusesテーブルに初期データを投入
`feature/seeders`ブランチを作成し、入っておく
```
$ git checkout -b feature/seeders
```
StatusSeederを作成
```
$ php artisan make:seeder StatusSeeder
```
`database/seeders/StatusSeeder.php`が作成されているので以下を記述
```php
<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class StatusSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('statuses')->insert([
            [
                'id' => 1,
                'name' => '未処理'
            ],
            [
                'id' => 2,
                'name' => '処理中'
            ],
            [
                'id' => 3,
                'name' => '完了'
            ],
        ]);
    }
}

```
同じ階層にある`DatabaseSeeder.php`に以下を記述
```php
<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        // \App\Models\User::factory(10)->create();

        // \App\Models\User::factory()->create([
        //     'name' => 'Test User',
        //     'email' => 'test@example.com',
        // ]);
        $this->call(StatusSeeder::class);
    }
}

```
データベースにデータを投入
```
$ php artisan db:seed
```
データベースを確認したら3つデータが入ってるはず

追加したファイルをgitにコミットする
```
$ git add src/app/database/seeders/StatusSeeder.php

$ git commit -m "statusesテーブルにデータを追加する"

$ git add src/app/database/seeders/DatabaseSeeder.php

$ git commit -m "StatusSeederを追加"

$ git push origin feature/seeders
```
git hubを覗くと緑色のボタンが出てるのでプルリクエストを送り、レビューする
レビューが終わったらマージする

gitのローカルブランチを掃除
```
$ git checkout develop

$ git branch -D feature/seeders
```
リモートリポジトリから変更したコードを取得
```
$ git pull origin develop
```
## モデルを修正する
`fix/models`ブランチを作成し入る
```
$ git checkout -b fix/models
```

`app/Models/Status.php`を以下に修正
```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Status extends Model
{
    use HasFactory;
    protected $guarded = [];

    public function tasks() {
        return $this->hasMany('App\Models\Task');
    }
}
```
`app/Models/Task.php`を以下に修正
```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    use HasFactory;
    protected $guarded = [];

    public function statuses() {
        return $this->belongsTo('App\Models\Status', 'status_id');
    }
}
```

追加したファイルをgitにコミットする
```
$ git add src/app/app/Models/Status.php

$ git commit -m "hasManyに修正"

$ git add src/app/app/Models/Task.php

$ git commit -m "belongsToに修正、外部キーを指定"

$ git push origin fix/models
```
git hubを覗くと緑色のボタンが出てるのでプルリクエストを送り、レビューする
レビューが終わったらマージする

gitのローカルブランチを掃除
```
$ git checkout develop

$ git branch -D fix/models
```
リモートリポジトリから変更したコードを取得
```
$ git pull origin develop
```
## ページを作成する
`feature/tasks`ブランチを作成し入る
```
$ git checkout -b feature/tasks
```

Livewireコンポーネントを作成
```
$ php artisan make:livewire tasks
```
コマンドを入力すると以下のファイルが作成されている
`app/Http/Livewire/Tasks.php`
`resources/views/livewire/Tasks.blade.php`

`app/Http/Livewire/Tasks.php`に以下を記述
```php
<?php

namespace App\Http\Livewire;

use Livewire\Component;
use App\Models\Status;
use App\Models\Task;

class Tasks extends Component
{
    public $tasks, $name, $description, $task_id,$statuses;
    public $status_id = 1;
    public $isOpen=false;

    public function render()
    {
        $this->statuses = Status::with('tasks')->get();
        $this->tasks = Task::with('statuses')->get();
        
        return view('livewire.tasks');
    }

    public function mount($status_id = 1)
    {
        $this->status_id = $status_id;
        $this->onChange();
    }

    public function onChange()
    {
        $status_id= intval($this->status_id);
    }

    public function create()
    {
        $this->resetInputFields();
        $this->openModal();
    }

    public function openModal()
    {
        $this->isOpen = true;
    }

    public function closeModal()
    {
        $this->isOpen = false;
    }

    public function resetInputFields()
    {
        $this->name = '';
        $this->description = '';
        $this->task_id = null;
    }

    public function store()
    {
        $this->validate([
            'name' => 'required',
            'description' => 'required'
        ]);

        Task::updateOrCreate(['id' => $this->task_id], [
            'name' => $this->name,
            'description' => $this->description,
            'status_id' => $this->status_id
        ]);

        session()->flash('message', 
        $this->task_id ? 'Task Updated Successfully.' : 'Task Created Successfully.');
        $this->closeModal();
        $this->resetInputFields();
    }

    public function edit($id)
    {
        $task = Task::findOrFail($id);
        $this->task_id = $task->id;
        $this->name = $task->name;
        $this->description = $task->description;
        $this->stattus_id = $task->status_id;
        $this->openModal();
    }

    public function delete($id)
    {
        Task::find($id)->delete();
        session()->flash('message', 'Task Deleted Successfully.');
    }
}

```

`resources/views/livewire/tasks.blade.php`に以下を記述
```php
<x-slot name="header">
    <h2 class="font-semibold text-xl text-gray-800 leading-tight">
        ToDo App
    </h2>
</x-slot>
<div class="py-12">
    <div class="max-w-7x1 mx-auto sm:px-6 lg:px-8">
        <div class="bg-white overflow-hidden shadow-x1 sm rounded-lg px-4 py-4">
            @if(session()->has('message'))
                <div class="bg-teal-100 border-lt-4 border teal-500 rounded-b text-teal-900 px-4 py-3 shadow-md my-3" role="alert">
                    <div class="flex">
                        <div>
                            <p class="text-sm">{{ session('message') }}</p>
                        </div>
                    </div>
                </div>
            @endif
            <button wire:click="create()" class="bg-blue-500 hover bg-blue-700 text-white font-bold py-2 px-4 rounded my-3">Create Task</button>
            @if($isOpen)
            @include('livewire.create')
            @endif
            <table class="table-fixed w-full">
                <thead>
                    <tr class="bg-gray-1000">
                        <th class="px-4 py2 w-20">No.</th>
                        <th class="px-4 py-2">Name</th>
                        <th class="px-4 py-2">Desc</th>
                        <th lass="px-4 py-2">Status</th>
                        <th class="px-4 py-4">Action</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach($tasks as $task)
                        <tr>
                            <td class="border px-4 py-2">{{ $task->id }}</td>
                            <td class="border px-4 py-2">{{ $task->name }}</td>
                            <td class="border px-4 py-2">{{ $task->description }}</td>
                            <td class="border px-4 py-2">{{ $task->statuses->name }}</td>
                            <td class="border px-4 py2">
                                <button wire:click="edit({{ $task->id }})" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Edit</button>
                                <button wire:click="delete({{ $task->id }})" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Delete</button>
                            </td>
                        </tr>
                    @endforeach
                </tbody>
            </table>
        </div>
    </div>
</div>
```
新しく`resources/views/livewire/`フォルダに `create.blade.php`ファイルを作成し、以下を記述
```php
<div class="fixed z-10 inset-0 overflow-y-auto ease-out duration-400">
    <div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
        <div class="fixed inset-0 tansition-opacity">
            <div class="absolute inset-0 bg-gray-500 opacity-75"></div>
        </div>
        <span class="hidden sm:inline-block sm:aligh-middle sm:h-screen"></span>
        <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" role="dialog" aria-modal="true" aria-labelledby="modal-headline">
            <form>
                <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
                    <div class="">
                        <div class="mb-4">
                            <label for="name" class="block text-gray-700 text-sm font-bold mb-2">name</label>
                            <input type="text" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="name" placeholder="Enter name" wire:model="name">
                            @error('name') <span class="text-red-500">{{ $message }}</span>@enderror       
                        </div>
                        <div class="mb-4">
                            <label for="description" class="block text-gray-700 text-sm font-bold mb-2">Description</label>
                            <textarea  id="description" cols="30" rows="10" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="Enter Description" wire:model="description"></textarea>
                            @error('description') <span class="text-red-500">{{ $message }}</span>@enderror       
                        </div>
                        <div class="mb-4">
                            <label for="status_id" class="block text-gray-700 text-sm font-bold mb-2">状態</label>
                            <select  id="status_id" cols="30" rows="10" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
                                name="status_id" wire:model="status_id" wire:change="onChange">
                                @foreach ($statuses as $status)
                                <option value="{{ $status->id }}">{{ $status->name }}
                                @endforeach
                            </select>
                        </div>
                    </div>
                </div>
                <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
                    <span class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
                        <button wire:click.prevent="store()" type="button" class="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 bg-green-600 text-base leading-6 font-medium text-white shadow-sm hover:bg-green-500 focus:outline-none focus:border-green-700 focus:shadow-outline-green transition ease-in-out duration-150 sm:text-sm sm:leading-5">
                            Save
                        </button>
                    </span>
                    <span class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
                        <button wire:click="closeModal()" type="button" class="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 bg-green-600 text-base leading-6 font-medium text-white shadow-sm hover:bg-green-500 focus:outline-none focus:border-green-700 focus:shadow-outline-green transition ease-in-out duration-150 sm:text-sm sm:leading-5">
                            Cancel
                        </button>
                    </span>
                </div>
            </form>
        </div>
    </div>
</div>
```

最後にルートを追加する
`routes/web.php`に以下を記述
```php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Livewire\Tasks;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Route::middleware([
    'auth:sanctum',
    config('jetstream.auth_session'),
    'verified'
])->group(function () {
    Route::get('/dashboard', function () {
        return view('dashboard');
    })->name('dashboard');
});

Route::get('tasks', Tasks::class);
```

新しくターミナルを立ち上げてLaravelのディレクトリまで移動
```
$ cd src/app
```
vite(JavaScriptの実行環境)を動かす
```
$ npm install && npm run dev
```

以上の作業が終わったらブラウザから`localhost`にアクセス
画面右上のregisterからユーザ登録後、`localhost/tasks`を開く

タスクの確認・作成・編集・削除が正しく行えることを確認する

追加したファイルをgitにコミットする
```
$ git add src/app/resources/views/livewire/tasks.blade.php

$ git commit -m "タスク確認ページを追加"

$ git add src/app/resources/views/livewire/create.blade.php

$ git commit -m "タスク編集フォームを追加"

$ git add src/app/routes/web.php

$ git commit -m "ルートを追加"

$ git push origin feature/tasks
```
git hubを覗くと緑色のボタンが出てるのでプルリクエストを送り、レビューする
レビューが終わったらマージする

gitのローカルブランチを掃除
```
$ git checkout develop

$ git branch -D fix/models
```
リモートリポジトリから変更したコードを取得
```
$ git pull origin develop
```
# git 

多く使用されているブランチモデル

| ブランチ名 | 役割 |
| -------- | -------- |
| mein     | リリース用    |
| develop | 開発時のマスター |
| feature | 機能ごとに分けたブランチ |


リモートブランチを確認する
```
$ git remote -v
```
全てのブランチを確認する
```
$ git branch -a
```

新しくfeature/xxxブランチを作成して切り替える
```
$ git checkout -b feature/xxx
```
現在のブランチを確認する
```
$ git branch
```

コードを書く

ファイルをステージングさせる
```
$ git add ファイル名
```
コミット
```
$ git commit -m "コメント"
```
作成したブランチをリモートにプッシュする
```
$ git push origin feature/xxx
```


githubを確認するとプルリクを送る旨のメッセージが表示されているのでプルリクエストを送る

無事マージされたら以下を実行しブランチを削除する
```
$ git checkout develop

$ git branch -D feature/xxx
```

もしリモートが変更されていたら、
```
$ git pull origin develop
```
でコードをローカルに追加する



コンテナ破棄
```
$ docker-compose down --rmi all --volumes --remove-orphans
```

# 今回のリポジトリ
https://github.com/murata0531/Laravel-CI