laravel学んで2か月で自サービスを開発した話 Part5

皆さんこんにちは、本格的な勉強会に参加したら？？？になった私です。

今までの開発記録はこちらへ
<a href="https://qiita.com/k-hayack884/items/e571138e8bf7f35f7502">胡蝶蘭を捨てるくらいならワイが欲しいので、サービス開発する編</a>
<a href="https://qiita.com/k-hayack884/items/720c7a6300139317deac">公式ドキュメントの言う通り、パッケージをインストールされたら、Inertia.jsが導入されて？？？になった編</a>
<a href="https://qiita.com/k-hayack884/items/dccb9d479bd162144559">マルチログインを作ってみた編</a>
<a href="https://qiita.com/k-hayack884/items/0354c0ddecee58272e46">デザインをtailwindcssに丸投げする編</a>

# いよいよユーザー画面を作る  
今回の目的はユーザーのプロフィール画面
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f323238313934362f30396630376266302d323930352d643666652d366366632d613837386465373638643834.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/0e6465c6-e1b2-6329-74d7-3f2965a07b49.jpeg)
そのために必要なものは
- ユーザー詳細表示機能
- ユーザー編集機能（Adminでやったひな型を使いまわす）
- 画像取り込み機能
- 退会機能

## まずはユーザー詳細機能を作っていく
ルートはresourceを利用してもよかったが今回は普通に作成
```web.php
Route::prefix('profiles')->middleware(['auth:users'])->group(function () {
    Route::get('show/{profile}', [ProfileController::class, 'show'])->name('profiles.show');
    Route::get('edit/{profile}', [ProfileController::class, 'edit'])->name('profiles.edit');
    Route::post('update/{profile}', [ProfileController::class, 'update'])->name('profiles.update');
    Route::get('destroy/{profile}', [ProfileController::class, 'destroy'])->name('profiles.destroy');
    Route::post('destroy/{profile}', [ProfileController::class, 'destroy'])->name('profiles.destroy');
});
```
コントローラー側
```ProfileController.php
namespace App\Http\Controllers\User;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\Review;
use App\Models\Product;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Auth;
use App\Http\Requests\UploadImageRequest;
use App\Services\ImageService;
use Illuminate\Validation\Rules\Password;

class ProfileController extends Controller
{
    
    public function show($id)
    {
        $userProfile=User::findOrFail($id);
        return view('user.profiles.show', compact('userProfile'));
    }

    public function edit(Request $request, $id) //リクエスト入れる
    {
        $id=$request->route()->parameter('profile');
        if (!is_null($id)) {
            $userId=User::findOrFail($id)->id;
            $currentUserId=(int)$userId;
            $authId=Auth::id();
            if ($currentUserId!==$authId) {
                abort(404);
            }
        }   
        $userProfile = User::findOrFail($id);

        return view('user.profiles.edit', compact('userProfile'));
    }

    public function update(UploadImageRequest $request, $id)
    {
        $id=$request->route()->parameter('profile');
        if (!is_null($id)) {
            $userId=User::findOrFail($id)->id;
            $currentUserId=(int)$userId;
            $authId=Auth::id();
            if ($currentUserId!==$authId) {
                abort(404);
            }
        }
        $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'comment' => [ 'string', 'max:200'],
            'password' => ['required', 'confirmed','string', Password::defaults()],
            'prefecture' => ['string', 'max:50'],
            'comment' => ['string', 'max:100'],
        ]);
        $userProfile = User::findOrFail($id);
        $imageFile=$request->image;
        if (!is_null($imageFile)&&$imageFile->isValid()) {
            // Storage::putFile('public/profiles', $imageFile);//リサイズなし
            $fileNameToStore=ImageService::upload($imageFile, 'profiles');
        }
        $user=User::findOrFail($id);
        $user->name=$request->name;
        $user->comment=$request->comment;
        $user->prefecture=$request->prefecture;
        $user->password=Hash::make($request->password);
        if (!is_null($imageFile)&&$imageFile->isValid()) {
            $user->img=$fileNameToStore;
        }
        
        $user->save();
        
        return redirect()
        ->route('user.profiles.show', ['profile' => $userProfile->id])
    }

    public function destroy(Request $request,$id)
    {
        $this->middleware('auth:users');
        //直接別ユーザーにアクセスするとはじくシステム
        $this->middleware(function ($request, $next) {
            $id=$request->route()->parameter('profile');
            if (!is_null($id)) {
                $userId=User::findOrFail($id)->id;
                $currentUserId=(int)$userId;
                $authId=Auth::id();
                if ($currentUserId!==$authId) {
                    abort(404);
                }
            }
            return $next($request);
        });        
        try {
            DB::transaction(function () use ($id) {
                Product::select('id', )
                ->where('user_id', Auth::id())->delete();
                
                User::select('id', 'name')
                ->where('id', Auth::id())->delete();
            }, 2); //試行する回数
        } catch (Throwable $e) {
            Log::error($e);
            throw $e;
        }
        Auth::guard('users')->logout();
        return redirect('/');
    }
}
```
Adminのコントローラーの使いまわしだけれど、ところどころそうではない部分があるので一つずつ説明
#### showメゾット
Userテーブルから、入力されたidの情報を取り出して終わり

