ログイン機能とフォロー機能

## Controllerを作成
### ResourceController
Controllerファイルを生成する際`--resource`と付けることでResourceに対応したControllerを自動的に生成。

| HTTP動詞 |URL |アクション |役割 |
|:-----------------|:-------------------|:------------------|:------------------|
| GET |/tweets |index |一覧表示 |
| GET |/tweets/create |create |新規ツイート入力画面 |
| POST |/tweets |store |新規ツイート投稿処理 |
| GET |/tweets/{tweet}/show|show |ツイート詳細画面 |
| GET |/tweets/{tweet}/edit|edit |ツイート編集画面 |
| PUT/PATCH |/tweets/{tweet} |update |ツイート編集処理 |
| DELETE |/tweets/{tweet} |destory |ツイート削除処理 |

```php:TweetsConroller.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class TweetsConroller extends Controller
{
 // 一覧表示
 public function index()
 {
 //
 }
 // 新規ツイート入力画面
 public function create()
 {
 //
 }
 // 新規ツイート投稿処理
 public function store(Request $request)
 {
 //
 }
 // ツイート詳細画面
 public function show($id)
 {
 //
 }
 // ツイート編集画面
 public function edit($id)
 {
 //
 }
 // ツイート編集処理
 public function update(Request $request, $id)
 {
 //
 }
 // ツイート削除処理
 public function destroy($id)
 {
 //
 }
}
```
### UsersController
```terminal
php artisan make:controller UsersController --resource
```
### Routing

```php:routes/web.php
<?php
/*
|--------------------------------------------------------------------------
| 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');
});
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
// ログイン状態
Route::group(['middleware' => 'auth'], function() {
 // ユーザ関連
 Route::resource('users', 'UsersController');
});
```
このクロージャーの中にルートを設定することでログインした時にしかアクセス出来ないようにする。

```php
Route::group(['middleware' => 'auth'], function() {
 //
});
```

```php
Route::resource('users', 'UsersController');
```
ユーザ機能では`一覧`/`詳細`/`編集`/`更新`のみを使用するので第３引数に`only`と記述して使うアクションのみを設定。
```php
Route::resource('users', 'UsersController', ['only' => ['index', 'show', 'edit', 'update']]);
```

## ユーザを取得
### ユーザ一覧表示画面
#### Controller

```php:app/Http/Controllers/UsersController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use App\Models\User;
use App\Models\Tweet;
use App\Models\Follower;
class UsersController extends Controller
{
 public function index(User $user)
 {
 $all_users = $user->getAllUsers(auth()->user()->id);
 return view('users.index', [
 'all_users' => $all_users
 ]);
 }
}
```
##### `メソッドインジェクション`
LaravelにはDI（依存性の注入）というのが内蔵されており、メソッドの引数にインジェクトしたいオブジェクトを書くだけで、そのインスタンスが使用できる。

```php
public function index(User $user)
{
 $all_users = $user->getAllUsers(auth()->user()->id);
}
```

#### Model

```php:app/Models/User.php
 public function getAllUsers(Int $user_id)
 {
 return $this->Where('id', '<>', $user_id)->paginate(5);
 }
```

#### View

```php:resources/views/users/index.blade.php
@extends('layouts.app')
@section('content')
 <div class="container">
 <div class="row justify-content-center">
 <div class="col-md-8">
 @foreach ($all_users as $user)
 <div class="card">
 <div class="card-haeder p-3 w-100 d-flex">
 <img src="{{ $user->profile_image }}" class="rounded-circle" width="50" height="50">
 <div class="ml-2 d-flex flex-column">
 <p class="mb-0">{{ $user->name }}</p>
 <a href="{{ url('users/' .$user->id) }}" class="text-secondary">{{ $user->screen_name }}</a>
 </div>
 </div>
 </div>
 @endforeach
 </div>
 </div>
 <div class="my-4 d-flex justify-content-center">
 {{ $all_users->links() }}
 </div>
 </div>
@endsection
```
## フォロー関連の処理
### フォロー/フォロー解除/フォローされているか
#### Controller
フォローとフォロー解除はresourceのどのアクションにも該当しないので、独自に追加。

```php:app/Http/Controllers/UsersController.php
 // フォロー
 public function follow(User $user)
 {
 $follower = auth()->user();
 // フォローしているか
 $is_following = $follower->isFollowing($user->id);
 if(!$is_following) {
 // フォローしていなければフォローする
 $follower->follow($user->id);
 return back();
 }
 }
 // フォロー解除
 public function unfollow(User $user)
 {
 $follower = auth()->user();
 // フォローしているか
 $is_following = $follower->isFollowing($user->id);
 if($is_following) {
 // フォローしていればフォローを解除する
 $follower->unfollow($user->id);
 return back();
 }
 }
```
#### Model
```php:app/User.php
 // フォローする
 public function follow(Int $user_id)
 {
 return $this->follows()->attach($user_id);
 }
 // フォロー解除する
 public function unfollow(Int $user_id)
 {
 return $this->follows()->detach($user_id);
 }
 // フォローしているか
 public function isFollowing(Int $user_id)
 {
 return (boolean) $this->follows()->where('followed_id', $user_id)->first(['id']);
 }
 // フォローされているか
 public function isFollowed(Int $user_id)
 {
 return (boolean) $this->followers()->where('following_id', $user_id)->first(['id']);
 }
```

