7
8

More than 3 years have passed since last update.

【Laravel】初めてのCRUD機能

Last updated at Posted at 2020-10-27

Laravelでの単純なCRUD機能作成時の備忘録。

最終的なブラウザ表示

  • 一覧表示ページ と 詳細表示ページ
  • 新規投稿ページ と 編集ページ

最終的なコード

■ ルーティング

実装機能 HTTPメソッド メソッド
一覧表示 get index
新規作成 get → post create → store
詳細表示 get show
編集 get → post edit → update
削除 delete destroy
web.php
Route::get('/', function () {
    return redirect('/articles');
});
Route::get('/articles', [App\Http\Controllers\ArticleController::class, 'index'])->name('article.list');                 // 一覧表示ページ
Route::get('/article/new', [App\Http\Controllers\ArticleController::class, 'create'])->name('article.new');              // 新規投稿ページ
Route::post('/article', [App\Http\Controllers\ArticleController::class, 'store'])->name('article.store');                // 新規保存
Route::get('/article/{id}', [App\Http\Controllers\ArticleController::class, 'show'])->name('article.show');              // 詳細表示ページ
Route::delete('/article/{id}', [App\Http\Controllers\ArticleController::class, 'destroy'])->name('article.delete');      // 削除
Route::get('/article/edit/{id}', [App\Http\Controllers\ArticleController::class, 'edit'])->name('article.edit');         // 編集ページ表示
Route::post('/article/update/{id}', [App\Http\Controllers\ArticleController::class, 'update'])->name('article.update');  // 更新

■ コントローラー

Articleコントローラー
<?php
namespace App\Http\Controllers;
use App\Models\Article;
use Illuminate\Http\Request;
class ArticleController extends Controller{
  // 一覧表示
  public function index(Request $request){
    if ($request->filled('keyword')) {
      $keyword = $request->input('keyword');
      $message = '検索キーワード: '.$keyword;
      $articles = Article::where('content', 'like', '%'.$keyword.'%')->get();
    }else{
      $message = "検索キーワードを入力してください。";
      $articles = Article::all();
    }
    return view('index', ['message' => $message, 'articles' => $articles]);
  }
  // 新規投稿ページ表示
  public function create(Request $request){
    $message = '投稿フォーム: ';
    return view('new', ['message'=>$message]);
  }
  // 新規保存
  public function store(Request $request){
    $article = new Article();
    $article->content = $request->content;
    $article->user_name = $request->user_name;
    $article->save();
    return redirect()->route('article.show', ['id' => $article->id]);
  }
  // 詳細表示
  public function show(Request $request, $id, Article $article){
    $message = '記事の内容: ';
    $article = Article::find($id);
    return view('show', ['message' => $message, 'article' => $article]);
  }
  // 編集ページ表示
  public function edit(Request $request, $id, Article $article){
    $message = '記事の編集: '.$id;
    $article = Article::find($id);
    return view('edit', ['message'=>$message, 'article'=>$article]);
  }
  // 更新
  public function update(Request $request, $id, Article $article){
    $article = Article::find($id);
    $article->content = $request->content;
    $article->user_name = $request->user_name;
    $article->save();
    return redirect()->route('article.show', ['id' => $article->id]);
  }
  // 削除
  public function destroy(Request $request, $id, Article $article){
    $article = Article::find($id);
    $article -> delete();
    return redirect("/articles");
  }
}

■ ビュー

  • Bootstrapを使用。

- 共通部分

layout.blade.php
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset='utf-8'>
    <title>Laravel_sample</title>
    <style>body {padding: 10px;}</style>
    @include('style-sheet')      <!-- Bootstrapの読み込み -->
  </head>

  <body>
    @include('nav')      <!-- ナビゲーションバー -->
    <div class='container'>
      @yield('content')
    </div>
  </body>
</html>
  • ナビゲーションバー
nav.blade.php
<nav class='navbar navbar-expand-md navbar-dark bg-dark fixed-top'>
  <a class='navbar-brand' href='{{ route("article.list") }}'>My Articles📗</a>
</nav>
style-sheet.blade.php
<meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<style>body {padding-top: 80px;}</style>  <!-- 設置位置の調節  -->

- 一覧表示ページ(root)

一覧表示ページ(index.blade.php)
@extends('layout')
@section('content')
  <h1>一覧表示ページ</h1>
  <p>{{ $message }}</p>
  @include('search')     <!-- 検索フォーム -->
  <table class="table table-striped table-hover">
    @foreach ($articles as $article)
      <tr>
        <td>
          <a href='{{ route("article.show", ["id" => $article->id]) }}' >
            {{ $article -> content }}
          </a>
        </td>
        <td>{{ $article -> user_name }}</td>
        <td>{{ $article -> created_at }}</td>
      </tr>
    @endforeach
  </tabel>
  <div>
    <a href = '{{ route("article.new") }}' class="btn btn-primary">投稿</a>
  </div>
