BeginnerEngineerBlog
中の人
中の人

勝手にlaravelチュートリアル #4の2

公開: 2021-09-02 22:03
更新: 2023-05-11 01:39
969
php laravel6.x #4の2 validation 1対多
今回はコントローラに渡されたリクエストをデータベースに保存して、表示するところまでやろうと思います。前回登場しなかったMVCパターンのMがついに登場します!さらに、バリデーションやリレーションといったlaravelの機能を使いながら、アプリケーションの質を高めていきます!結構長い記事になりますが、頑張ってください!
変更履歴
2021/09/12 validationのリダイレクト処理を編集しました。
2023/05/11 $validator->fails()のくだりでタイポと返り値が間違ってたので修正しました

こんにちは!

前回はMVCパターンに触れながら、viewからrequestを送信してコントローラで値を確認するところまでやりました。


今回は送信されたデータをデータベースに保存して、保存した値をviewで表示するところまでやろうと思います!合わせて、MVCのM(モデル)を利用してリレーションを設定したり、リクエストの値を見て処理を制限するバリデーションなども設定していきたいと思います。先に言っておきます。長くなります!気合い入れてやっていきましょう!!🔥🕺🔥

  1. チュートリアル説明
  2. プログラミングの準備(エディタ、dockerの用意、環境構築)
  3. 認証機能を追加してログイン機能を実装する
  4. ブログのタイトルと記事を登録する(今ここ)
  5. ブログの記事へタグを登録する
  6. ブログの一覧表示、編集機能の実装
  7. ブログの検索機能の実装
  8. ユーザーへの通知機能の実装


さて、それでは、viewから送信されたブログのタイトルと記事を保存したいところですが、保存先がまだありませんね。

ということで、データベースに新しくテーブルを作成しましょう!


articlesテーブルの作成


まず、実際にテーブルを作成する前に、便利ツールを利用してテーブルのイメージを固めていこうと思います。
ブラウザに、

http://localhost:3000

と入力してください。


はい、こんな画面が表示されたかと思います。

これは環境構築の際にちらっと説明しましたが、データベースのER図(テーブル間の関係を表す図)を簡単に確認、作成できるsqldesignerいうツールです。

データベース設計は複雑になることが多いので、こういったツールを活用することで、実際にテーブルを作成する際に混乱しなくて済むようになります。(混乱するときは混乱します。)

ということで、これから作るarticles(記事)テーブルをこのツールでイメージしていきましょう!

左上のアイコンをクリックして、「SAVE / LOAD」をクリックしましょう。




そしたら、SERVERをクリックし、Server backendにphp-mysqlを選択して、IMPORT FROM DBを選択してください。


するとこんなアラート的なやつが表示されるので、データベース名を入力してください。


はい、こんな感じでテーブル一覧が表示されたと思います。

ここにarticlesテーブルを作っていきます。
(ちなみにですが、私だけかもしれませんが、この画面上で右クリックするとバグります。ので、バグったらリロードなどしてリセットしてください。あと、各テーブルの上側をドラッグすると、テーブルの位置を動かすことができますが、一番左上のテーブルはアイコンの要素が邪魔して動かせません。検証ツールでアイコンのcssにdisplay: noneを指定すると動かせるようになります。検証でcssを設定するやり方については調べてみてください!)

ということで、図を作成する前に、簡単にデータベース設計の基本を説明します。基本的にテーブルは

  • 1対1
  • 1対多
  • 多対多

の三つの関係(リレーション)を意識して設計していきます。

・「1対1」は、例えばusersテーブルとマイナンバーテーブルがあったとしましょう。
マイナンバーてuser一人に対して一つしかないですよね?複数userで同じマイナンバーを持つことてないですよね?そういうときが該当します。
ただ、1対1であれば、そのままusersテーブルに定義しちゃっていいと思いますので、個人的には1対1ではテーブルを作成しません。(マスタとしてテーブルを持っている場合は1対1に設定することもあります。)