#### editメゾット
ユーザー情報詳細はほかの人からも見られてもいいけれど、ユーザー編集、削除は本人以外からのアクセスをはじくようにする。
最初は、__constructに記述したが、それだとユーザー情報表示も見られなくなっちゃうので、edit,update,deleteのメゾットに書いたのがこれ。
```ProfileController.php
       $id=$request->route()->parameter('profile');
        if (!is_null($id)) {
            $userId=User::findOrFail($id)->id;
            $currentUserId=(int)$userId;
            $authId=Auth::id();
            if ($currentUserId!==$authId) {
                abort(404);
            }
        }   
```
このコードが何を示しているかというと、ユーザーがアクセスしようとしているIDとログイン中のIDを照合して一致していれば、次の処理進み、そうでない場合は404ではじき返す仕組みです。
もう少し中身を見ていくと
まずはddヘルパーを利用して、$request->route()を入力すると。
![スクリーンショット (56).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/d7eb98bf-62c0-c382-d643-8d20c6a42e18.png)
このような画面が出ます
parametersのところに"1"がありますね。
$request->route()->parameter('profile')で"1"を取得しましょう。
その後、Userテーブルから、そのidを取得
ただし、このidは**文字列**で、照合したいAuth::id()は**数字**だから一致しません。
そのためidを**数字**に変換
あとはif文で照合させるだけです

#### 画像の取り込み
webサイトには必須な機能ですね！

ただ画像をアップロードして表示するのだったら、素でもできますが、
![joushi_buka_men3_gekido.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/28b3d0c3-870f-47bb-2536-e8f7294c0d86.png)

と怒られそうなので、画像のリサイズをするためにInterventionImageを利用します
```
composer require intervention/image
```
インストールが終わったらconfig/app.phpへ
```app.php
    'providers' => [   
        Intervention\Image\ImageServiceProvider::class,
    ],

    'aliases' => [
        'InterventionImage'=>Intervention\Image\Facades\Image::class
    ],
```
を追記して **php artisan cache:clear**を押しましょう。
あとは**use InterventionImage**で利用できる。
このサイトでも、ユーザーの画像、商品画像には画像をアップロードさせるシステムを作る必要がある。
そうなると２つの問題点が出てくる
- 画像名が重複せずにどうやって保存するか
- どこに保存するか

一つ目の問題について、アップロードした画像名をランダムに変更するプログラムを作る
二つ目の問題については、laravelのstorageフォルダに保存する
コードが多くなるのと商品投稿のコントローラーとも共有したいので、UploadImageRequestとImageServiceに切り分けて作成
```UploadImageRequest.php
    public function authorize()
    {
        return true; //falseになっているのでtrueに切り替える
    }
    public function rules()
    {
        return [
            'image' => 'image|mimes:jpg,jpeg,png|max:2048',
        ];
    }
public function message()
    {
        return [
            'image' => '指定されたファイルが画像ではありません',
            'mines' => '指定された拡張子(jpg/jpeg/png)ではありません',
            'max' => 'ファイルサイズは2MB以内にしてください',
        ];
    }

```
```ImageService.php
class ImageService
{
    public static function upload($imageFile, $folderName)
    {
            $fileName=uniqid(rand().'_'); //ファイル名をランダムに生成
            $extension=$imageFile->extension(); //拡張子を判別
            $fileNameToStore=$fileName.'.'.$extension; //上二つの文字列をつなげる
            $resizedImage=InterventionImage::make($imageFile)->resize(400, 400)->encode();
            //InterventionImageがリサイズしてくれる
            Storage::put('public/'.$folderName.'/'.$fileNameToStore, $resizedImage);
            //storageフォルダのpublicフォルダに$folderNameを作成（今回はProfiles）して保存
        return $fileNameToStore;
    }
}
```
準備は整ったのでコントローラーにも追記
```
        if (!is_null($imageFile)&&$imageFile->isValid()) {
           $fileNameToStore=ImageService::upload($imageFile, 'profiles');//ImageServiceに画像ファイルとフォルダ名の情報を送る
        }

        if (!is_null($imageFile)&&$imageFile->isValid()) {
            $user->img=$fileNameToStore; //データベースに生成されたランダム名を書き込む
        }
```
**これで画像の保存はOKです**
![operator_man4_laugh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/a3fd5cf4-42fc-8e78-5ebe-060ea141f2e7.png)
### よし、これで大量の画像があっても重複せずに処理ができるぞい！  
herokuに上げても大丈夫や！


![necchusyou_face_boy3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/de1097ba-885f-fc0f-102b-3b6a7db39bfd.png)
### あのお・・・herokuのストレージの画像は一定時間で消えますよ


![bikkuri_me_tobideru_man.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/a34ebc0b-a237-887a-7288-fc52c9407527.png)
### えええええええ！！

![スクリーンショット (57).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/0a0a85d3-adfa-c823-cda8-54249a9d6e20.png)
<a href="https://qiita.com/hmmrjn/items/b02f076ceaeeaaceb999">Herokuでアップロードした画像が時間経つと消える問題
</a>

対処法AWS使うのかよ・・・
## 今回で終わらなくてごめんなさい
次回はAWSに挑んできます