2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-03-04

皆さんこんばんわ(この記事を投稿したのは真昼間です)

今回も引き続き、プロフィール画面の続きを開発していきます。
68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f323238313934362f30396630376266302d323930352d643666652d366366632d613837386465373638643834.jpg

今までの開発記録はこちらへ
胡蝶蘭を捨てるくらいならワイが欲しいので、サービス開発する編
公式ドキュメントの言う通り、パッケージをインストールされたら、Inertia.jsが導入されて???になった編
マルチログインを作ってみた編
デザインをtailwindcssに丸投げする編
デザイナーに怒られないために、画像をリサイズする編

今回投稿すること

  • s3に画像を保存する
  • ユーザーの退会機能

AWSを使う

開発以前からAWSによるインフラ開発を勉強していたのだが、いきなりは不安だったので今回はHerokuにデプロイをしようと思ったが、

Herokuではストレージの中身が一定時間で削除されることが判明

ということで、結局AWSに触れることに・・・

AWSストレージサービスのS3を使います。

まずはAWSのアカウントを作りましょう!
リージョンは特にこだわらなければ東京を選びましょう。
※登録にはクレジットカードが必要です。

pose_kyosyu_boy.png

先生、未成年の場合どうやってクレジットカードを用意すればいいんですか。

job_teacher_man.png

お母さんやお父さんにお願いしようね

gorogoro_neet.png

先生、クレジットカードの審査が落ちるんだけどどう知ればいいですか?

job_teacher_man.png

知らん

スクリーンショット (1655)_LI.jpg
アカウント名をクリックして、セキュリティ認証情報を押します。
FireShot Capture 003 - IAM Management Console - console.aws.amazon.com.png
新しいアクセスキーの作成をクリックして、アクセスキーとシークレットアクセスキーを入手します
シークレットアクセスキーはここ以外では見ることができないので、別のファイルに控えるか、ダウンロードしましょう。

アクセスキーとシークレットアクセスキーを入手したら、上の検索フォームからs3と入力しましょう。
S3は無料枠もありますが、基本的にはお金がかかります
学習用にAWSをいじって放置した結果、私は月に4000円取られました
無料枠についてはこちらを見てね

S3の使い方

  1. まずはバケットを作成します
  2. バケット名を入力する
  3. リージョンは特にこだわってなければ東京で
  4. オブジェクト所有者のACLは有効に
  5. パブリックアクセスをすべてブロックをオフに変更
    にします。

バケットが完成すれば、バケットをクリックして、アクセス許可のタブをクリック
するとバケットポリシーの欄があるので

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "(初期設定でIDがある)",
            "Effect": "Allow",
            "Principal": "*", //変更
            "Action": "s3:*", //変更
            "Resource": "arn:aws:s3:::(バケット名)/*" //変更
        }
    ]
}

で保存
AWS側の設定はこれで大丈夫だと思う。

laravel側の動作

laravelでs3にアップロードするには紐づけないといけないので、aws-sdk-phpをインストールする

composer require aws/aws-sdk-php

ファイルを操作するパッケージである、flysystem-aws-s3-v3もインストール。

composer require league/flysystem-aws-s3-v3": "1.0.29

インストールに成功すると、config/filesystems.phpが出現するので、

filesystems.php
'disks' => [
//略
        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
        ],

を入力する。
今度はenvファイルの設定

.env
AWS_ACCESS_KEY_ID=(アクセスキー)
AWS_SECRET_ACCESS_KEY=(シークレットアクセスキー)
AWS_DEFAULT_REGION=ap-northeast-1 (東京の場合)
AWS_BUCKET=(バケット名)
AWS_URL=https://s3-(リージョン).amazonaws.com/(バケット名)/

に設定する。

これで準備は終了です

s3の使い方

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();
        // Storage::put('public/'.$folderName.'/'.$fileNameToStore, $resizedImage);
        Storage::disk('s3')->put($folderName.'/'.$fileNameToStore,$resizedImage, 'public'); //追加
        
        return $fileNameToStore;
    }
}

前回はstorageフォルダに保存していましたが、この部分を書き換えます
komaru.png

こんなに大掛かりな準備をしたのにコードはこれだけ?

ということできょとんとしている彼をアップロードします
FireShot Capture 006.png
ちゃんとS3に画像が入っていますよね
画像の名前の部分を押してみると、
スクリーンショット (1657)_LI.jpg
本当に連携できている・・・

ということで、S3とlaravelを連携させて画像をアップロードすることができました!

退会機能

退会の処理をすることは、ユーザーテーブルからユーザーを削除するわけではない。
ECサイトなら、ユーザーが出した商品など、ユーザーテーブルと商品テーブルがつながっていることが多いだろう。
そのため、外部キーを使ってリレーションをするはずなので、先に退会ユーザーが出している商品を削除してから、ユーザーを削除するという流れになる。
しかし、商品テーブルを削除した後、何らかの不具合で、ユーザーを削除できていなかった場合、商品テーブルは削除されただけになる。
この場合は、一見問題なさそうに見えるかもしれないが、在庫やお金をやり取りするサイトではそれが消されたのに処理がされていないことになり、クレームにつながる。
例えば、AがBに金を振り込むとAの残高が減って、Bの残高が増える
しかし、Aが振り込んだ後にシステムが止まれば、Aはただお金を損しただけであって、Bにはお金が増えない。