@endsection

- 新規投稿ページ

新規投稿ページ(new.blade.php)
@extends('layout')
@section('content')
  <h1>新規投稿ページ</h1>
  <p>{{ $message }}</p>

  {{ Form::open(['route'=>'article.store']) }}
    <div class='form-group'>
      {{ Form::label('content', '内容:') }}
      {{ Form::text('content', null, ['placeholder'=>'内容を入力']) }}
    </div>

    <div class='form-group'>
      {{ Form::label('user_name', '投稿者名: ') }}
      {{ Form::text('user_name', null, ['placeholder'=>'投稿者名を入力']) }}
    </div>

    <div class='form-group'>
      {{ Form::submit('投稿', ['class' => 'btn btn-primary']) }}  <!-- 投稿ボタン -->
      <a href='{{ route("article.list") }}'>もどる</a>  <!-- indexページへのリンク -->
    </div>
  {{ Form::close() }}
@endsection

- 詳細表示ページ

詳細表示ページ(show.blade.php)
@extends('layout')
@section('content')
  <h1>詳細表示ページ</h1>
  <p>{{ $message }}</p>
  <p>{{ $article -> content }}</p>
  <p>{{ $article -> user_name }}</p>
  <p>{{ $article -> created_at }}</p>
  <p>
    <a href='{{ route("article.edit", ["id"=> $article->id]) }}' class="btn btn-secondary">編集</a>
    <a href = '{{ route("article.list") }}' class="btn btn-outline-secondary">戻る</a>
  </p>
  <div>
    {{ Form::open(['method' => 'delete', 'route' => ['article.delete', $article -> id]]) }}
      {{ form::submit("削除ボタン") }}
    {{ Form::close() }}
  </div>
@endsection

- 編集ページ

編集ページ(edit.blade.php)
@extends('layout')
@section('content')
  <h1>編集ページ</h1>
  <p>{{ $message }}</p>

  {{ Form::model($article, ['route'=> ['article.update', $article->id] ]) }}
    <div class='form-group'>
      {{ Form::label('content', '内容:') }}
      {{ Form::text('content', null) }}
    </div>

    <div class='form-group'>
      {{ Form::label('user_name', '投稿者名: ') }}
      {{ Form::text('user_name', null) }}
    </div>

    <div class='form-group'>
      {{ Form::submit('保存', ['class' => 'btn btn-primary']) }}
      <a href='{{ route("article.show", ["id"=> $article->id]) }}'>もどる</a>
    </div>
  {{ Form::close() }}
@endsection

- 検索フォーム

  • 部分テンプレートで作成し、indexページに設置。
検索フォーム(search.blade.php)
{{ Form::open(['method' => 'get']) }}
  {{ csrf_field() }}

  <div class='form-group'>
    {{ Form::label('keyword', 'キーワード:') }}
    {{ Form::text('keyword', null, ['class' => 'form-control'], ['placeholder'=>'キーワードから記事を検索できます']) }}
  </div>

  <div class='form-group'>
    {{ Form::submit('検索', ['class' => 'btn btn-outline-primary']) }}
    <a href='{{ route("article.list") }}'>クリア</a>
  </div>
{{ Form::close() }}

各論

前提

  • MySQLに、Articlesテーブル(content、user_nameカラム)作成済みの状態とする。
  • 動作確認のために、rootページにコントローラーからデータを送れるか?、表示できるか? 程度は確認しとく。
参)コントローラー作成コマンド
% php artisan make:controller articleController
動作確認(ルーティング→コントローラー→indexビュー)
// コントローラー
public function index(){
  $message = '動作確認用';
  return view('index', ['message' => $message]);    // indexビューに、message を渡してみる
}

// ルーティング
Route::get('/', function () {
  return redirect('/articles');
});

// ビュー(index.brade.php で、messageを取得してみる)
<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8'>
    <title>Laravel_sample</title>
    <style>body {padding: 10px;}</style>
  </head>
  <body>
    <p>{{ $message }}</p>
  </body>
</html>

CRUD機能 の実装

■ 一覧表示(index)

  • コントローラー(モデルから全データを取得し、ビューに送る) → ビュー(一覧表示) の流れ。
