ポートフォリオの解説投稿（自分なりのアウトプットです！！）

#自分が書いたコードを説明できないのは恥だよ!!
これは、あるYouTuberの言葉です。
確かにそうですよね…
はい、やります。やらせてください！
解説やらせてください！
てな感じで始めます笑
と、その前にこちらがポートフォリオの内容です。

#三部構成でいきます！
Controller・Model・Viewの三部構成で解説していきます。
主にTweetの内容からです。


###まずControllerから行きます！
```php:app/Http/Controllers/TweetController.php
namespace App\Http\Controllers;
#namespaceとは、名前空間といい、こちらを使用してクラス被りをしないようにする。というもの。

use App\Models\Tweet;
use App\Models\Tag;
use App\Models\Comment;
#各種のModelを読み込む際の記述
use Storage;
use Illuminate\Support\Str;
#ストレージファイルの読み込む際の記述
use InterventionImage;
use Image;
#写真の編集ライブラリ(Intervention Image)を使用したため、その読み込みの記述
use Illuminate\Http\Request;
use App\Http\Requests\TweetRequest;
#Requestファイル(バリデーションのルールが書いてあるファイル)を読み込む際の記述

class TweetController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {

        $q = \Request::query();
        #$qにリクエストのクエリストリングを入れる。

        if(isset($q['tag_name'])){#もし、$qの中のtag_nameに値があればtrueを実行する
            $tweets = Tweet::with(['user', 'tags'])->latest()->where('tag_box', 'like', "%{$q['tag_name']}%")->paginate(10);
            #$tweetsにN+1問題を解決するためにwithを使用し[userとtags]のModel情報を取得し、最新の情報を取得し、カラム名tag_boxを文字列検索で$q['tag_name']を取得し、ページネーションで10ページを表示させる。
            #withとはリレーションを解決したい時に使用する。
            #whereの引数について、第１引数はカラム名です。第２引数はデータベースがサポートしているオペレーターです。第３引数はカラムに対して比較する値である。
            $tags = \DB::table('tags')->get();
　　　　　　　#$tagsにtagsテーブルの情報を取得する。

            return view('tweets.index', [
                'tweets' => $tweets,
                'tags' => $tags,
                'tag_name' => $q['tag_name']
            ]);
　　　　　　　#返り値でViewに値を渡す。
        }else {

            $tweets = Tweet::with(['user', 'tags'])->latest()->paginate(10);
            $tags = \DB::table('tags')->get();

            $tags_name = [];
            foreach ($tags as $tag) {
                array_push($tags_name, $tag->tag_name);
            }
            #tags_nameに配列として値を渡す。foreachで値を回し、タグテーブルの情報からtag_nameを配列に追加していく。

            return view('tweets.index', [
                'tweets' => $tweets,
                'tags' => $tags
            ]);


        }
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        return view('tweets.create'
        );
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(TweetRequest $request)
    {
        $tweet = new Tweet;
        $tweet->user_id = $request->user_id;
        $tweet->content = $request->content;
        $tweet->tag_box = $request->tag_box;
        $tweet->title = $request->title;
        #$tweetにパラメーターで取得した情報をそれぞれ値を入れていく。
      
        if($request->hasFile('image')){#パラメーターの中にimageが入っていたらtrueで値を返す。
            $filename = $request->file('image');#リクエストでimageファイルとして$filenameに入れる。
            $name = $filename->getClientOriginalName(); #$filenameの画像の名前を取得する
            $ext = strtolower(substr($filename->getClientOriginalName(), strrpos($filename->getClientOriginalName(), '.')+1));#strtolowerで大文字を小文字に戻し、substrで文字を切り出す。これで、写真の拡張子を取得する。
            if(!in_array($ext, ['png', 'jpg', 'gif', 'jpeg'], true)) {#['png', 'jpg', 'gif', 'jpeg']の配列に$extの値があるかチェックする。
                $tag_view = '画像以外のファイルが指定されています。画像ファイル(png/jpg/jpeg/gif)を指定して下さい';
                return view('tweets.tag', compact('tag_view'));
                #tweets.tagのViewにcompactでtag_viewの情報を渡して表示させる。
            }

            $imageFile = time(). '_' . $name;
            $imagePath = storage_path('app/public/') . $imageFile;
            $image = Image::make($filename)#imageを作成する
                ->resize(1000, null, function ($constraint) {
                    $constraint->aspectRatio();#横幅を1000にして縦横比を保持したまま変更を行う。
                    $constraint->upsize();#小さい写真を無理やり1000にすることをせずにそのままのサイズを維持する。
                })
                ->orientate()#画像の向きを自動的に調整する。
                ->save($imagePath);#その情報を一旦ローカルに保存する。

            $path = Storage::disk('s3')->putFile('myprefix',$imagePath, 'public');#config/filesystems.phpの中に設定した情報からawsのs3にファイル名は自動で生成し保存する。
            $tweet->image = Storage::disk('s3')->url($path);

            Storage::disk('local')->delete('app/public/' . $imageFile);#ローカルの情報を削除する。
        } 

        preg_match_all('/#([a-zA-Z0-9０-９ぁ-んァ-ヶー一-龠]+)/u', $request->tag_box, $match);#ハッシュタグで始まる単語を取得。結果は、$matchに多次元配列で代入される。

　　　　 # $match[0]に#(ハッシュタグ)あり、$match[1]に#(ハッシュタグ)なしの結果が入ってくるので、$match[1]で#(ハッシュタグ)なしの結果のみを使う。
        $tags = [];
        foreach ($match[1] as $tag) {
            $found = Tag::firstOrCreate(['tag_name' => $tag]);#firstOrCreateメソッドで、tags_tableのnameカラムに該当のない$tagは新規登録される。
            array_push($tags, $found);#$foundを配列に追加します(=$tags)
        }

        #投稿に紐付けされるタグのidを配列化
        $tag_ids = [];
        foreach ($tags as $tag) {
            array_push($tag_ids, $tag['id']);
        }

        $tag_count = count($tag_ids);#tagの数を取得する。
        if ($tag_count <= 5){#もしtagの数が5以下ならtrueを実施
            $tweet->save();
            $tweet->tags()->attach($tag_ids);#投稿ににタグ付するために、attachメソッドをつかい、モデルを結びつけている中間テーブルにレコードを挿入

            return redirect('/top');
        } else{
            $tag_view = 'タグ数が５つ以上ですので変更してください。';
            $tweet_id = $id;
            return view('tweets.tag-edit', compact('tag_view','tweet_id'));
        }
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Models\Tweet  $tweet
     * @return \Illuminate\Http\Response
     */
    public function show(Tweet $tweet)
    {

        $tweetid = $tweet->id;
        $comments = Comment::where('tweet_id', '=', $tweetid)->get();#commentの情報をtweet_idと$tweetidの情報が同じならその情報を取得する。

        return view('tweets.show',[
            'tweet' => $tweet,
            'comments' => $comments,
        ]);

    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\Models\Tweet  $tweet
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        $tweet = Tweet::with(['user','comments'])->findOrFail($id);#[userとcomments]のModel情報を取得し、$id(パラメーター)の情報から取得する。
        return view('tweets.edit',[
            'tweet' => $tweet,
        ]);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Models\Tweet  $tweet
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request)
    {
        #ここはstoreクラスとほとんど同じ
        $id = $request->tweet_id;
        $tweet = Tweet::findOrFail($id);
        $tweet->content = $request->content;
        $tweet->title = $request->title;
        $tweet->user_id = $request->user_id;
        $tweet->tag_box = $request->tag_box;

        if($request->hasFile('image')){
            $filename = $request->file('image');
            $name = $filename->getClientOriginalName(); 
            $ext = strtolower(substr($filename->getClientOriginalName(), strrpos($filename->getClientOriginalName(), '.')+1));
            if(!in_array($ext, ['png', 'jpg', 'gif', 'jpeg'], true)) {
                $tag_view = '画像以外のファイルが指定されています。画像ファイル(png/jpg/jpeg/gif)を指定して下さい';
                return view('tweets.tag', compact('tag_view'));
            }

            $imageFile = time(). '_' . $name;
            $imagePath = storage_path('app/public/') . $imageFile;
            $image = Image::make($filename)
                ->resize(1000, null, function ($constraint) {
                    $constraint->aspectRatio();
                    $constraint->upsize();
                })
                ->orientate()
                ->save($imagePath);

            $path = Storage::disk('s3')->putFile('myprefix',$imagePath, 'public');
            $tweet->image = Storage::disk('s3')->url($path);

            Storage::disk('local')->delete('app/public/' . $imageFile);
        }

        preg_match_all('/#([a-zA-Z0-9０-９ぁ-んァ-ヶー一-龠]+)/u', $request->tag_box, $match);

        $tags = [];
        foreach ($match[1] as $tag) {
            $found = Tag::firstOrCreate(['tag_name' => $tag]);
            array_push($tags, $found);
        }

        $tag_ids = [];
        foreach ($tags as $tag) {
            array_push($tag_ids, $tag['id']);
        }


        $tag_count = count($tag_ids);
        if ($tag_count <= 5){
            $tweet->save();
            $tweet->tags()->sync($tag_ids);

            return redirect('/top');
        } else{
            $tag_view = 'タグ数が５つ以上ですので変更してください。';
            $tweet_id = $id;
            return view('tweets.tag-edit', compact('tag_view','tweet_id'));
        }

    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Models\Tweet  $tweet
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        #Tweetモデルから$idを見つけ出す。
        $tweet = TWeet::find($id);


        preg_match_all('/#([a-zA-Z0-9０-９ぁ-んァ-ヶー一-龠]+)/u', $tweet->tag_box, $match);

        $tags = [];
        foreach ($match[1] as $tag) {
            $found = Tag::firstOrCreate(['tag_name' => $tag]);
            array_push($tags, $found);
        }

        $tag_ids = [];
        foreach ($tags as $tag) {
            array_push($tag_ids, $tag['id']);
        }


        $tweet->tags()->delete($tag_ids);#deleteで削除する。
        $tweet->delete();#上記と同じ
        return redirect('/top');

    }

    public function search(Request $request)
    {

        $tweets = Tweet::where('title' ,'like', "%{$request->search}%")
        ->orwhere('content' ,'like', "%{$request->search}%")
        ->paginate(10);#Tweetモデルから情報を取得し、where句を使用してパラメータで取得した値をtitleカラムとcontentカラムの中に同じ値があるかを取得する。でそのページネーションを10で表示させる。ものを$tweetsに入れる。

        $search_result = '【'. $request->search. '】の検索結果は'.$tweets->total().'件';#検索情報と検索にヒットした数を$search_resultに入れる。

        $tags = \DB::table('tags')->get();
        return view('tweets.index',[
            'tweets' => $tweets,
            'search_result' => $search_result,
            'search_query'  => $request->search,
            'tags' => $tags,
        ]);
    }
}
```