なので、Aの残高を減らす、Bの残高を増やすという工程をひとまとめにして、全部できたら処理をする、途中でエラーが発生すれば、処理を止めて実行前に戻すトランザクションの処理を書く必要がある。
長くなりましたが、コードを書きます。

ProfileController.php
 public function destroy(Request $request,$id)
    {
//略   
        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(); //退会すれば、ログアウト処理がいります
    }

これでOK

画像を表示する

view側の設定もしていきましょう edit.blade.phpはadminのものを使いまわして調整

show.blade.php
<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            ユーザー情報確認
        </h2>
    </x-slot>
    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="p-6 bg-white border-b border-gray-200">
                    <section class="text-gray-600 body-font">
                        <div class="container mx-auto flex px-5 py-24 md:flex-row flex-col items-center">
                            <div class="lg:max-w-lg lg:w-full md:w-1/2 w-5/6 mb-10 md:mb-0">
                                <x-user-icon :filename="$userProfile->img" />
                            </div>
                            <div
                                class="lg:flex-grow md:w-1/2 lg:pl-24 md:pl-16 flex flex-col md:items-start md:text-left items-center text-center">
                                <h1 class="title-font sm:text-4xl text-3xl mb-4 font-medium text-gray-900">ユーザー情報</h1>
                                <ul>
                                    <li class="mb-8 leading-relaxed">ユーザー名: {{ $userProfile->name }}</li>

                                    @if ($userProfile->id === auth()->user()->id)
                                        <li class="mb-8 leading-relaxed">メールアドレス: {{ $userProfile->email }}</li>
                                    @endif
                                    <li class="mb-8 leading-relaxed">出身地: {{ $userProfile->prefecture }}</li>
                                    <li class="mb-8 leading-relaxed">自己紹介: {{ $userProfile->comment }}</li>
                                    <ul>
                                        <div class="flex flex-col md:flex-row">
                                            @if ($userProfile->id === auth()->user()->id)
                                                <button type="button"
                                                    onclick="location.href='{{ route('user.profiles.edit', ['profile' => $userProfile->id]) }}'"
                                                    class=" mx-auto text-white bg-indigo-500 border-0 py-2 px-8 focus:outline-none hover:bg-gray-600 rounded text-lg mx-4 mb-4">編集</button>
                                            @endif
                                            <button type="button"
                                                onclick="location.href='{{ route('user.dashboard') }}'"
                                                class=" mx-auto text-white bg-gray-500 border-0 py-2 px-8 focus:outline-none hover:bg-gray-600 rounded text-lg mx-4 mb-20 md:mb-4">戻る</button>
                                            @if ($userProfile->id === auth()->user()->id)
                                                <form id="delete_{{ $userProfile->id }}" method="post"
                                                    action="{{ route('user.profiles.destroy', ['profile' => $userProfile->id]) }}">
                                                    @csrf
                                                    <a href="#" data-id="{{ $userProfile->id }}"
                                                        onclick="deletePost(this)"
                                                        class=" text-white bg-red-500 border-0 py-2 px-20 text-center focus:outline-none hover:bg-red-600 rounded text-lg mx-4 mt-8 md:hidden">
                                                        削除</a>
                                                </form>
                                            @endif
                                        </div>
                                    </ul>
                                </ul>
                            </div>
                        </div>
                        @if ($userProfile->id === auth()->user()->id)
                            <div class="flex justify-end">
                                <form id="delete_{{ $userProfile->id }}" method="post"
                                    action="{{ route('user.profiles.destroy', ['profile' => $userProfile->id]) }}">
                                    @csrf
                                    <a href="#" data-id="{{ $userProfile->id }}" onclick="deletePost(this)"
                                        class=" text-white bg-red-500 border-0 py-2 px-8 mx-auto focus:outline-none hover:bg-red-600 rounded text-lg mx-4 mt-10 hidden md:block">
                                        削除</a>
                                </form>
                            </div>
                        @endif
                    </section>
                </div>
            </div>
        </div>
    </div>
    <script src="{{ mix('/js/app.js') }}"></script>
    <script>
        'use strict';

        function deletePost(e) {
            if (confirm('本当に退会してもよろしいですか?')) {
                document.getElementById('delete_' + e.dataset.id).submit();
            }
        }
    </script>
</x-app-layout>

特に画像の表示に使う部分が<x-user-icon :filename="$userProfile->img" />の部分で、
いちいち、bladeごとに画像表示のコードを書くのは面倒くさいので、コンポーネントを挿入しています。

コンポーネントについてはこちら
Laravel Breeze:Componentの使い方を図入りで分かりやすく説明しました

ざっくりいうと、コントローラーから取得したimgのテキストをコンポーネントファイルに渡します。
コンポーネントの中身はresources/views/componentsにあるので、新しくuser-icon.blade.phpを新規作成

user-icon.blade.php
<div>
    @if(empty($filename))
    <img src="{{asset('images/no_image.png')}}">
    @else
    <img src="{{asset('https://(バケット名).s3.ap-northeast-1.amazonaws.com/profiles/'.$filename)}}">
    @endif
</div>

初期設定や、ユーザーが画像を設定しない場合はnoimageの画像を使います。
これでS3にある画像ファイルを読み込んでくれます。

終わりに

これでプロフィール画面の表示や退会機能を作りました。
AWSの機能はいろいろあるのでいじってみたいですね適当にいじくったら月4000円も取られました
いよいよ、商品部分の作成ですね!

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?