・「1対多」は、例えばまさにブログの記事。記事はuser一人につき、複数投稿できますよね?ということは、userからして記事は「多」になります。で、記事の投稿userて一人ですよね?つまり、記事からするとuserは「1」になります。そういうことです。なので、今回は1対多の関係を作るように設計していきます

・「多対多」は、今後やる予定の「タグ」のような関係です。タグていろんな記事に同じタグがついてたりしますよね?この勝手にlaravelチュートリアルシリーズにも、大体「php」「laravel6.x」というタグが付いています。つまり、一つの記事に複数タグを設定することができるということは、記事からしたらタグは「多」になります。で、前述したように複数の記事に対し、同じタグが設定されてますよね。つまり、タグからしてみても、記事は「多」になります。両者から見て、お互いに「多」の関係がある場合、「多対多」となるわけです。「多対多」は最初はよくわからないかと思いますが、今後タグの機能を実装するときに実際に設定してみてイメージをつかんでください。

ということで、articlesテーブルをusersテーブルとの関係を「1対多」で設計していきます!


+マークをクリックして、適当にマウスでクリックすると画像のような画面になります。
Nameをarticlesと書いて、OKをクリックしてください。(Commentは入力してもしなくてもオッケーです。)


するとこんな感じでarticlesが作成されます。
これに、ブログのタイトルと記事を保存するフィールド(カラム)を作っていきます。

そしたら、この赤枠がついた状態(対象のテーブルが選択された状態)で、


画像の+マークを押すと、新しいフィールドを追加できるようになりますので、

Nameはtitle
TypeはVarchar
NULLのチェックボックスをはずしましょう

Nameはフィールドの名前
Typeはフィールドの型(Varcharは文字列)
NULLのチェックボックスはテーブルのレコードを作成する際に、このフィールドに何も保存しなくてもいい場合はチェック(Null)、何かしら値がなければいけないときはチェックを外す(Not Null)という感じです。

今回ブログを保存する際は、タイトルは必ず入力しなければならない前提で進めますので、NULLのチェックを外して、必須にしています。(DefaultのNULLは無視してます。気にせずいきましょう。)


はい、で、適当な箇所をクリックすると、フィールドが追加されます。


はい、こんな感じでやっていきます。(その他の操作は適当にぽちぽちして確認してみてください。)

そしたら、今度は記事のフィールドを作成しましょう。


はい、今度はcontentという名前でフィールドを作ります。
TypeはTextとなってますが、これはVarcharよりも長い文字を保存することができます。

で、これもNot Nullを指定します。

タイトル、記事と保存するフィールドを作成しました。
もう一つフィールドが必要です。

先ほどリレーションの1対多を説明しましたが、「このブログは一体誰が投稿したブログなのか」がこのままだとわかりませんよね?

ということで、投稿者のidを保存するフィールドを作成します!


はい、こんな感じで作りましょう。

usersテーブルのidを格納するので、user_idという名前で作ります。(後述しますが、laravelのルールに則っています。)
TypeはBIGINTといって、数字を保存する型になります。(これも後述します。)

で、このフィールドもNot Nullを指定します。投稿userは必ずいるはずですからね。

はい、そしたら、今こんな感じになってますね。



で、このままだと、テーブルが多くなってくるとわけわからん状態になるので、どのフィールドとどのフィールドにリレーションが設定されているのか分かりやすくします。

usersテーブルの「id」をクリックしてください。


はい、>>というマークが出まして、さらに下のツールの→←というアイコンがクリックできるようになりました。

そしたら→←アイコンをクリックしてください。


Click target rowと出るので、リレーションを設定する相手テーブルのフィールドをクリックします。

今回はarticlesテーブルのuser_idフィールドですね。


はい、こんな感じで紐づきました。

わかりやすいですね!


そしたら、イメージできたと思いますので、実際にテーブルを作成していきましょう!

そしたら、ターミナルを開いて、appコンテナの中に入ってください。


そしたら
php artisan make:migration create_articles_table

と入力してエンターを押してください。


はい、migrationがcreateされましたね。

