# 勉強会
# 今回作るプロジェクトの概要
## 使用技術
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