/app/Http/Controllers/ArticleController.php
public function index(){
  $message = 'Hello world';
  $articles = Article::all();        // モデルから全データ取得
  return view('index', ['message' => $message, 'articles' => $articles]);  // ビューに渡す
}
/resources/views/index.blade.php
@extends('layout')      // 部分テンプレートを呼び出す
@section('content')
  <h1>一覧表示ページ</h1>
  <p>{{ $message }}</p>               // コントローラーから $message 取得
  @foreach ($articles as $article)    // コントローラーから $article をループで取得
    <p>{{ $article->content }}</p>
  @endforeach
@endsection

■ 詳細表示(show)

  • コントローラー(モデルから1つの指定データ取得し、ビューに送る) → ビュー(詳細表示) の流れ。
/app/Http/Controllers/ArticleController.php
public function show(Request $request, $id, Article $article){            // id、記事情報が必要
  $message = '詳細表示: '.$id;
  $article = Article::find($id);       // モデルから指定データ取得
  return view('show', ['message' => $message, 'article' => $article]);   // ビューに渡す
}
  • 詳細表示ページを作成。
/resources/views/show.blade.php
<h1>詳細表示ページ</h1>
<p>{{ $message }}</p>
<p>{{ $article->content }}</p>
<p>
  <a href='{{ route("article.list") }}'>戻る</a>      // 一覧表示ページへのリンク (例:ルーティング名「article.list」と名付けてる)
</p>
  • 一覧表示ページに、詳細表示ページへのリンクを貼る。
/resources/views/index.blade.php
@foreach ($articles as $article)
  <p>
    <a href='{{ route("article.show", ["id" =>  $article->id]) }}'>
      {{ $article->content }}
      {{ $article->user_name }}
      {{ $article->created_at }}
    </a>
  </p>
@endforeach

■ 新規投稿(create、store)

  • 投稿ページへのルーティングを設定。
    • 記述順序に注意!! newよりshowを先に記述するとエラー! /article/{id}を探しに行っちゃうから。
/routes/web.php
Route::get('/articles', [App\Http\Controllers\ArticleController::class, 'index'])->name('article.list');       // 一覧表示ページ
Route::get('/article/new', [App\Http\Controllers\ArticleController::class, 'create'])->name('article.new');    // 新規投稿ページ
Route::get('/article/{id}', [App\Http\Controllers\ArticleController::class, 'show'])->name('article.show');    // 詳細表示ページ
  • コントローラーに、投稿ページ表示のためのメソッド(create)を追加。
/app/Http/Controllers/ArticleController.php
public function create(Request $request){   // createでは、取得したい情報はない
  // 確認用:フォームを使わずに、固定テキストや日時を格納する場合 (※ storeメソッドは不要)
  $article = new Article();       // インスタンス作成
  $article->content = '投稿内容';
  $article->user_name = '投稿者';
  $article->save();
  return redirect('/articles');   // 保存したら、indexページにリダイレクト

  // フォームから、投稿する場合
  $message = '投稿フォーム: ';
  return view('new', ['message'=>$message]);   // newページへ送る
}
  • 続いて、保存のためのメソッド(store)も追加。
app/Http/Controllers/ArticleController.php
public function store(Request $request){
  $article = new Article();                   // インスタンス作成
  $article->content = $request->content;      // 投稿内容
  $article->user_name = $request->user_name;  // 投稿者名
  $article->save();                           // 保存
  // レコード保存後に、showページへデータを渡してリダイレクト
  return redirect('article.show', ['id' => $article->id]);
}
  • indexページに投稿ページへのリンクを設置。
    • 上で、新規投稿ページへのルーティング名は、「article.new」と名付けたので、それを使用。
/resources/views/index.blade.php
<div>
  <a href='{{ route("article.new") }}'>新規投稿</a>   // 新規投稿ページへのリンク
</div> 
  • 最後に、投稿フォームを作成する。
    • 投稿フォームを部分テンプレートとして作成。
    • Laravelでのフォーム設置には、laravelcollective/html ライブラリ のインストールが必要。
      注意)インストールを忘れると、Class 'Form' not found エラー!!
ターミナルでフォーム使用時に必要なライブラリをインストール
% composer require laravelcollective/html
// メモリ制限エラー( Allowed memory size of 1610612736 bytes exhausted)の場合は、メモリを上げてインストールしてみる
% php -d memory_limit=-1 /usr/local/bin/composer require laravelcollective/html