###次はModelの解説していきます！

```php:app/Models/Tweet.php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;


class Tweet extends Model
{
    protected $table = 'tweets';

    protected $fillable = [
        'title', 'user_id','tag_box','content', 'image',
    ];

    public function user(){
        return $this->belongsTo(\App\Models\User::class,'user_id');
    }#tweetとuserとの１対多の関係

    public function tags(){
        return $this->belongsToMany('App\Models\Tag');
    }#tweetとtagとの多対多の関係

    public function comments(){
        return $this->hasMany(\App\Models\Comment::class,'tweet_id', 'id');
    }#tweetとcommentとの１対多の関係
}
```

###最後にViewの解説をします！

```php:resources/views/tweets/index.blade.php
@extends('layouts.app')#部分テンプレート

@section('content')

<h2 class="card-header" style="text-align: center;">
  投稿一覧
</h2>


    @isset($search_result)#$search_resultの情報が入っていればtrueで実施
    <h5 class="card-title" style="text-align: center; padding-top: 30px; font-size: 20px; color: #55c500">{{ $search_result }}</h5>
    @endisset

<div class="row m-3">
        <div class="col-sm-3">
        <h5 class="card-title"><i class="fas fa-tags"></i>タグ一覧</h5>
            @foreach($tags as $tag)#Controllerから$tagsの情報を取得して$tagに入れて回す。
                <a href="{{ route('tweets.index', ['tag_name' => $tag->tag_name]) }}" class="btn btn-outline-success m-1">
                    {{ $tag->tag_name }}#tag_nameを表示させる
                </a>
            @endforeach
        </div>
        <div class="col-sm-9">
            @if (session('status'))
                <div class="alert alert-success" role="alert">
                    {{ session('status') }}
                </div>
            @endif
            @foreach ($tweets as $tweet)
            <div class="toast fade show" role="alert" aria-live="assertive" aria-atomic="true">
                <div class="toast-header">
                    <strong class="mr-auto">
                        <a href="{{ route('users.show', $tweet->user_id) }}" class="btn btn-outline-primary btn-sm">{{ "@".$tweet->user->name }}</a>
                        が
                        <span class="text-muted" style="font-size:15px;">{{ $tweet->created_at->format('Y年m月d日')  }}にストック</span>
                    </strong>
                </div>
                <div class="toast-body">
                    <h5 class="card-title">
                        <i class="fas fa-tags"></i>
                        @foreach($tweet->tags as $tag)
                            <a href="{{ route('tweets.index', ['tag_name' => $tag->tag_name]) }}" class="badge badge-success">
                                #{{ $tag->tag_name }}
                            </a>
                        @endforeach
                    </h5>
                    <a href="{{ route('tweets.show', $tweet->id) }}" class="text-dark">
                        <h2 class="card-title">{{ $tweet->title }}</h2>
                    </a>
                </div>
            </div>
            @endforeach


            @if(isset($tag_name))#もし$tag_nameに情報があればtrueで実施
                {{ $tweets->appends(['tag_name' => $tag_name])->links() }}#tag_nameを基にペジネーションリンクにクエリ文字列を付け加えたいときは、appendsメソッドを使用する。linksメソッドは結果の残りのページヘのリンクをレンダーする。
            @elseif(isset($search_query))#もし$search_queryの値が入っていればtrueを実施
                {{ $tweets->appends(['search' => $search_query])->links() }}
            @else
                {{ $tweets->links() }}
            @endif

        </div>
</div>
@endsection
```

とりあえず、これでアウトプットとします。
自分なりのアウトプットですので間違い等はあるかもしれませんが、自分のための投稿しています。