app/database/migrationsディレクトリの中を見てください。


はい、先ほど作成したマイグレーションファイルが追加されてますね。

そしたら中身を編集していきましょう!

<?php


use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;


class CreateArticlesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title');//<-追加
            $table->text('content');//<-追加
            $table->unsignedBigInteger('user_id');//<-追加
            $table->timestamps();

       //↓追加
            $table->foreign('user_id')
                ->references('id')
                ->on('users')
                ->onDelete('cascade');
        });
    }


    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('articles');
    }
}



上記のように編集してください。
マイグレーションファイルについては、勝手にlaravelチュートリアル#3で少し説明しましたが、自分で作成するのは初めてですね。


public function up()

これは

php artisan migrate

というコマンドで実行する内容を記載します。

public function down()

は、

php artisan migrate:rollback

などで実行する内容を記載します。

ではupの内容を見てみましょう

$table->bigIncrements('id');
$table->string('title');
$table->text('content');
$table->unsignedBigInteger('user_id');
$table->timestamps();


$table->foreign('user_id')
    ->references('id')
    ->on('users')
    ->onDelete('cascade');

デフォルトで記載されているidbigIncrements(数字 AUTO INCREMENT)で、特に指定しなくても数字を1から順に振っていってくれます。また、主キーといって、このテーブルのレコード(テーブル内のデータの行)を区別するキー(なんて言うか、テーブルを代表するフィールド的な)が設定され、indexという検索を早くする設定も自動でしてくれます。
また、
$table->timestamps();

こちらもデフォルトで記載されていますが、これはcreated_at(データを作成した日時)、updated_at(データの変更(作成も含む)があった日時)のフィールドを作成する記述になります。
基本的にはidとtimestampsは特段の理由がない限りそのままにしておきましょう。

で、
titlestring(文字)で、ツールでイメージを作った際にVarcharと指定した箇所になります。laravel側ではstringと指定します。
contenttext(文字)で、ツールでイメージを作った際と同じです。

user_idunsignedBigInteger(数字)で、0から正の数のみ保存するよう指定します。(idにマイナスはないので)

$table->foreign('user_id')
    ->references('id')
    ->on('users')
    ->onDelete('cascade');

これは、user_idフィールドに、usersテーブルのidフィールドを外部キー制約として設定する記述になります。

この外部キーとは、usersテーブルのidが存在しないと保存できないようにする設定のことです。これもindexが貼られます。

また、onDelete('cascade')により、外部キーを設定したuserがいなくなった時(usersテーブルから削除したとき)、このレコードも一緒に削除される設定になります。(ちなみに、ユーザーを削除してもデータを残したい場合はこの記述は不要です。お好みでどうぞ。なんですが、慣れていないと予期しないところでエラーが発生する可能性があるので、その場合はご自身で調べて解決してみてください。)

そして、

$table->unsignedBigInteger('user_id');

unsignedBigIntegerは、相手のusersテーブルのidフィールドと同じ型(bigIncrements)になります。名前は違いますが型は同じです。リレーションを設定する際は、相手のフィールドの型と同じ型にしないと(確か)エラーとなってしまうので、覚えておきましょう。

あと、downに書いてある処理は、このテーブルを削除する内容が書かれています。upはテーブルを作成する内容なので、downは削除する内容ということですね。


はい、色々と説明が長くなりましたが、早速このマイグレーションファイルを実行しましょう。

php artisan migrate

と入力してエンターを押してください。


成功すると、上のような感じで終了します。

そしたら、

http://localhost:4040/

にアクセスして、実際にテーブルが作成されているか確認しましょう。


はい、ちゃんとテーブルが作成されてますね!

さぁ、あと少しでブログを登録できるようになります!
頑張ってください!私も頑張ってます!


ということで、早速ブログのタイトルと記事を保存したいのですが、まだやることがあります。🕺 < マダアルンカイ!!

モデルを作成します。やっとMVCのM(モデル)の登場です!


modelの作成


モデルとはテーブルへの保存処理や更新処理などしやすくしてくれるものになります。

