Laravel共同開発を終えて

# 目次

[1.はじめに](#1はじめに)
[2.共同開発概要](#2共同開発概要)
[3.成果物](#3成果物)
[4.タスク管理](#4タスク管理)
[5.gitFlow](#5gitflow)
[6.開発](#6開発)
[7.テスト](#7テスト)
[8.デプロイ](#8デプロイ)
[9.共同開発における反省点](#9共同開発における反省点)
[10.共同開発で学んだこと](#10共同開発で学んだこと)
[11.工夫したこと](#11工夫したこと)
[12.まとめ](#12まとめ)


# 1.はじめに
1ヶ月間phpの基礎学習+2ヶ月間Laravelの基礎学習をした。その後、Laravel共同開発に参加した。この記事を書いている意図としては共同開発で学んだことのアウトプットとこれからエンジニアを目指す人の参考になればと思い記事を執筆した。

# 2.共同開発概要
・開発期間：２ヶ月（2023年9月15日〜2023年11月15日)
・全体MTG/週1回 チームメンバ-とのMTG/週1回　作業会/週1回
・講師2名(現役プログラマー)　
・チームメンバー(未経験3人)

# 3.成果物
特定の話題についてチャットできるウェブサイトの実装
下記URL参照

# 4.タスク管理
 Googleスプレッドシートにて管理。下記画像参照。
 毎週のミーティングでチームメンバーの進捗状況の確認を行う。
 開発期間が2ヶ月のため期限から逆算して開発を行なっていた。
 チームメンバーのタスクが遅れている時はお互いに助け合いながらエラーを解決していた。
 この時に自分も一緒になってエラー解決に取り組めたのが自分の経験値になってよかったと感じている。
 ![582CD4A7-438A-4128-97D8-13C34553EEBE.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585901/1cf4c784-bf65-0d10-a134-50c908efef3b.jpeg)

 # 5.GitFlow
GitHubのブランチ機能を活用したチームでの開発手法を使用した。
masterブランチはリリース用のため使用しない 
developブランチは開発共有用。 
feauture/〇〇(開発者の名前)/△(追加機能名等)で機能開発を行う。
①機能開発が完成したら、pushし、担当講師にプルリク(PR)を行う。 
②指摘があれば修正し、再度push。 
③指摘がなければ、developへ担当講師がマージする。
機能開発が全て終わるまで繰り返す。

![GitFlow-640wri.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585901/03b581e5-d8aa-f7a0-7618-290eab860a9b.png)
[【Git】git-flowを知ろう！　利用時のルールについて
](https://cloudsmith.co.jp/blog/efficient/2020/08/1534208.html)※画像は上記URLのサイトから引用

# 6.開発
開発では下記のタスクを三人で割り振った。タスク担当は講師が決めた。
タスクは下記の通り。担当Yの行が私のタスクになる。※デプロイは各自で行うことになった。



![582CD4A7-438A-4128-97D8-13C34553EEBE_1_201_a.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585901/68fe4b51-db56-b72c-cf42-047af750fd5e.jpeg)


私が開発したタスク
①posts（投稿）テーブルのマイグレーションとシーダー作成
②ユーザログイン・ログアウト機能
③ユーザ詳細機能
④投稿新規作成機能
⑤フォローによるタイムライン変更機能

自分が実装したタスクについて、⑤フォローによるタイムライン変更のタスクのみ実装したコードを紹介する。

このタスクにおける目標は、
①”フォロー中”をクリックすると
自分がフォローしているユーザの画面に遷移することと
②”フォロワー”をクリックすると自分のフォロワーのユーザーの画面に遷移することの二点である。画像参照

![78B0E41F-02E3-40C8-8C79-FD72219FF79F.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585901/8af625f0-5703-dc8a-3d14-6cb3806a5ef7.png)

ざっくりだが以下のようにコードを記述していく。
(i)ルーティングへの記述(web.php)
(ii)コントローラーへの記述(UserController.php)
(iii)ビューへの記述
(show.blade.php＆followings.blade.php＆followers.blade.php)
※ビューファイル名は任意もので構わない

<font color="red">**アプリの仕様上seeding ファイルの一部追加・変更を行った。**</font>


(i).ルーテイング(web.php)への記述(一部抜粋)
```web.php
//フォロー、フォロワー
Route::group(['prefix' => 'users/{id}'],function(){
    Route::get('followings','UsersController@followings')->name('followings');
    Route::get('followers','UsersController@followers')->name('followers');
});
```
(ii)コントローラーへの記述(一部抜粋)
```Userscontroller.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\User;
use App\Http\Requests\UserRequest;
use Illuminate\Support\Facades\Hash;

class UsersController extends Controller
{
public function followings($id)
    {
        $user = User::findOrFail($id);
        $followings = $user->followings()->orderBy('id', 'desc')->paginate(9);
        $data=[
            'user' => $user,
            'followings' => $followings,
        ];
       return view('follow.followings' ,$data);
    }

    public function followers($id)
    {
        $user = User::findOrFail($id);
        $followers = $user->followers()->orderBy('id', 'desc')->paginate(9);
        $data=[
            'user' => $user,
            'followers' => $followers,
        ];
       return view('follow.followers' ,$data);
    }
}    
```
(iii)Viewファイルへの記述(Users.show.blade.php)
```rs
@extends('layouts.app')
@section('content')
<div class="row">
    <aside class="col-sm-4 mb-5">
        <div class="card bg-info">
              <div class="card-header d-flex justify-content-between">
                <h3 class="card-title  text-light">{{ $user->name }}</h3>
              </div>
               <div class="card-body">
                    <img class="rounded-circle img-fluid" src="{{ Gravatar::src($user->email, 500) }}" alt="ユーザのアバター画像">
                    @if(Auth::id() === $user->id)
                        <div class="mt-3">
                        <a href="{{ route('users.edit', $user->id) }}" class="btn btn-primary btn-block">ユーザ情報の編集</a> 
                        </div>
                    @else
                        @if(Auth::check())
                            @if (Auth::user()->isFollows($user->id))
                                <form method="POST" action="{{ route('unfollow', $user->id) }}" class="text-center mt-4">
                                    @csrf
                                    @method('DELETE')
                                    <button type="submit" class="btn btn-danger">フォロー解除</button>
                                </form>
                            @else
                                <form method="POST" action="{{ route('follow', $user->id) }}" class="text-center mt-4">
                                    @csrf
                                    <button type="submit" class="btn btn-success">フォロー</button>
                                </form>
                            @endif
                        @endif
                    @endif
               </div>
        </div>
    </aside>
    <div class="col-sm-8">
        <ul class="nav nav-tabs nav-justified mb-3">
            <li class="nav-item"><a href="{{ route('user.show', $user->id) }}"  class="nav-link {{ Request::routeIs('user.show') ? 'active' : '' }} ">タイムライン</a></li>
            <li class="nav-item"><a href="{{ route('followings', $user->id) }}" class="nav-link {{ Request::routeIs('followings') ? 'active' : '' }}">フォロー中</a></li>                                                                      
            <li class="nav-item"><a href="{{ route('followers', $user->id) }}"  class="nav-link {{ Request::routeIs('followers') ? 'active' : '' }}">フォロワー</a></li>
        </ul>
      @include('posts.posts', ['posts' => $posts])
    </div>
</div>
@endsection
```
followings.blade.php
```rs
@extends('layouts.app')
@section('content')
<div class="row">
    <aside class="col-sm-4 mb-5">
        <div class="card bg-info">
              <div class="card-header d-flex justify-content-between">
                <h3 class="card-title  text-light">{{ $user->name }}</h3>
              </div>
               <div class="card-body">
                    <img class="rounded-circle img-fluid" src="{{ Gravatar::src($user->email, 500) }}" alt="ユーザのアバター画像">
                    @if(Auth::id() === $user->id)
                        <div class="mt-3">
                        <a href="{{ route('users.edit', $user->id) }}" class="btn btn-primary btn-block">ユーザ情報の編集</a> 
                        </div>
                    @elseif(Auth::check())
                            @if (Auth::user()->isFollows($user->id))
                                <form method="POST" action="{{ route('unfollow', $user->id) }}" class="text-center mt-4">
                                    @csrf
                                    @method('DELETE')
                                    <button type="submit" class="btn btn-danger">フォロー解除</button>
                                </form>
                            @else
                                <form method="POST" action="{{ route('follow', $user->id) }}" class="text-center mt-4">
                                    @csrf
                                    <button type="submit" class="btn btn-success">フォロー</button>
                                </form>
                            @endif
                    @endif
                </div>
        </div>
    </aside>
    <div class="col-sm-8">
        <ul class="nav nav-tabs nav-justified mb-3">
            <li class="nav-item"><a href="{{ route('user.show', $user->id) }}"  class="nav-link {{ Request::routeIs('user.show') ? 'active' : '' }} ">タイムライン</a></li>
            <li class="nav-item"><a href="{{ route('followings', $user->id) }}" class="nav-link {{ Request::routeIs('followings') ? 'active' : '' }}">フォロー中</a></li>                                                                     
            <li class="nav-item"><a href="{{ route('followers', $user->id) }}"  class="nav-link {{ Request::routeIs('followers') ? 'active' : '' }}">フォロワー</a></li>
        </ul>
        <ul class="list-unstyled">
        @foreach($followings as $following)
         <li class="mb-3 text-center">
             <div class="text-left d-inline-block w-75 mb-2">
                <img class="mr-2 rounded-circle" src="{{ Gravatar::src($user->email, 50) }}" alt="">
                <p class="mt-3 mb-0 d-inline-block"><a href="{{ route('user.show', $following->id) }}">{{ $following->name }}</a></p>
            </div>
        </li>   
        @endforeach
        <div class="text-right">{{ $followings->links('pagination::bootstrap-4') }}</div>
        </ul> 
    </div>
</div>    
</ul>    
@endsection
```
followers.blade.php
```rs
@extends('layouts.app')
@section('content')
<div class="row">
    <aside class="col-sm-4 mb-5">
        <div class="card bg-info">
              <div class="card-header d-flex justify-content-between">
                <h3 class="card-title  text-light">{{ $user->name }}</h3>
              </div>
               <div class="card-body">
                    <img class="rounded-circle img-fluid" src="{{ Gravatar::src($user->email, 500) }}" alt="ユーザのアバター画像">
                    @if(Auth::id() === $user->id)
                        <div class="mt-3">
                        <a href="{{ route('users.edit', $user->id) }}" class="btn btn-primary btn-block">ユーザ情報の編集</a> 
                        </div>
                    @elseif(Auth::check())
                            @if (Auth::user()->isFollows($user->id))
                                <form method="POST" action="{{ route('unfollow', $user->id) }}" class="text-center mt-4">
                                    @csrf
                                    @method('DELETE')
                                    <button type="submit" class="btn btn-danger">フォロー解除</button>
                                </form>
                            @else
                                <form method="POST" action="{{ route('follow', $user->id) }}" class="text-center mt-4">
                                    @csrf
                                    <button type="submit" class="btn btn-success">フォロー</button>
                                </form>
                            @endif
                    @endif
                </div>
        </div>
    </aside>
    <div class="col-sm-8">
        <ul class="nav nav-tabs nav-justified mb-3">
            <li class="nav-item"><a href="{{ route('user.show', $user->id) }}"  class="nav-link {{ Request::routeIs('user.show') ? 'active' : '' }} ">タイムライン</a>
            <li class="nav-item"><a href="{{ route('followings', $user->id) }}" class="nav-link {{ Request::routeIs('followings') ? 'active' : '' }}">フォロー中</a></li>                                                                         
            <li class="nav-item"><a href="{{ route('followers', $user->id) }}"  class="nav-link {{ Request::routeIs('followers') ? 'active' : '' }}">フォロワー</a></li>
        </ul>
        <ul class="list-unstyled">
            @foreach($followers as $follower)
               <li class="mb-3 text-center">
                  <div class="text-left d-inline-block w-75 mb-2">
                   <img class="mr-2 rounded-circle" src="{{ Gravatar::src($user->email, 50) }}" alt="">
                  <p class="mt-3 mb-0 d-inline-block"><a href="{{ route('user.show', $follower->id) }}">{{ $follower->name }}</a></p>
                 </div>
               </li>   
            @endforeach 
            <div class="text-right">{{ $followers->links('pagination::bootstrap-4') }}</div>
        </ul>
    </div>
</div>    
</ul>    
@endsection    
```    
<font color="#ff0000">※Seeding ファイルの一部変更</font>
本来コードの実装はここまでだったが、今回フォロー一覧とフォロワー一覧にアクセスした時に９人のユーザーを表示しないとページネーション機能がうまく機能しているか確認することができない。そのため９人のユーザを表示するようにシーディーングファイルの変更を行なった。ページネーション機能は下記画像参照。

![AD487A63-D51A-406E-A41D-C114A3368AF9.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585901/0de2fbdc-7e99-d1d1-bdfe-bbd903e37f1f.jpeg)

<変更前>
```UsersTableSeeder.php
<?php
use Illuminate\Database\Seeder;
class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */


 public function run()
    {
        DB::table('users')->insert([
            'name' => 'test1',
            'email' => 'test1@test.com',
            'password' => bcrypt('test1')
        ]);
        DB::table('users')->insert([
            'name' => 'test2',
            'email' => 'test2@test.com',
            'password' => bcrypt('test2')
        ]);
        DB::table('users')->insert([
            'name' => 'test3',
            'email' => 'test3@sample.com',
            'password' => bcrypt('test3')
        ]);
        DB::table('users')->insert([
            'name' => 'test4',
            'email' => 'test4@test.com',
            'password' => bcrypt('test4')
        ]);   
   }
}
```
 変更後
 ```UsersTableSeeder.php
 <?php

use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run(){
        for ($i = 1; $i < 100; $i++)
        {
        DB::table('users')->insert([
            'name' => 'test'.$i,
            'email' => "test".$i."@test",
            'password' => bcrypt("test".$i)
        ]);
     }
   }
}
```
次にPostsTableSeederの変更を書いていく。
※PostsTableはフォロー一覧とフォロワー一覧の表示に関係なかったが
他のシーダーファイルと仕様を統一するためにこちらも変更を行なった。
<変更前>
```PostsTableSeeder.php
<?php
use Illuminate\Database\Seeder;
class PostsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */

 public function run()
    {    
        DB::table('posts')->insert([    
            'user_id' => 1,
            'content' => 'テスト1',
        ]);
        DB::table('posts')->insert([
            'user_id' => 2,
            'content' => 'テスト2',
        ]);
        DB::table('posts')->insert([
            'user_id' => 3,
            'content' => 'テスト3',
        ]);
        DB::table('posts')->insert([
            'user_id' => 4,
            'content' => 'テスト4',
    }  
}
```
<変更後>
```PostsTableSeeder.php
<?php

use Illuminate\Database\Seeder;

class PostsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run(){
    
    for ($i = 1; $i < 100; $i++)
        {
        DB::table('posts')->insert([
            'user_id' => $i,
            'content' => "test".$i,
        ]);
        }
     }
}
```
最後にFollowsTableSeederの作成をする。
```FollowsTableSeeder.php
<?php

use Illuminate\Database\Seeder;

class FollowsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run(){
        for ($i = 1; $i < 98; $i++)
        {
        DB::table('follows')->insert([
            'following_user_id' => 2,
            'followed_user_id' => $i,
        ]);
    }
 }
}
```



# 7.テスト
簡単な動作確認を行いました。
1,画面がきちんと遷移するか
2,URlテスト(URLを直接入力した際にきちんと画面が遷移するかどうか)
3,バリデーションテスト(バリデーションエラーがきちんと表示されるかどうか)

# 8.デプロイ
FLYを使用

# 9.共同開発における反省点
①期限内にアプリを完成させることができなかったこと。
共同開発間際になって開発が終わったため全体的なアプリの確認・修正があまりできなかった。納期を意識し決められたタスクを期間内に終わらせるよう実装を進めたいと思う。
②LaravelのMVCの構造や実装に必要な知識が曖昧な部分があり開発中に何度か復習した。
知識が曖昧な箇所は他の人に説明できる程度に理解を深めた。



# 10.共同開発で学んだこと
1,タイプミスには気をつける。一文字でエラーになる。
2,他の人が見て見やすいコードを心がける。
3,自分が実装しやすいコードを書くのではなく他のコードが活かせるコードを書く。

# 11.工夫したこと
1,**コミニュケーションの強化**
チャットアプリを完成させるためにチャットのみのやり取りだとどうしても
伝わらないことがあったため、ミーティングを率先して開きチームメンバーと
アプリの仕様を細かく確認した。またお互いが実装したコードで矛盾が起きている場合
最終的に誰の実装が一番良いかの確認をslackで細かく確認しあった。
2,**コーディングのアドバイス**
チームメンバー全員が未経験のためお互いに実装のアドバイスをしあった。
主にエラー解決やコンフリクトの解決をした。

# 12.まとめ
共同開発に入るまではインプット重視の勉強だったが、共同開発に入ってアウトプット中心の勉強に変わってコードの勉強になった。エラーの解決やコンフリクトの解消は頻繁に起きたが、自分のコードの癖を掴むことができた。今後はLaravelの勉強をより一層していきフロントエンド側の勉強を行なっていきたい。