はじめに
Laravelで初めてのアプリケーションを作成中です。
学習しながら作成しているので、私自身の頭の整理をしつつ、超基礎的な流れや頻出コマンドをまとめてみました。
かなり長いですが、この通りにおこなえば簡単にできるはずです。
ちなみに前提として、モデル・コントローラーとは何かを理解しており、環境構築は終わっていることとします。
私はRuby on Railsは簡単なアプリケーションであればサクサク作成できる程度の知識がありますが、PHP、Laravelは学習開始して1週間程度の初学者です。間違えていることなどあれば、ご指摘ください。
目次
開発環境
- AWS Cloud9
- PHP 8.0.2
- Laravel 9.21.6
- Composer 2.3.10
- MariaDB 5.5.68
手順
ここでは、超シンプルな投稿機能を実装します。
ベースを作成するだけですので、バリデーションやレイアウトは無視していますので、実際にアプリケーションを作成する場合は、これをもとに作りこむ必要があります。
また、名称は、それぞれ下記のようにしました。
テーブル: items
モデル: Item
コントローラー: ItemsController
もしこの記事を見てご自身で作成される場合は、それぞれ名称を変えてくださいね。
その1 マイグレーションファイル・テーブル作成
最初に、下記コマンドでマイグレーションファイルを作成します。
$ php artisan make:migration create_items_table
database/migrationsディレクトリの中にマイグレーションファイルが作られるので、テーブルに追加するカラムを記入します。
public function up()
{
Schema::create('items', function (Blueprint $table) {
$table->id();
// ここから
$table->string('name');
$table->text('description');
$table->integer('price');
// ここまで追記
$table->timestamps();
});
}
追加できたら、下記コマンドを打ってテーブルを作成します。
$ php artisan migrate
【その1:応用編】Seederで初期データ追加
Seederで初期データを追加してみましょう。
下記コマンドで、database/seeders内にitemsテーブル用のseederファイルを作成します。
$ php artisan make:seeder ItemsTableSeeder
ファイルを下記のように編集します。内容は何でもOKです。
class ItemsTableSeeder extends Seeder
{
public function run()
{
// ここから
DB::table('items')->insert(
[
'name'=>'サンプル',
'description'=>'サンプルアイテム',
'price'=>100,
]
);
// ここまで追記
}
}
データは複数作っても良いです。お好きなだけ、お好きな内容を追加してください。
気が済んだら、ターミナルでコマンドを実行し、内容を反映させます。
$ php artisan migrate --seed
以上でテーブルが完成しました。
その2 モデル作成
次に、モデルを作っていきます。
ターミナルで、下記コマンドを実行し、モデルを作成します。
$ php artisan make:model Item
app/Modelディレクトリ内に「Item.php」というファイルが作られるはずです。
これを開いて、下記のように編集します。
class Item extends Model
{
use HasFactory;
public $guarded = ['id', 'created_at']; //追記部分
}
追記部分について説明します。
$guarded
これは、データベースに保存する際の、書き込み禁止のフィールドを設定しています。
この記述をしないと、データベースに保存するときにエラーが発生します。
Eloquentという、データベースとモデルを紐づけるLavarelの機能があり、書き込み可能もしくは書き込み禁止フィールドを指定しなくてはいけないようになっているようです。
例の記述ですと、
public $guarded = ['id', 'created_at'];
としているため、自動で追加される「id」と「created_at」が書き込み禁止になってます。
書き込み可能を指定することもできます。その場合は、
public $fillable = ['name', 'description', 'price'];
のように記述します。
どちらでも良いみたいですが、記述禁止フィールドの方が少ないことが多いと思うので、私は$guardedを多用していきたいと思います。
その3 コントローラー・ルート作成
次に、コントローラーを作っていきます。
コントローラーは、下記のコマンドで作成します。
$ php artisan make:controller ItemsController --model=Item --resource
--model=Item
でモデルを関連付けさせています。
また--resource
を付けることで、投稿機能に必要なアクションを処理するコントローラーが作成できます。
コントローラーはapp/Http/Controllers内に作成されます。
余談ですが、私がPHPを学習し始めて最初に躓いたのが、コントローラーの書き方でした。(resourceコマンドを知らずに一ページずつ作ろうとしていました。)
最初に引っかかったのが、Rubyでいう「new」アクションと「create」アクションです。
Rubyの「new」 => PHPでは「create」
Rubyの「create」 => PHPでは「store」
なのですが、最初に新規投稿ページ作ろ!と思ってnewとcreateアクションを実装しようとするもののよくわからず、ちょっとして分かったときには感動しました。
本題に戻ります。
アクションごとに説明しながら、コントローラーアクションを記述していきます。
まずは、indexから。
public function index()
{
// データベース内のすべてのItemを取得し、item変数に代入
$items = Item::all();
// 'items'フォルダ内の'index'viewファイルを返す。
// その際にview内で使用する変数を代入します。
return view('items/index', ['items' => $items]);
}
PHPでは、変数は $
を使用します。
この後作成するindexのviewで、$items = Item::all()
で取得したデータを表示するための記述をします。そのために、viewで表示するための変数を代入しています。
次に、createアクションです。
Createは新規投稿作成画面を表示するアクションです。
public function create()
{
return view('items/create');
}
viewの表示のみですので、説明は割愛します。
次に、storeアクションです。
データを作成し、データベースに保存するアクションです。
public function store(Request $request)
{
// 新しい Item を作成
$item = new Item;
// フォームから送られてきたデータをそれぞれ代入
$item->name = $request->name;
$item->description = $request->description;
$item->price = $request->price;
// データベースに保存
$item->save();
// indexページへ遷移
return redirect('/items');
}
$item->name
は、itemのnameです。これに、$request->name
(nameフォームから送られてきた内容)をいれています。
Createページから送られてきたフォームのデータが、$request
に入ってくるイメージだと、わかりやすいかもしれません。
再度申し上げますが、createページ(新規投稿作成画面)を含むviewページは、次のview作成のステップで作成します。
showは詳細画面のように、データを一つずつ表示するページです。
public function show($id)
{
$item = Item::find($id); // idでItemを探し出す
return view('items.show', ['item' => $item]);
}
showアクションは、()内にidと記入することで、findメソッドでidを呼び出せるようにします。これにより、たとえば一覧ページ(index)から、itemの詳細ページ(show)へリンクするときに、idを指定することができます。
$item = Item::find($id);
は、idに一致するItemを探し出しています。
showページのurlは、/items/1 のように、最後にアイテムのidがつきます。
次に、editとupdateです。
createとstoreに非常に似ているので、特に説明はしません。
public function edit($id)
{
$item = Item::find($id);
return view('items.edit', ['item' => $item]);
}
public function update(Request $request, $id)
{
$item = Item::find($id);
$item->name = $request->name;
$item->description = $request->description;
$item->price = $request->price;
$item->save();
return redirect('items/'.$id);
}
最後に、destroyアクションです。
削除機能に使われます。こちらも非常にシンプルなので、説明は割愛します。
public function destroy($id)
{
$item = Item::find($id);
$item->delete();
return redirect('/items');
}
今回作成したコントローラーは、この通りです。
public function index()
{
$items = Item::all();
return view('items/index', ['items' => $items]);
}
public function create()
{
return view('items/create');
}
public function store(Request $request)
{
$item = new Item;
$item->name = $request->name;
$item->description = $request->description;
$item->price = $request->price;
$item->save();
return redirect('/items');
}
public function show($id)
{
$item = Item::find($id);
return view('items.show', ['item' => $item]);
}
public function edit($id)
{
$item = Item::find($id);
return view('items.edit', ['item' => $item]);
}
public function update(Request $request, $id)
{
$item = Item::find($id);
$item->name = $request->name;
$item->description = $request->description;
$item->price = $request->price;
$item->save();
return redirect('items/'.$id);
}
public function destroy($id)
{
$item = Item::find($id);
$item->delete();
return redirect('/items');
}
いくらレイジーでも、コピペはしないでくださいね。
そして、重要なルートを記述します。
これがないと、viewの表示もできません。
ルートは、routes/web.phpというファイルに記述していきます。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ItemsController; // 追記
Route::get('/', function () {
return view('layouts/app');
});
Route::resource('items', ItemsController::class); //追記
最初に、下記を追加して、ItemsControllerを読み込みます。
use App\Http\Controllers\ItemsController;
これがないとエラーが起きます。
「逆にルートがない」エラーのほとんどが、この一行で治りました。
次にルートを記述します。
ルートの書き方は様々ありますが、今回はresourceでCRUD機能に必要なアクションをすべて作ったので、ルートもresourceを使用します。
Route::resource('items', ItemsController::class);
resource
を使用することで、ItemsController
内で定義したCRUDのアクションのルートを一括で作成しています。
ルートを確認してみます。ターミナルで下記コマンドを打ってみてください。
$ php artisan route:list
このようなルートができているはずです。
GET|HEAD items ................................................ items.index › ItemsController@index
POST items ................................................ items.store › ItemsController@store
GET|HEAD items/create ....................................... items.create › ItemsController@create
GET|HEAD items/{item} ........................................... items.show › ItemsController@show
PUT|PATCH items/{item} ....................................... items.update › ItemsController@update
DELETE items/{item} ..................................... items.destroy › ItemsController@destroy
GET|HEAD items/{item}/edit ...................................... items.edit › ItemsController@edit
これは、下記のように書き換えもできます。
Route::get('/items', [ItemController::class, 'index']);
Route::post('/items', [ItemController::class, 'store']);
// 以下省略
また、resource
を使用し、必要のないルート以外、もしくは必要なルートだけ指定することもできます。
// onlyを付けて、indexとshowだけ
Route::resource('items', ItemsController::class)->only(['index','show']);
// exceptを付けて、create, store, update, destroy以外 = indexとshowだけ
Route::resource('items', ItemsController::class)->except(['create', 'store', 'update', 'destroy]');
他にもたくさんの記述方法があります。
今回は、ひとまず以上です。
その4 ビュー作成
次はviewを作成していきますが、今回はレイアウトなどは完全無視で、ベースとなるフォームやデータを表示させる方法についてのみ触れます。レイアウトはご自身で作成してください。
まず、Laravelは、bladeというテンプレートを使用しています。ちなみにview作成コマンドは現時点でありません。なので、ひとつづつ作っていきます。
アクションを定義した際に、viewのディレクトリを指定していますので、その通りになるように、resources/views内にitemsフォルダを作成します。
itemsフォルダ内に、viewファイルを作成します
- create.blade.php
- edit.blade.php
- index.blade.php
- show.blade.php
また、layoutsフォルダとその中にapp.blade.phpファイルを作成し、それを親テンプレートとします。
root/
├ resources/
│ └ views/
│ └ create.blade.php
│ └ edit.blade.php
│ └ index.blade.php
│ └ show.blade.php
│ └ layouts/
│ └ app.blade.php
こんな感じになればOK。
そして、app.blade.php
を下記のように編集します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>アプリケーション名</title>
</head>
<body class="antialiased">
<main>
@yield('content')
</main>
</body>
</html>
これが親テンプレートとなり、@yield('content')
の部分に子テンプレート(各ページの内容)が表示される仕組みです。
次に、items用のページを作成します。
子テンプレートとなるページは、この形式に沿って記述します。
@extends('layouts.app') // 親テンプレート'layouts.app'内に表示するための記述
@section('content')
// 内容
@endsection
それぞれの内容は下記を参考にしてください。
Createページ
@extends('layouts.app')
@section('content')
<div class="create-items">
<div class="form">
<form action="/items" method="POST">
@csrf // 送信されるデータを保護する
<div class="input-form">
<label for="name">Name</label>
<input name="name">
</div>
<div class="input-form">
<label for="description">Description</label>
<textarea name="description"></textarea>
</div>
<div class="input-form">
<label for="price">Price</label>
<input name="price">
</div>
<div class="input-form">
<input type="submit" value="Submit">
</div>
</form>
</div>
</div>
@endsection
基本はHTMLだけで構成されていますが、下記の記述を追加することを忘れないようにしてください。
@csrf
// 以下の記述でもOK
{{ csrf_field() }}
これがないと、データが保護されていません。という表示が出てきて、エラーになってしまいました。
Editページ
※createページとほぼ一緒です。{{$item->name}}
などを追加することで、既に保存されている情報をフォームに表示します。
@extends('layouts.app')
@section('content')
<div class="create-items">
<div class="form">
<form action="/items/{{$item->id}}" method="POST">
@csrf
@method('PUT')
<div class="input-form">
<label for="name">Name</label>
<input name="name" value="{{$item->name}}">
</div>
<div class="input-form">
<label for="description">Description</label>
<textarea name="description">{{$item->description}}</textarea>
</div>
<div class="input-form">
<label for="price">Price</label>
<input name="price" value="{{$item->price}}">
</div>
<div class="input-form">
<input type="submit" value="Update">
</div>
</form>
</div>
</div>
@endsection
メソッドを指定する必要があることだけ忘れないでください。
@method('PUT')
この記述により、POSTではなくPUTメソッドであることを指定しています。
Indexページ
@extends('layouts.app')
@section('content')
<table>
<tr>
<th>Item Id</th>
<th>Name</th>
<th>Description</th>
<th>Price</th>
<td></td>
</tr>
@foreach($items as $item)
<tr>
<td>{{$item->id}}</td>
<td>{{$item->name}}</td>
<td>{{$item->description}}</td>
<td>{{$item->price}}</td>
<td><a href="/items/{{$item->id}}">Details</a></td> // showページへのリンク
</tr>
@endforeach
</table>
@endsection
seederで初期データを追加している場合は、すでにデータが表示されるはずです。
Showページ
@extends('layouts.app')
@section('content')
<table>
<tr>
<th>Item Id</th>
<th>Name</th>
<th>Description</th>
<th>Price</th>
</tr>
<tr>
<td>{{$item->id}}</td>
<td>{{$item->name}}</td>
<td>{{$item->description}}</td>
<td>{{$item->price}}</td>
</tr>
</table>
<a href="/items/{{$item->id}}/edit">Edit</a>
<form action="/items/{{$item->id}}" method="POST">
{{ csrf_field() }}
<input type="hidden" name="_method" value="delete">
<input type="submit" name="" value="Delete">
</form>
<a href="/items">Back to index</a>
@endsection
これで、下記の事ができるようになるはずです。
- 新規itemの登録(create, store)
- 登録済みitemの一覧表示(index)
- 登録済みitemの詳細表示(show)
- 登録済みitemの編集(edit, update)
- 登録済みitemの削除(destroy)
まとめ
お疲れ様でした。
長くなりましたが、ここまでお読みいただきありがとうございます。
PHP、Laravelの学習を始めて間もないですが、Laravel 9やPHP 8の情報はまだあまり多くなく、エラーが出た時の解決が簡単ではありませんでした。
ここまでの工程は簡単に作れるようになったので、これからもアプリケーションを作成しながら、新しい機能を作れるようになりたいです。