% composer info      // インストール済みか確認
resources/views/new.blade.php
@extends('layout')
@section('content')
  <h1>投稿フォーム</h1>
  <p>{{ $message }}</p>

  {{ Form::open(['route' => 'article.store']) }}  <!-- 投稿フォーム  -->
    <div class='form-group'>                      <!-- 投稿内容の記述エリア -->
      {{ Form::label('content', '内容:') }}
      {{ Form::text('content', null, ['placeholder'=>'内容を入力']) }}
    </div>

    <div class='form-group'>                      <!-- 投稿者名の記述エリア -->
      {{ Form::label('user_name', '投稿者:') }}
      {{ Form::text('user_name', null, ['placeholder'=>'投稿者を入力']) }}
    </div>

    <div class="form-group">                      <!-- ボタン設置 -->
      {{ Form::submit('投稿ボタン', ['class' => 'btn btn-primary']) }}
      <a href='{{ route("article.list") }}'>一覧に戻る</a>
    </div>
  {{ Form::close() }}
@endsection

■ 編集 (edit、 update)

  • 編集ページ表示機能を実装(editメソッド)。
app/Http/Controllers/ArticleController.php
public function edit(Request $request, $id, Article $article){  // 編集には、id情報 と 記事データが必要
  $message = '記事の編集: '.$id;    // 表示用
  $article = Article::find($id);  // 編集するレコードをid情報から取得
  return view('edit', ['message'=>$message, 'article'=>$article]);  // 編集ページに渡す
}
  • 編集フォームを作成する。
resources/views/edit.blade.php
@extends('layout')
@section('content')
  <h1>編集フォーム</h1>
  <p>{{ $message }}</p>
  <!-- Form::model : 編集内容を表示してくれる -->
  {{ Form::model($article, ['route' => ['article.update', $article->id]]) }}
    <div class='form-group'>
      {{ Form::label('content', '内容:') }}
      {{ Form::text('content', null) }}
    </div>

    <div class='form-group'>
      {{ Form::label('user_name', '投稿者:') }}
      {{ Form::text('user_name', null) }}
    </div>

    <div class="form-group">
      {{ Form::submit('保存ボタン', ['class' => 'btn btn-primary']) }}
      <a href='{{ route("article.show", ["id" =>  $article->id]) }}'>もどる</a>
    </div>
  {{ Form::close() }}
@endsection
  • showページに、編集ボタンを設置。
resources/views/show.blade.php
<a href='{{ route("article.edit", ["id" => $article->id]) }}' class='btn btn-outline-primary'>編集ボタン</a>
  • 編集内容を保存する更新機能を実装(updateメソッド)。
app/Http/Controllers/ArticleController.php
public function update(Request $request, $id, Article $article){
  $article = Article::find($id);
  $article->content = $request->content;
  $article->user_name = $request->user_name;
  $article->save();
  return redirect()->route('article.show', ['id' => $article->id]);
}

■ 削除(delete)

  • Laravelでは、ブラウザとの通信にHTTPメソッドを指定するが、ブラウザが deleteメソッドをサポートしてないので、POSTで隠しメソッドを送って、コントローラーメソッドのdestroyを実行してもらうイメージ。

  • まず、削除用のルーティングを設定。

/routes/web.php
Route::delete('/article/{id}', [App\Http\Controllers\ArticleController::class, 'destroy'])->name('article.delete');
  • コントローラーにアクション(destroy)を定義。
/app/Http/Controllers/ArticleController.php
public function destroy(Request $request, $id, Article $article){   // 削除するレコードのid、記事データ が必要
  $article = Article::find($id);   // id情報から、レコードを取得
  $article->delete();              // 削除
  return redirect('/articles');    // 削除後に、一覧表示ページへリダイレクト
}
  • 詳細表示ページに、削除ボタンを設置。
/resources/views/show.blade.php
<div>
  {{ Form::open(['method' => 'delete', 'route' => ['article.delete', $article->id]]) }}
    {{ Form::submit('削除ボタン') }}
  {{ Form::close() }}
</div>

■ 検索

  • 削除と同じく、Laravelでフォームを使うには、「laravelcollective/html」ライブラリが必要。
参)laravelcollective/htmlのインストール
% composer require laravelcollective/html
% composer info      // インストール済みかの確認
  • Laravelでのフォームは、{{ Form:: ... }}に記述する(Formファサードという)。
    (※ ファサード(facade)とは、建物の入り口という意味。)
  • 1. まず、検索フォームの部分テンプレートを作成する。
    • GETメソッドによって、text型で、キー:「keyword」に値を格納してコントローラーにデータを送る。
