# 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> ```