実際にコードを書きながらなんとなくイメージを掴んでください。

そしたらターミナルで、

php artisan make:model Article

と入力してエンターを押してください。


はい、Modelがcreateされました。

appディレクトリの中を見てください。


Article.phpが作成されてますね!

早速中身を見てみましょう!

<?php


namespace App;


use Illuminate\Database\Eloquent\Model;


class Article extends Model
{
    //
}

はい、こんな中身ですね。

そしたら編集しましょう!

<?php


namespace App;


use Illuminate\Database\Eloquent\Model;


class Article extends Model
{
    ↓追加
    protected $fillable = [
        'title', 'content', 'user_id',
    ];
}

こんな感じで編集しましょう。

$fillableは、テーブルのフィールドのうち、操作していいフィールドを指定します。
例えば、idフィールドは、何もしなくても数字が保存されていきます。これを、外部(僕ら開発者など)から変更されたら、データの整合性が合わなくなる可能性が高いですよね。(articleテーブルのuser_idの参照先であるusersテーブルのidが変更されたら、投稿者が別のuserになってしまいますよね)
なので、操作していいフィールドを設定します。これを設定しないと、値を保存できません。(ちなみに、$guardedという逆に操作してはいけないフィールドを指定することもできます。個人的にはこちらを設定することが多いです。)

さて、このファイルの中身にはarticlesテーブルを参照するような記述が何も書かれてないですよね?これはlaravelの標準の機能を利用していて、テーブル名を指定しない場合、laravelはこのモデルのclass名から、スネークケース(小文字の単語をアンダーバーでつなげて書く書き方)の複数形で作成されているテーブルを探すようになります。つまり、Articleのスネークケースはarticleで、その複数形ということは、articlesですね。テーブル名になりました!
ということで、modelを作成する際は、特段の理由がない限りこのルールに則って作成した方が楽だし、わかりやすくなると思います。

そしたらArticleControllerのcreateアクションを編集して保存処理を実装しましょう!


modelを使ってブログ保存処理を実装する


<?php


namespace App\Http\Controllers;


use Illuminate\Support\Facades\Auth;//<-追加
use App\Article;//<-追加
use Illuminate\Http\Request;


class ArticleController extends Controller
{
    public function new ()
    {
        return view('article.new');
    }


    public function create(Request $request)
    {
        //↓色々追加
        $title = $request->get('title');
        $content = $request->get('content');
        $user_id = Auth::id();
        Article::create(
            [
                'title' => $title,
                'content' => $content,
                'user_id' => $user_id,
            ]
        );


        return redirect('/');
    }
}


はい、こんな感じで編集しましょう。

useも忘れずに記載してください。

$titleや$contentは変数です。以前に紹介したデータの入れ物ですね。
$request->get('title');などは、requestに渡されたパラメータから、値を取得するための記述です。(ちなみに、$request->titleみないな書き方でも取得できます。)

Auth::id();これは、Authというクラスをuseして、ログインユーザーのidを取得する記述になります。ログインしていない場合、$user_idはnull(値なし)となります。

$title = $request->get('title');

この書き方はつまり、$titleという変数にリクエストのパラメータにあるtitleの値を入れるという意味になります。

そして、Article::createの「Article」これがモデルになります。MVCのMです!

データベースに値を保存などする際は、本来はSQL文という、sql用の言語の記述が必要になります。

それを

Article::create(
    [
        //↓key     ↓value
        'title' => $title,
        'content' => $content,
        'user_id' => $user_id,
    ]
); 

この記述で、内部的にsql文を作成してくれて、実行してくれているのです。

sql文について少し調べてみればわかるかと思いますが、非常に簡単で、わかりやすいですね!

ちなみに、keyの'title'や'content','user_id'は、articlesテーブルのフィールド名になります。
まぁ、これもなんとなくわかっていればオッケーです。(今後モデルの利用が多くなってきますので、いやでもわかってくるかと思います。)

ということで、値を保存する準備が整いましたね!

