はじめに
本記事はcomposerを用いてLaravelの環境を構築する方法を紹介する。データベースはPostgreSQLを採用している。
また、チュートリアルとしてLaravel5.7入門: 初心者でも簡単! ブラウザだけでLaravelを使ったWeb開発!の内容を利用する。なおこの講座はこの記事を復唱したものだったので、これを読めば十分と感じた。Laravelをとりあえず学びたい方や、環境構築に興味ない方、古いLaravelを使いたい方はこの記事ではなく直接講義を受けることをお勧めする。
環境構築
以下の環境で行った。
MacOS BigSur 11.1
php 8.0.1
composer 2.0.8
Laravel 8.20.0
Composerのインストール
ComposerはPHPのライブラリー管理ツールである。インストールは公式サイトから行うことができる。また、Macであればbrew install composer
でインストールができる。インストールが完了したら以下のコマンドで正常にインストールされていることを確認する。
% composer -v
以下のように表示されれば正常にインストールされている。
______
/ ____/___ ____ ___ ____ ____ ________ _____
/ / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
/_/
Composer version X.X.X XXXX-XX-XX XX:XX:XX
.
.
.
Laravelプロジェクトの作成
Laravelを用いたプロジェクトは以下のコマンドで作成することができる。
% composer create-project "laravel/laravel" myapp
myappは作成されるプロジェクト名(ディレクトリ名)。これによって作成されたプロジェクトで使うLaravelのバージョンを確認したい場合は以下のコマンドを入力する(出力されるバージョンは環境によって異なる)。
% cd myapp
myapp % php artisan -V
Laravel Framework 8.24.0
以下のコマンドであれば好きなバージョンで作成できる。
% composer create-project "laravel/laravel=8.20.0" myapp
ここまで出来たところで一度プロジェクトの実行を行なって初期画面を見てみる。プロジェクトの実行は以下のコマンドを入力する。
myapp % php artisan serve
入力後表示されたURLにアクセスし、以下のようなページが現れたら成功。
データベースの作成
講義ではMYSQLを使っているが、PostgreSQLを用いた。MYSQLで行いたい方は講義の記事を参照して頂きたい。
PostgreSQLはこのサイトからインストールできる。Macであればbrew install postgresql
でも可能。インストールできたら、以下のコマンドを打ちインストールされていることを確認する。
% postgres -V
postgres (PostgreSQL) 13.1
インストールできたら便利のために環境変数に以下を追加する。
export PGDATA=/usr/local/var/postgres
手元の環境によって環境変数の追加方法は異なると考えられるが、zshの場合以下のコマンドで行う。
% echo 'export PGDATA=/usr/local/var/postgres' >> ~/.zshrc
% source ~/.zshrc
追加後以下のコマンドでバックエンドでサーバーを動かすことができる(データベースを使用する間は常にサーバーを動かさなければいけないため)。
% pg_ctl start
また、以下のコマンドで止めることができる。
% pg_ctl stop
次にユーザーを作成する。サーバーを動かした状態で以下のコマンドを入力する。
% createuser -P username
Enter password for new role: passward
Enter it again: passward
usernameとpasswardは任意に設定する。
ユーザーを作成したところでついにデータベースを作成する。データベースは以下のコマンドで作成することができる。
createdb dbname −0username
dbnameは任意で、-Oは新しいデータベースの所有者となるデータベースユーザを指定することを意味している。
データベースの設定
このプロジェクトは.env
ファイルとconfig/database.php
によってデータベースの設定を行なっている。以下のように二つのファイルを変更する。
DB_CONNECTION=pgsql
DB_HOST=localhost
DB_PORT=5432
DB_DATABASE=dbname
DB_USERNAME=username
DB_PASSWORD=passward
'default' => env('DB_CONNECTION', 'pgsql'),
チュートリアル
チュートリアルではこれをもとにタスク管理アプリを作成する。
データベースの利用
データベースのテーブル、モデル、コントローラーを作成する方法について説明する。まず、モデルとはLaravelにおいてデータベースのテーブルなどをオブジェクトのように扱えるようにしてくれるものである(ORM)。また、コントローラーはモデルから受け取ったオブジェクトをもとにビューを作成するものである。これらを作成するには以下のコマンドを入力する。
% php artisan make:model Task -m -c -r
-mはマイグレーションファイル、-cはコントローラ、"-r"でリソース操作に関連したメソッドを作成する。マイグレーションファイルは/myapp/database/migrations
フォルダに作成され、モデルは/myapp/app/Task.app
、コントローラーは/myapp/app/Http/Controllers/TaskController.php
のように作成される。 マイグレーションファイルを変更してタスク名を表すnameと言うカラムを追加する。マイグレーションファイルは以下のように変更し、
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
以下のコマンドで実行する。
% php artisan migrate
実際にテーブルが作成され、タスク名が追加されていることは以下のコマンドを実行することで確かめられる。
% myapp psql -U username dbname
psql (13.1)
Type "help" for help.
mydb=> \d tasks
Table "public.tasks"
Column | Type | Collation | Nullable | Default
------------+--------------------------------+-----------+----------+-----------------------------------
id | bigint | | not null | nextval('tasks_id_seq'::regclass)
name | character varying(255) | | not null |
created_at | timestamp(0) without time zone | | |
updated_at | timestamp(0) without time zone | | |
% myapp % psql -U username dbname
でdbnameの操作が可能となり、\d tasks
でtasksテーブルの中身を確認することができる。
コントローラーの設定
コントローラーは前述の通りmyapp/app/Http/Controllers/TaskController.php
に書かれている。内容がない様々なメソッドがあるが、今回はindex
、store
、destroy
メソッド以外は使わないので消す。index
はタスクの一覧を表示させるメソッドである。store
はタスクの追加を行うメソッドである。delete
はタスクを削除するメソッドである。それぞれ以下のように実装する。
class TaskController extends Controller
{
public function index()
{
$tasks = Task::all();
return view('tasks', ['tasks' => $tasks]);
}
public function store(Request $request)
{
$task = new Task;
$task->name = request('name');
$task->save();
return redirect('/tasks');
}
public function destroy(Request $request, $id, Task $task)
{
$task = Task::find($id);
$task->delete();
return redirect('/tasks');
}
}
1.index
はTask::all()
によってTaskモデルのallメソッドを呼び出すことでテーブル内の全ての内容を取り出し、return view('tasks', ['tasks' => $tasks]);
によって/myapp/resources/views/tasks.blade.php
(最後に作成)をテンプレートとして全タスクを表示させている。
2.store
は$tasks=new Task;
によって新たなTaskオブジェクトを作成し、$task->name = request('name');
で受け取った名前にタスク名を変更し、$task->name = request('name');
で保存している。return redirect('/tasks');
はリダイレクトすることでindex
メソッドを再度呼び出し、追加したタスクを表示させている。
3.delete
は$task = Task::find($id);
で受け取ったidのタスクを探し、$task->delete();
でそのタスクを削除している。return redirect('/tasks');はstore
メソッド同様。
ルーティング設定
一つ前でコントローラーを作成した。このコントローラーのメソッドをルーティングに割り当てることでアクションを引き起こさせる。パスが/tasks
でGETメソッドの時は一覧を表示、POSTメソッドの時はタスクを追加する。パスが/tasks/id
のDELETEメソッドの場合idのタスクを削除する(idは任意のタスクに割り当てられた数字、DELETEメソッドは後で作成)。ルーティングは/myapp/routes/web.php
で設定しており、以下のよう変更することで実装した。
Route::get('/', function () {
return redirect('/tasks');
});
Route::get('/tasks', 'TaskController@index');
Route::post('/tasks', 'TaskController@store');
Route::delete('/tasks/{id}', 'TaskController@destroy');
最初のRouteではパスが/
の時/tasks
にリダイレクトするようにしている。二つ目はコントローラーのindex
メソッドをパスが/tasksでGETメソッドの時引き起こすようにしている。三つ目はコントローラーのstore
メソッドをパスが/tasksでPOSTメソッドの時引き起こすようにしている。最後は三つ目はコントローラーのdelete
メソッドをパスが/tasks/idでDELETEメソッドの時引き起こすようにしている。また、デフォルトではコントローラーのフルパスが必要なので/myapp/app/Providers/RouteServiceProvider.php
のコメントアウトされているコードprotected $namespace = 'App\\Http\\Controllers';
のコメントアウトを外す必要がある。
ビューの設定
コントローラーを作成した時indexメソッドでビューを使用した。ビューはLaravelのBladeと言うテンプレートをもとに作成するものであり、return view(文字列, 連想配列)
のように使われる。文字列の部分はテンプレートファイルを指しており、テンプレートファイルは/myapp/resources/views/文字列.blade.php
のように作成する。連想配列はこのファイル内でキーがそのバリューの変数のように扱う。今回のケースであれば/myapp/resources/views/tasks.blade.php
とテンプレートファイルを作成し、$tasks
と言う変数を使える。この変数はタスクの一覧が配列で格納されている。テンプレートファイルは以下のように書く。
@extends('layout')
@section('content')
<h1>Task List</h1>
<form action="/tasks" method="POST" class="form-horizontal">
{{ csrf_field() }}
<!-- Task Name -->
<div class="form-group">
<label for="task" class="col-sm-3 control-label">Task</label>
<div class="col-sm-6">
<input type="text" name="name" id="task-name" class="form-control">
</div>
</div>
<!-- Add Task Button -->
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-default">
<i class="fa fa-plus"></i> Add Task
</button>
</div>
</div>
</form>
<!-- Current Tasks -->
<h2>Current Tasks</h2>
<table class="table table-striped task-table">
<thead>
<th>Task</th><th> </th>
</thead>
<tbody>
@foreach ($tasks as $task)
<tr>
<!-- Task Name -->
<td>
<div>{{ $task->name }}</div>
</td>
<td>
<form action="/tasks/{{ $task->id }}" method="POST">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button>Delete Task</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
@endsection
長いので細かいところの説明は省略する。{{ csrf_field() }}
はcsrf対策である。@foreach ($tasks as $task)
はblade特有の記法で$tasks配列でループを回している。{{ $task->id }}
はループ内のタスクのidを抜き出しており、nameだとタスク名を抜き出している。{{ method_field('DELETE') }}
ではDELETEメソッドを擬似的に作成している。
また、最初の一文はレイアウトファイル/myapp/resources/views/layout.blade.php
を使用することを意味している。レイアウトファイルは以下のようになっている。
<!DOCTYPE html>
<html>
<head>
<title>Task List</title>
<!-- CSS And JavaScript -->
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
<link rel="stylesheet" href="//cdn.rawgit.com/necolas/normalize.css/master/normalize.css">
<link rel="stylesheet" href="//cdn.rawgit.com/milligram/milligram/master/dist/milligram.min.css">
</head>
<body>
<div class="container">
@yield('content')
</div>
</body>
</html>
中盤にある@yield('content')
にはこのファイルをエクスポートしたbladeテンプレートの@section('content')
から@endsection('content')
までの文を当てはめると言う意味がある。このコードはCSSなどを追加して見た目をよくしているだけである。
環境の違いによるコードの変更(講義を受けていない人には関係ない)
講義で使用した環境と本記事で構築した環境が異なることによって3つの変更が要請された。
1.タイトル部分
講義ではファイルの編集の練習のため、myapp/resources/view/welcome.blade.php
の<div class="title m-b-md">
に囲まれた文字列を変更している。構築した環境において同名のファイルには前述のタグがなく、他の内容も著しく異なるのでここは割愛した(ファイル編集の練習としているだけなので、無理にしなくても良いと考えた)。
2.HTTPSについて
講義ではPaizaCloudを使っているため、HTTPSプロコトルを使っている。構築した環境ではHTTPを使っているので、myapp/routes/web.app
に追加した\URL::forceSchme('https')
は不要となる。また、myapp/app/Providers/AppServiceProvider.php
内のAppServiceProvider
クラスのboot
メソッドに追加した\URL::forceSchme('https')
も同様に不要。
3.ルーティング設定
構築した環境では以前はあったmyapp/app/Providers/RouteServiceProvider.php
ファイルからデフォルトの名前空間がコメントアウトされている。これによってコントローラーの呼び出しをフルパスで与えなければいけなくなっている。これはprotected $namespace = 'App\\Http\\Controllers';
のコメントアウトを解除することで解決できる。当然コメントアウトせずに
Route::get('/tasks', 'App\Http\Controllers\TaskController@index');
Route::post('/tasks', 'App\Http\Controllers\TaskController@store');
Route::delete('/tasks/{id}', 'App\Http\Controllers\TaskController@destroy');
としても良い。