# Laravel_02
### 授業資料ダウンロードリンク
[Googleドライブ](https://drive.google.com/drive/folders/1PVyoulX1_TY-mPQaN4SEnNGUpzpRvagu?usp=sharing)
## 環境構築
過去資料確認
## Laravelのプロジェクト立ち上げ
### 【Laravelの起動】
#### 1. サーバーの起動とLaravelプロジェクトの起動チェック
#ディレクトリ移動
`cd cms`
#BuiltInサーバーを起動:動作確認
`php artisan serve --port=8080`
*Cloud9の画面上部の緑の起動ボタンの左側Previewボタン>Preview Running Applicationをクリック
右下に画面が起動してLaravelの文字が確認できたらOK
## リレーション(1対多)
### 1人のログインユーザーは複数のチームを作成してオーナーになることができる機能
#### 1 artisanコマンドでマイグレーション 作成
`php artisan make:migration create_teams_table --create=teams`
#### 2 database/migrationsの直下のteams_table.phpに以下追記
```
public function up()
{
Schema::create('teams', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('team_name');
$table->integer('user_id');
$table->timestamps();
});
}
```
#### 3 マイグレーション実行(テーブルの作成)
`php artisan migrate`
### モデルの作成(リレーションの設定)
#### 1: artisanコマンドでモデル作成
`php artisan make:model Team`
#### 1 App/User.phpの中の39行目を改行して以下の内容をコピー
```
// Teamsテーブルとのリレーション (主テーブル側)
public function o_teams() {
return $this->hasMany('App\Models\Team');
}
```
#### 2 App/Team.phpの中の9行目を改行して以下の内容をコピー
```
// Userテーブルとのリレーション (従テーブル側)
public function user() {
return $this->belongsTo('App\Models\User');
}
```
### チーム作成画面の作成
#### 1 viewsの直下にteams.blade.phpを作成して以下の内容をコピペ
```
<!-- resources/views/books.blade.php -->
<x-app-layout>
<!--ヘッダー[START]-->
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
<form action="" method="GET" class="w-full max-w-lg">
<x-button class="bg-gray-100 text-gray-900">{{ __('Dashboard') }}</x-button>
</form>
</h2>
</x-slot>
<!--ヘッダー[END]-->
<!-- バリデーションエラーの表示に使用-->
<x-errors id="errors" class="bg-blue-500 rounded-lg">{{$errors}}</x-errors>
<!-- バリデーションエラーの表示に使用-->
<!--全エリア[START]-->
<div class="flex bg-gray-100">
<!--左エリア[START]-->
<div class="text-gray-700 text-left px-4 py-4 m-2">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-500 font-bold">
チームを管理する
</div>
</div>
<!-- チームのフォーム -->
<form action="{{ url('teamadd') }}" method="POST" class="w-full max-w-lg">
@csrf
<div class="flex flex-col px-2 py-2">
<!-- カラム1 -->
<div class="w-full md:w-1/1 px-3 mb-2 md:mb-0">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2">
チーム名
</label>
<input name="team_name" class="appearance-none block w-full text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" type="text" placeholder="">
</div>
</div>
<!-- カラム5 -->
<div class="flex flex-col">
<div class="text-gray-700 text-center px-4 py-2 m-2">
<x-button class="bg-blue-500 rounded-lg">送信</x-button>
</div>
</div>
</form>
</div>
<!--左エリア[END]-->
<!--右側エリア[START]-->
<div class="flex-1 text-gray-700 text-left px-4 py-2 m-2">
<!-- チームの一覧 -->
<div class="flex flex-col">
<div class="-m-1.5 overflow-x-auto">
<div class="p-1.5 min-w-full inline-block align-middle">
<div class="border rounded-lg shadow overflow-hidden dark:border-gray-700 dark:shadow-gray-900">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase dark:text-gray-400">チーム名</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase dark:text-gray-400">オーナー</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase dark:text-gray-400">参加人数</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase dark:text-gray-400">詳細</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase dark:text-gray-400">参加</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase dark:text-gray-400">編集</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
<x-content></x-content>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!--右側エリア[[END]-->
</div>
<!--全エリア[END]-->
</x-app-layout>
```
#### 2. /resources/views/components/content.blade.php を作成し以下の内容をコピー
content.blade.php
```
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-200"></td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-200"></td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800 dark:text-gray-200">人参加中</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<a class="text-blue-500 hover:text-blue-700" href="#">詳細</a>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<a class="text-blue-500 hover:text-blue-700" href="#">参加</a>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<a class="text-blue-500 hover:text-blue-700" href="#">編集</a>
</td>
</tr
```
#### 5. コンポートネントをビルド
`npm run build`
### 投稿画面表示処理の実装
#### 1 teamsコントローラーを作成
`php artisan make:controller TeamController`
#### 2 teamsコントローラー内にモデルの呼び出しをした上でindexメソッドに投稿表示処理を記述
```
use App\Models\Team; //この行を上に追加
use App\Models\User;//この行を上に追加
use Auth;//この行を上に追加
use Validator;//この行を上に追加
public function index()
{
//
return view('teams');
}
```
#### 3 web.phpにルーティングを追記
```
use App\Http\Controllers\TeamController;//追記
//チーム一覧表示
Route::get('teams', [TeamController::class, 'index'])->middleware(['auth']);
```
### チーム登録処理の実装
#### 1 web.phpにルーティングを追記
`Route::post('teamadd', [TeamController::class, 'store']);`
#### 2 teamsコントローラー内のstoreメソッドに登録処理を記述
```
public function store(Request $request)
{
//バリデーション
$validator = Validator::make($request->all(), [
'team_name' => 'required|max:255'
]);
//バリデーション:エラー
if ($validator->fails()) {
return redirect('teams')
->withInput()
->withErrors($validator);
}
//以下に登録処理を記述(Eloquentモデル)
$teams = new Team;
$teams->team_name = $request->team_name;
$teams->user_id = Auth::id();//ここでログインしているユーザidを登録しています
$teams->save();
return redirect('teams');
}
```
### チームの一覧表示処理を実装
#### 1 teams.blade.php内72~73行目に以下を記述して表示エリア作成
```
<!-- チームの一覧 -->
==省略==
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
@if (count($teams) > 0)
@foreach ($teams as $team)
<x-content :team="$team"></x-content>
@endforeach
@endif
</tbody>
```
#### 2 teamコントローラー内のindexメソッドを以下のように変更して投稿を取得する
```
public function index()
{
//チーム 全件取得
$teams = Team::get();
return view('teams',[
'teams'=> $teams
]);
}
```
#### 3 content.blade.php内2~3行目を以下のように変更してチームメ名とオーナー名を表示
```
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-200">{{ $team->team_name }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-200">{{ $team->user->name }}</td>
```
## リレーション(多対多)チーム参加機能
### データベースに中間テーブルを作成する為にマイグレーションを作成
#### 1 artisanコマンドでマイグレーション 作成
`php artisan make:migration create_team_user_table --create=team_user`
#### 2 database/migrationsの直下のpost_user_table.phpに以下追記
```
public function up()
{
Schema::create('team_user', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('team_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); //外部キー参照
$table->foreign('team_id')->references('id')->on('teams')->onDelete('cascade'); //外部キー参照
$table->unique(['user_id', 'team_id'],'uq_roles'); //Laravelは複合主キーが扱いにくいのでユニークで代用
$table->timestamps();
});
}
```
#### 3 マイグレーション実行(テーブルの作成)
`php artisan migrate`
### モデルにリレーション設定
#### 1 App/User.phpに以下の内容を追記
```
// Teamsテーブルとの多対多リレーション
public function my_teams() {
return $this->belongsToMany('App\Models\Team');
}
```
#### 2 App/Team.phpに以下の内容を追記
```
// Userテーブルとの多対多リレーション
public function members() {
return $this->belongsToMany('App\Models\User');
}
```
### チーム参加ボタン&参加処理実装
#### 1 collection.blade.phpの9行目チーム参加ボタンのtdタグ内に以下をコピペ
```
<a class="text-blue-500 hover:text-blue-700" href="{{ url('team/'.$team->id) }}">参加</a>
```
#### 2 web.phpにルーティングを追加
`Route::get('team/{team_id}', [TeamController::class, 'join']);`
#### 3 teamsコントローラーに新規でメソッドを作成
```
public function join($team_id)
{
}
```
#### 4 joinメソッドに以下に処理を追記
```
public function join($team_id)
{
//ログイン中のユーザーを取得
$user = Auth::user();
//参加するチームを取得
$team = Team::find($team_id);
//リレーションの登録
$team->members()->attach($user);
return redirect('teams');
}
```
#### 5 content.blade.phpの所属人数の部分に以下のように記述
```
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800 dark:text-gray-200">{{ $team->members()->count() }}人参加中</td>
```
#### 6 参加ボタンの表示を切り分けたいのでcollection.blade.phpのボタンエリアを以下のように変更
```
@if(Auth::check())
@if(Auth::id() != $team->user_id && $team->members()->where('user_id',Auth::id())->exists() !== true)
<a class="text-blue-500 hover:text-blue-700" href="{{ url('team/'.$team->id) }}">参加</a>
@endif
@endif
```
#### 7 teamコントローラーのstoreメソッドのリダイレクト処理の直前に以下の処理を追記
```
//多対多のリレーションもここで登録
$teams->members()->attach( Auth::user() );
```
## チーム編集機能
#### 1 contact.blade.phpのチーム編集ボタンのtd内に以下を記述
```
@if(Auth::check()&& Auth::id() == $team->user_id )
<a class="text-blue-500 hover:text-blue-700" href="{{ url('teamedit/'.$team->id) }}">編集</a>
@endif
```
#### 2 web.phpにルーティングを作成
`Route::get('teamedit/{team}', [TeamController::class, 'edit']);
`
#### 3 teamsコントローラーにeditメソッドを作成して以下の処理を作成
```
//チーム編集画面表示
public function edit (Team $team) {
return view('teamedit', ['team' => $team]);
}
```
#### 4 teamedit.blade.phpを作成して編集画面を作成
```
<!-- resources/views/teamedit.blade.php -->
<x-app-layout>
<!--ヘッダー[START]-->
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
<form action="" method="GET" class="w-full max-w-lg">
<x-button class="bg-gray-100 text-gray-900">{{ __('Dashboard') }}</x-button>
</form>
</h2>
</x-slot>
<!--ヘッダー[END]-->
<!-- バリデーションエラーの表示に使用-->
<x-errors id="errors" class="bg-blue-500 rounded-lg">{{$errors}}</x-errors>
<!-- バリデーションエラーの表示に使用-->
<!--全エリア[START]-->
<div class="flex bg-gray-100">
<!--左エリア[START]-->
<div class="text-gray-700 text-left px-4 py-4 m-2">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-500 font-bold">
チームを管理する
</div>
</div>
<!-- 本のタイトル -->
<form action="{{ url('team/update') }}" method="POST" class="w-full max-w-lg">
@csrf
<div class="flex flex-col px-2 py-2">
<!-- カラム1 -->
<div class="w-full md:w-1/1 px-3 mb-2 md:mb-0">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2">
チーム名
</label>
<input name="team_name" class="appearance-none block w-full text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" type="text" placeholder="" value="{{$team->team_name}}">
</div>
</div>
<!-- id 値を送信 -->
<input type="hidden" name="id" value="{{$team->id}}" /> <!--/ id 値を送信 -->
<!-- カラム5 -->
<div class="flex flex-col">
<div class="text-gray-700 text-center px-4 py-2 m-2">
<x-button class="bg-blue-500 rounded-lg">送信</x-button>
</div>
</div>
</form>
</div>
<!--左エリア[END]-->
</div>
<!--全エリア[END]-->
</x-app-layout>
```
#### 5 ルーティングをweb.phpに記述
```
//チーム更新処理
Route::post('team/update', [TeamController::class, 'update']);
```
#### 6 更新処理をteamコントローラーに作成
```
//更新処理
public function update (Request $request) {
//バリデーション
$validator = Validator::make($request->all(), [
'team_name' => 'required|max:255',
]);
//バリデーション:エラー
if ($validator->fails()) {
return redirect('/')
->withInput()
->withErrors($validator);
}
//対象のチームを取得
$team = Team::find($request->id);
$team->team_name = $request->team_name;
$team->save();
return redirect('/');
}
```
### チーム詳細機能
#### 1 web.phpにルーティングを作成
```
// チーム詳細表示
Route::get('teams/{team}', [TeamController::class, 'show']);
```
#### 2 コントローラーに処理を追記
```
//詳細表示
public function show(Team $team)
{
return view('teamdetail',[
'team'=> $team
]);
}
```
#### 3 teamdetail.blade.phpを作成して以下をコピペ
```
<!-- resources/views/books.blade.php -->
<x-app-layout>
<!-- Team -->
<div class="max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 mx-auto">
<!-- Title -->
<div class="max-w-2xl mx-auto text-center mb-10 lg:mb-14">
<h2 class="text-2xl font-bold md:text-4xl md:leading-tight dark:text-white">{{ $team->team_name}}メンバー一覧</h2>
<p class="mt-1 text-gray-600 dark:text-gray-400">Creative people</p>
</div>
<!-- End Title -->
<!-- Grid -->
<div class="grid grid-cols-2 lg:grid-cols-3 gap-8 md:gap-12">
@if ($team)
@foreach ($team->members as $member)
<div class="grid sm:flex sm:items-center gap-y-3 gap-x-4">
<img class="rounded-lg w-20 h-20" src="https://images.unsplash.com/photo-1568602471122-7832951cc4c5?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=2&w=900&h=900&q=80" alt="Image Description">
<div class="sm:flex sm:flex-col sm:h-full">
<div>
<h3 class="font-medium text-gray-800 dark:text-gray-200">
{{ $member->name }}
</h3>
<p class="mt-1 text-xs uppercase text-gray-500">
メンバー
</p>
</div>
<!-- Social Brands -->
<div class="mt-2 sm:mt-auto space-x-2.5">
<a class="inline-flex justify-center items-center text-gray-500 hover:text-gray-800 dark:hover:text-gray-200" href="#">
<svg class="w-3.5 h-3.5" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"/>
</svg>
</a>
<a class="inline-flex justify-center items-center text-gray-500 hover:text-gray-800 dark:hover:text-gray-200" href="#">
<svg class="w-3.5 h-3.5" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
</svg>
</a>
<a class="inline-flex justify-center items-center text-gray-500 hover:text-gray-800 dark:hover:text-gray-200" href="#">
<svg class="w-3.5 h-3.5" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M3.362 10.11c0 .926-.756 1.681-1.681 1.681S0 11.036 0 10.111C0 9.186.756 8.43 1.68 8.43h1.682v1.68zm.846 0c0-.924.756-1.68 1.681-1.68s1.681.756 1.681 1.68v4.21c0 .924-.756 1.68-1.68 1.68a1.685 1.685 0 0 1-1.682-1.68v-4.21zM5.89 3.362c-.926 0-1.682-.756-1.682-1.681S4.964 0 5.89 0s1.68.756 1.68 1.68v1.682H5.89zm0 .846c.924 0 1.68.756 1.68 1.681S6.814 7.57 5.89 7.57H1.68C.757 7.57 0 6.814 0 5.89c0-.926.756-1.682 1.68-1.682h4.21zm6.749 1.682c0-.926.755-1.682 1.68-1.682.925 0 1.681.756 1.681 1.681s-.756 1.681-1.68 1.681h-1.681V5.89zm-.848 0c0 .924-.755 1.68-1.68 1.68A1.685 1.685 0 0 1 8.43 5.89V1.68C8.43.757 9.186 0 10.11 0c.926 0 1.681.756 1.681 1.68v4.21zm-1.681 6.748c.926 0 1.682.756 1.682 1.681S11.036 16 10.11 16s-1.681-.756-1.681-1.68v-1.682h1.68zm0-.847c-.924 0-1.68-.755-1.68-1.68 0-.925.756-1.681 1.68-1.681h4.21c.924 0 1.68.756 1.68 1.68 0 .926-.756 1.681-1.68 1.681h-4.21z"/>
</svg>
</a>
</div>
<!-- End Social Brands -->
</div>
</div>
<!-- End Col -->
@endforeach
@endif
</div>
<!-- End Grid -->
</div>
<!-- End Team -->
</x-app-layout>
```
#### 4 詳細ボタンをcollection.blade.phpのtdタグ内に追加
```
<a class="text-blue-500 hover:text-blue-700" href="{{ url('teams/'.$team->id) }}">詳細</a>
```
## 複数オーナー対応(中間テーブルにデータを持たせるWithPivot)
#### 1 カラム追加用のマイグレーションを作成
`php artisan make:migration add_role_to_team_user_table --table=team_user`
#### 1 中間テーブルにカラムを追加
```
public function up()
{
Schema::table('team_user', function (Blueprint $table) {
$table->string('role')->default('member');//<-これを追加しています!!!
});
}
```
#### 2 migration refresh(テーブル再構築:データ全部一回消えます)
`php artisan migrate`
#### 3 TeamControllerのstoreメソッドの部分のアタッチを変更
```
上は省略
//以下に登録処理を記述(Eloquentモデル)
$teams = new Team;
$teams->team_name = $request->team_name;
$teams->user_id = Auth::id();//ここでログインしているユーザidを登録しています
$teams->save();
//多対多のリレーションもここで登録
$teams->members()->attach( Auth::user(),['role'=>'owner'] );//<-ここが変わってるよ!!
return redirect('/');
```
#### 4 モデルの中でwithPivotの設定をする
##### App/Models/User.phpの多対多リレーションを変更
`->withPivot('カラム名');`をつけてあげる
```
// Teamsテーブルとの多対多リレーション
public function my_teams() {
return $this->belongsToMany('App\Models\Team')->withPivot('role');
}
```
##### App/Models/Team.phpの多対多リレーションを変更
```
// Userテーブルとの多対多リレーション
public function members() {
return $this->belongsToMany('App\Models\User')->withPivot('role');
}
```
#### 5 teamdetail.blade.phpの26~28行目の表示部分を変更!
```
<h3 class="font-medium text-gray-800 dark:text-gray-200">
{{ $member->name }}
</h3>
<p class="mt-1 text-xs uppercase text-gray-500">
{{ $member->pivot->role}}//ここが変更
</p>
```