そしたらみなさん一旦ログインしてください。
なぜなら、ログインしないと投稿ユーザーのidが取得できないためです!
ログインしましたか?

では記事を投稿してみましょう!


はい、適当に入力して、送信ボタンをクリックしてください。


はい、いい感じに値がとれてますね!
user_idも取得できています!

そしたらphpmyadminにアクセスして、articlesテーブルを見てみましょう!


ちゃんと保存されてますね!

やったぜ!

ついにブログの投稿に成功しましたね!

ひとまずお疲れ様した。



はい、まだ終わりせん。

今度は、保存した値を表示しましょう!

今回の記事はまだまだ終わりません!頑張りましょう!


保存したデータを表示する


さて、それでは、ブログの閲覧ページを作ります。


はい、こんな感じでshow.blade.phpを作成しましょう。

で、ひとまずhello world!!とだけ書いておきましょう。

そしたら、web.phpを開いてrouteを設定します。

Route::get('/article/show/{id}', 'ArticleController@show')->name('article.show');

こんな感じで設定しましょう。

ここでの{id}ですが、これはこの部分にarticleのidを設定するという意味になります。(ちなみに{}の中に書く内容はなんでも大丈夫です)

そしたらArticleControllerへshowアクションを定義しましょう!

public function show(Request $request)
{
    $id = $request->route('id');
    $article = Article::find($id);
    return view(
        'article.show',
        [
            'article' => $article,
        ]
    );
}

はい、こんな感じで定義しました。

$id = $request->route('id');

これは、先ほどrouteで設定した{id}の部分を取得する書き方になります。

$article = Article::find($id);

これは、articlesの主キー(id)から、引数で渡した$idに一致するレコード(モデル)を取得する書き方になります。

return view(
    'article.show',
    [
        'article' => $article,
    ]
);

これはviewで作成したshow.blade.phpへ、取得したarticleのレコード(モデル)を渡す書き方になります。

他にもviewへ値を渡す書き方はあるので、気になった方はいろいろ調べてみてください。


そしたら、ブログを投稿したら、そのままブログ閲覧ページを表示させるようにします。
ArticleControllerのcreateアクションを編集しましょう!

public function create(Request $request)
{
    $title = $request->get('title');
    $content = $request->get('content');
    $user_id = Auth::id();
    //↓変数追加
    $article = Article::create(
        [
            'title' => $title,
            'content' => $content,
            'user_id' => $user_id,
        ]
    );


    return redirect()->route('article.show', ['id' => $article->id]);//←リダイレクトの書き方を変更
}

上記のように修正してください。