フォローされているかの判定

```php:resources/views/users/index.blade.php
@if (auth()->user()->isFollowed($user->id))
```
フォローしているかの判定

```php:resources/views/users/index.blade.php
@if (auth()->user()->isFollowing($user->id))
```

#### Routing

```php:routes/web.php
// ログイン状態
Route::group(['middleware' => 'auth'], function() {
 // ユーザ関連
 Route::resource('users', 'UsersController', ['only' => ['index', 'show', 'edit', 'update']]);
 // フォロー/フォロー解除を追加
 Route::post('users/{user}/follow', 'UsersController@follow')->name('follow');
 Route::delete('users/{user}/unfollow', 'UsersController@unfollow')->name('unfollow');
});
```

### ユーザ詳細画面
- プロフィール
- プロフィールが自身だった時に編集ボタンを追加
- プロフィールが自身以外のユーザだった時にフォロー/フォロー解除/フォローされているかの判定を追加
- 総ツイート数/フォロー数/フォロワー数の表示
- ユーザがツイートしたタイムラインの表示

#### Controller

```php:app/Http/Controllers/UsersController.php
 public function show(User $user, Tweet $tweet, Follower $follower)
 {
 $login_user = auth()->user();
 $is_following = $login_user->isFollowing($user->id);
 $is_followed = $login_user->isFollowed($user->id);
 $timelines = $tweet->getUserTimeLine($user->id);
 $tweet_count = $tweet->getTweetCount($user->id);
 $follow_count = $follower->getFollowCount($user->id);
 $follower_count = $follower->getFollowerCount($user->id);
 return view('users.show', [
 'user' => $user,
 'is_following' => $is_following,
 'is_followed' => $is_followed,
 'timelines' => $timelines,
 'tweet_count' => $tweet_count,
 'follow_count' => $follow_count,
 'follower_count' => $follower_count
 ]);
 }
```
#### Model
##### `Tweet`

```php:app/Tweet.php
 public function getUserTimeLine(Int $user_id)
 {
 return $this->where('user_id', $user_id)->orderBy('created_at', 'DESC')->paginate(50);
 }
 public function getTweetCount(Int $user_id)
 {
 return $this->where('user_id', $user_id)->count();
 }
```

##### `Follower`

```php:app/Follower.php
 public function getFollowCount($user_id)
 {
 return $this->where('following_id', $user_id)->count();
 }
 public function getFollowerCount($user_id)
 {
 return $this->where('followed_id', $user_id)->count();
 }
```

#### ユーザ編集
##### `Controller`
$requestで取得したデータを`Validator::make`を使ってバリデーションをかける。
`Rule::unique('users')->ignore($user->id)`の部分はユニークに設定している`screen_name`や`email`を自身のIDの時だけ無効にする設定。

```php:app/Http/Controllers/UsersController.php
 public function edit(User $user)
 {
 return view('users.edit', ['user' => $user]);
 }
 public function update(Request $request, User $user)
 {
 $data = $request->all();
 $validator = Validator::make($data, [
 'screen_name' => ['required', 'string', 'max:50', Rule::unique('users')->ignore($user->id)],
 'name' => ['required', 'string', 'max:255'],
 'profile_image' => ['file', 'image', 'mimes:jpeg,png,jpg', 'max:2048'],
 'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users')->ignore($user->id)]
 ]);
 $validator->validate();
 $user->updateProfile($data);
 return redirect('users/'.$user->id);
 }
```
##### `Model`
$paramsの中に画像があれば処理を分けています。
`$file_name = $params['profile_image']->store('public/profile_image/');`
とすることで画像ファイルが`/storage/app/public/profile_image/`に保存される。

```app/User.php
 public function updateProfile(Array $params)
 {
 if (isset($params['profile_image'])) {
 $file_name = $params['profile_image']->store('public/profile_image/');
 $this::where('id', $this->id)
 ->update([
 'screen_name' => $params['screen_name'],
 'name' => $params['name'],
 'profile_image' => basename($file_name),
 'email' => $params['email'],
 ]);
 } else {
 $this::where('id', $this->id)
 ->update([
 'screen_name' => $params['screen_name'],
 'name' => $params['name'],
 'email' => $params['email'],
 ]);
 }
 return;
 }
```