resources/views/search.blade.php
{{ Form::open(['method' => 'get']) }}
  {{ csrf_field() }}           <!-- CSRFトークン -->
  <div class='form-group'>
    {{ Form::label('keyword', 'キーワード:') }}
    {{ Form::text('keyword', null, ['class' => 'form-control'], ['placeholder'=>'キーワードから記事を検索できます']) }}
  </div>
  <div class='form-group'>
    {{ Form::submit('検索', ['class' => 'btn btn-outline-primary']) }}  <!-- 検索ボタン -->
    <a href='{{ route("article.list") }}'>クリア</a>                     <!-- indexページへのリンク -->
  </div>
{{ Form::close() }}    <!-- フォーム終了タグ -->
  • 一覧表示ページに、部分テンプレートを読み込む。
resources/views/index.blade.php
<p>{{ $message }}</p>
@section('content')
  @include('search')   <!-- ココで、検索フォームを読み込み -->
@endsection
  • 2. 次に、検索機能の実装。
    • コントローラーで、フォームから送られたデータを取得し、該当する投稿をビューに渡して表示させる。
app/Http/Controllers/ArticleController.php
public function index(Request $request){      // GETメソッドで受け取ったデータは、引数に指定した変数$requestで取得できる
  if ($request->filled('keyword')) {          // データの有無で条件分岐
    $keyword = $request->input('keyword');    // $request(フォームで送られたデータ) の中から、「keyword」を取得して、変数に定義
    $message = '検索結果: '.$keyword;          // ビュー表示のため、変数に格納
    $articles = モデル名::where('検索するカラム名', 'like', '%'.$keyword.'%')->get();
  }else{     // 未入力の場合
    $message = "検索キーワードを入力してください。";
    $articles = モデル名::all();              // 未入力なら、全データ表示
  }
  return view('検索結果を送りたいビュー名', ['message' => $message, 'articles' => $articles]);  // ビューに渡す
}

✔️ モデル名::where()

  • 指定条件で、配列を絞り込む。
検索条件の指定(where)
use Illuminate\Support\Arr;
$array = [100, '200', 300, '400', 500];
$filtered = Arr::where($array, function($value, $key) {
  return is_string($value);    // string型のみ取得。 [1=>'200', 3=>'400']
});

✔️ filled( )

  • 値が空でないか?の判定。
  • blank メソッドの逆の動作。
空でないか?の判定(filled関数)
hoge1 = 'value';
$request->filled(hoge1);   // true : キーが存在、値が空でない
$request->filled(hoge2);   // false : キーが存在しない or 空

参) artisan tinker (対話型コンソール)

  • artisan tinker:対話型コンソール
  • 手っ取り早く、コンソールからDBにデータを登録したい時などに便利。
対話型コンソールの使い方
% php artisan tinker     // 対話型コンソールの起動
>>> Article::all()       // 全データ取得

>>> $article = Article::find(1)   // 指定idのレコードを取得
>>> $article->content             // カラムを出力
>>> $article                      // レコード出力
>>> $article->delete()            // レコード削除

// レコード追加
>>> $article = new Article()      // インスタンス作成
>>> $article->content = 'テスト'    // 変数に格納
>>> $article->save()              // データ保存

>>> exit       // CTRL + C でもok

(おまけ) 関連づけたテーブルからのデータ取得

例)articlesテーブル、categoriesテーブルのデータを表示したい
// モデル にリレーションを定義 (例: Articleモデルは、Categoryモデルに対して、1対多の場合)
public function category() {
  return $this->belongsTo('App\Models\Category');
}

// ビュー( ※変数はコントローラーで定義 )
{{ $article -> name }}               // articlesテーブルのnameカラムを表示
{{ $article -> category -> name }}   // articlesテーブルに関連付けたcategoriesテーブルのnameカラムを表示

(おまけ) 関連づけたテーブルへのデータ保存

例)articlesコントローラーから、categoriesテーブルにデータ保存したい
// コントローラー
use App\Models\Category;   // Categoryモデルの使用を宣言
         :
public function create(){
  $categories = Category::all()->pluck('name', 'id');   // Categoryモデルから、nameとidカラムだけ取得 
  return view('new', ['article' => $article, 'categories'=> $categories] );
}
public function store(Request $request){
  $article = new Article();
  $article -> content = request('content');
  $article -> user_name = request('user_name');
  $article -> category_id = request('category_id');
  $article -> save();
  return redirect() -> route('article.detail', ['article' => $article->id] );
}

// ビュー
{{ Form::open(['route' => 'shop.store']) }}
  {{ Form::text('content', null) }}               // articlesテーブルへのデータ保存
  {{ Form::select('category_id', $categories) }}  // categoriesテーブルのデータ選択
  {{ Form::submit('登録') }}
{{ Form::close() }}
7
8
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
7
8