$article = Article::create(

これは、作成されたarticleのモデルを変数に代入する記述になります。

return redirect()->route('article.show', ['id' => $article->id]);

これは、このように書くと、routeで設定したnameを見てリダイレクトしてくれます。
また、route関数の第2引数(['id' => $article->id]の部分)へこのように記載すると、routeのurlの/article/show/{id}のidを設定することができます。
そして、$article->idとは、作成されたarticleのモデルのidを取得する書き方になります。
つまり、ブログが登録されたら、その登録されたブログのidを持ってshowアクションを実行するということですね。

はい、そしたら、一回ちゃんと閲覧ページが表示されるか、適当にブログを投稿してみて確認してください。
できましたか?
必要であればxdebugを使って処理の流れを確認してみてください!

それでは、show.blade.phpでブログのタイトルと記事を表示できるようにします。
show.blade.phpを以下のように編集してください。

@extends('layouts.app')
@section('content')
    title: {{$article->title}}
    <br>
    内容:
    <br>
    {!!nl2br(e($article->content))!!}
@endsection

はい、
{{$article->title}}

急に$articleという変数が出てきてどういうこと?と感じたかもしれませんが、これは先ほどcontrollerのshowアクションでviewに渡した

'article' => $article,

のkeyの部分です。

例えば

'test' => $article,

と書いた場合、view側では

title: {{$test->title}}

と書くことができます。
この変数にはarticleのモデルが渡ってますので、titleは->で取得できます。

{!!nl2br(e($article->content))!!}

これは、textareaで入力したcontentを、改行ありで表示する書き方になります。(htmlとして表示する書き方です。)

で、e()これはクロスサイトスクリプティング(XSS)という脆弱性を考慮した書き方になります。気になる方は調べてみましょう!
(contentへ<script>alert(攻撃しました!)</script>を保存して、e()関数なしで表示してみるとよくわかると思います。)

はい、そしたらもう一度ブログを適当に入力して送信してみてください。



はい、こんな感じでご自身が送信した内容が表示されましたかね?

されていれば成功です!

やったぜ!
これでブログ投稿し放題です!

わーいわーい🙌


・・・と、はしゃいでるそこのあなた!甘いです!大甘です!!チョコレートのように大甘です!!!🔥🍫 < アッツイシウルセー!

みなさんも気づいていると思いますが、このままではブログの保存と表示の処理が甘々です。

例えば、ブログを投稿する際に、ログインせず投稿したらuser_idが取得できず、エラーになります。∑(゚Д゚)! < ナニ!?

また、articleのidが存在しないidを指定してshowアクションを実行すると、これまたエラーになります。∑(゚Д゚)! < ナニ!?

さらにさらに、title,contentを未入力で送信するとこれまたエラーになります。∑(゚Д゚)! < ナニィィ!?

これではアプリケーションとして使い物にならないですよね?

さらにいうと、ブログのタイトルと記事は表示されたけど、投稿者の名前がわからないですよね?

ということで、詰めが甘い部分を修正していきます!

喜ぶのはまだ早いです!まだ終わりませんよー!!


middleware,validationなどを設定する


さて、そしたら、まずはログイン状態じゃないとブログの投稿ページに遷移できないようにしましょう!

web.phpを開いてください。

で、投稿ページを表示するrouteを修正します。

Route::get('/article/new', 'ArticleController@new')->name('article.new')->middleware('auth');
Route::post('/article/create', 'ArticleController@create')->name('article.create')->middleware('auth');

はい、こんな感じで修正しましょう。

->middleware('auth');

これはミドルウェア(middleware)といって、処理に介入することができるものになります。で、この記述はログインしていないと遷移することができないようにする記述です。middlewareは自作することもできますので、気になった方はご自身で調べてみてください!

そしたら、ログアウトしたあと、ブログ投稿ページ(http://localhost:8080/public/article/new)にアクセスしてみてください。


ログイン画面にいきましたね!

こんな感じで、middlewareでアクセスを制限することができるようになります!

そしたらログインしましょう。



はい、ブログ投稿ページに行きましたね!便利!

これで、ログアウト状態で

  • ブログ投稿ページには行けない
  • ブログを投稿できない

ようになりました。

ブログ投稿時に投稿者のuser_idが取得できないといった心配はなくなりましたね!

第一関門突破です!


次に、ブログの閲覧ページで、articleのidがない場合のエラーハンドリングを設定しましょう!

まず、web.phpを開いてください。

で、showアクションのrouteを編集します。

Route::get('/article/show/{id}', 'ArticleController@show')->name('article.show')->where('id', '\d+');

はい、追加した

->where('id', '\d+');

の記述は、routeの{id}の値を制限するもので、この書き方だと数字のみ受け付ける設定になります。{id}に「a」とか「あ」とか入力されても、idは数字なので探しようがないですよね?で、

'\d+'

これは正規表現と言われるものです。気になった方は調べてみましょう!きっと最初は訳わからないと思います!

そしたら、ちゃんと数字のみ受け付けるかテストしてみてください。

http://localhost:8080/public/article/show/a

適当にこんなんでアクセスしてみてください。



はい、こんな画面が表示されたと思います。404は文字通りnot foundです。ルートが見つかりません的な。ひとまずオッケーですね!

ただ、これだけだと、数字は受け付けているので、存在しないidを入力された場合のハンドリングをします。

ArticleControllerのshowアクションを編集しましょう。

public function show(Request $request)
{
    $id = $request->route('id');
    $article = Article::find($id);
    ↓//追加
    if (!isset($article)) {
        abort(404);
    }
    return view(
        'article.show',
        [
            'article' => $article,
        ]
    );
}

はい、

if (!isset($article)) {
    abort(404);
}

これはif文といって、もし~~ならこうします。という記述になります。

「!」はnullとかfalseとか0とかの場合trueを返します

「isset()」は、値がnull、または未定義の変数などの場合falseを返します。

abort(404)とは、先ほどの404のエラー画面を表示する記述になります。

つまり、もし、articleのモデルが取得できなければ(nullならば)、404画面へ遷移させます。という意味になります。

適当にidを入力してテストしてみてください。存在しないidを渡した場合404画面が表示されたらオッケーです!
(ちなみに、結局エラー画面表示するんじゃんと感じる方もいるかと思いますが、自分で意図して発生させるエラーと、意図しないで発生するエラーでは意味が違います。不正なアクセスがあった場合、問答無用でエラーとするのも、アプリケーションでは必要なことです。)


これで、viewに空の変数を渡す心配がなくなりましたね!
第二関門突破です!いい感じです!


そしたら次に、ブログのtitleとcontentの入力を必須にするなどの設定をします!

ArticleControllerのcreateアクションを編集しましょう。

<?php


namespace App\Http\Controllers;


use Illuminate\Support\Facades\Validator;//<-追加
use Illuminate\Support\Facades\Auth;
use App\Article;
use Illuminate\Http\Request;


class ArticleController extends Controller
{
    public function new ()
    {
        return view('article.new');
    }


    public function create(Request $request)
    {
        //↓色々追加
        $validator = Validator::make($request->all(), [
            'title' => [
                'required',
                'string',
                'max:25',
            ],
            'content' => [
                'required',
                'string',
                'max:4000',
            ],
        ]);
        $validator->validate();
        $title = $request->get('title');
        $content = $request->get('content');
        $user_id = Auth::id();
        $article = Article::create(
            [
                'title' => $title,
                'content' => $content,
                'user_id' => $user_id,
            ]
        );


        return redirect()->route('article.show', ['id' => $article->id]);
    }
        //~~省略
}


はい、まず、忘れずにValidatorクラスをuseしましょう。

で、

$validator = Validator::make($request->all(), [
    'title' => [
        'required',
        'string',
        'max:25',
    ],
    'content' => [
        'required',
        'string',
        'max:4000',
    ],
]);

これは、requestのパラメータを全て取得して、バリデートしたい項目を設定する書き方です。
バリデートとは、値を制限するバリアみたいなものです。

ここで記載している'title'や'content'は、viewのinputのnameを指定しています。
'required'は入力必須、'string'は文字列、'max:○○'は、stringを指定している場合、文字の長さを制限する書き方になります。

バリデートで指定できる項目はいっぱいありますので、気になった方は調べてみてください!

$validator->validate();

で、これは、もしバリデートで何かしら引っ掛かったら、入力画面(直前にいた画面)へリダイレクトさせる書き方になります。つまり、バリデートに引っ掛かったら、この後に記述している処理をさせないということですね。

では、テストで未入力、もしくはいっぱい文字を入力して送信してみてください。


(2022/01/03追記
$validator->fails()
これはバリデーターで設定した制限で何か一つでも引っかかったらtrueを返す関数です。
いきなりこの関数が出てきて、え!?ってなった方すいませんでした。。。
)

はい、画像では、title、contentどちらも未入力の状態で送信したものとなりますが、ちゃんとif文の中を通ってますね!
みなさんもうまく動いているでしょうか?

そしたら、入力画面が表示されたはずです。

動作はオッケーですね!


ただ、このままだとバリデートでリダイレクトしたとき、入力した内容がなくなってますよね?(片方だけ入力して送信してみてください。)

これだと、もし記事を4000文字以上入力してしまってバリデートに引っかかってしまった場合、書いた4000文字が消えて無駄になってしまいます。そうなったらとても悲しいですよね。私なら悲しさのあまり、全裸でお隣さんちに駆け込んで、ちゃぶ台の上で踊り狂ってしまうでしょう。

そんな公然わいせつ犯罪者を生み出さないよう、しっかり対策しましょう!

new.blade.phpを編集します。

@extends('layouts.app')
@section('content')
    <form action="{{route('article.create')}}" method="post">
        @csrf
        <input name="title" type="text" value="{{old('title')}}">//←編集
        <textarea name="content" cols="30" rows="10">{{old('content')}}</textarea>//←編集
        <input type="submit">
    </form>
@endsection

はい、こんな感じで編集しましょう。

{{old('title')}}この書き方は、laravelのヘルパー関数で、直前に入力した内容を取得する書き方になります。

それでは、ご自身でテストしてみてください。

直前に入力した内容が残ってますか?残っていれば成功です!!

第三関門突破です!

やったぜ!これで全裸で捕まる人が少なくなりましたね!!お隣さんも一安心です!


さて、最後の関門です。
投稿者の名前を表示させます!

これをやって、この記事を終わりにしたいと思いますので、あとちょっと、力を振り絞ってください!

そしたら、Articleモデルへリレーションを設定します。


ArticleモデルにUserモデルを取得するリレーションを設定する


ようやくリレーションという言葉が出てきました!長かったここまで!

そしたら、Articleモデルから、投稿者であるuserを取得するために、1対多の関係でリレーションを設定していきます。

Article.phpを開いてください。

で、以下をコピペして追加してください。

public function user()
{
    return $this->belongsTo('App\User');
}

return $this->belongsTo('App\User');

これは、リレーションの相手が一対多の一に該当する場合に設定する書き方になります。

'App\User'は、app/User.phpのことで、要はuserモデルを指しています。Appと大文字になっているのは、laravelの仕様みたいです。

で、articlesテーブルを作成したとき、user_idというフィールド名はlaravelのルールに則っていると説明しました。

これは、相手のモデル名のスネークケース + idという書き方をすることによって、特に参照するフィールドを記述しなくても、laravelが勝手にuser_idフィールドを参照して、usersテーブルから紐づくレコード(モデル)を取得してくれるようになります。(このルールに則っていない場合でも、専用の書き方をすればリレーションを設定できます。)
特段の理由がない限り、このルールに則ってフィールド名をつけた方が楽になりますし、わかりやすくなります。

(逆にUserモデルには、
hasMany('App\Article')
を設定します。今回はこちらのリレーションを利用した実装はしないので、詳しく説明しません。
気になった方は調べてみてください!


ということで、これでリレーションが設定できました。簡単!!

では、show.blade.phpを開いてください。

で、以下のように編集してください。

@extends('layouts.app')
@section('content')
    投稿者: {{$article->user->name}}//←追加
    <br>
    title: {{$article->title}}
    <br>
    内容:
    <br>
    {!!nl2br(e($article->content))!!}
@endsection

はい、

{{$article->user->name}}

この$article->userで、articleに紐づくuserモデルを取得しています。(リレーションのpublic function user()の部分)
つまり、$article->user->nameで、userモデルのnameフィールドの値を取得しています。

よっしゃ、もう終わりです!

適当なブログを投稿するか、urlを指定してshowアクションを実行してみてください!

はい!投稿者の名前が表示されてますね!

以上で今回の記事を終わりたいと思います!!

お疲れ様でした!

ということで前回の記事と合わせて、MVCパターンについて触れながらやってきましたが、どうでしょう?MVCについて理解できましたでしょうか?なんとなく理解していただけたら嬉しいです。なんとなくで結構です。もっと掘り下げてみたい方は色々と調べてみてください!

さぁ、次回はタグを登録したいと思います!

多対多の関係ですね!

多対多を覚えると、アプリケーションの質がぐっとあがりますので、頑張っていきましょう!!

ではまた!



0
0
0
0
通信エラーが発生しました。
似たような記事