【PHP】Laravel で WEBアプリケーション開発

著者画像
Toshihiko Arai
(更新:2024年7月23日(火))

以前に仕事で、Laravelを使ったWEBアプリケーションの開発に携わりました。本記事はその際の忘備録になります。

macOSにLaravelをインストールして開発環境を整える

macOSバージョン

$ sw_vers

ProductName:        macOS
ProductVersion:     14.2
BuildVersion:       23C64

PHPバージョン

$ php -v
PHP 8.3.2 (cli) (built: Jan 16 2024 13:46:41) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.2, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.2, Copyright (c), by Zend Technologies

Composerのインストール

brew install composer

バージョン

composer --version
Composer version 2.6.6 2023-12-08 18:32:26

Laravelインストーラーのグローバルインストール

composer global require laravel/installer

環境パスを通す

laravelコマンドを使うために以下の通り追加:

export PATH="$PATH:/Users/mopipico/.composer/vendor/bin"

新しいターミナルセッションで動作確認:

laravel -v
Laravel Installer 5.2.0

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display help for the given command. When no command is given display help for the list command
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi|--no-ansi  Force (or disable --no-ansi) ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  completion  Dump the shell completion script
  help        Display help for a command
  list        List commands
  new         Create a new Laravel application

これでLaravelコマンドが使えるようになった。

Laravelプロジェクトの作成

適当なディレクトリへ移動し、新規プロジェクトを作成する:

laravel new test_laravel

質問を適宜選択

プロンプトが表示されるので、適宜選択する。

プロンプト

今回選択した回答:

  1. Would you like to install a starter kit?: No starter kit
  2. Which testing framework do you prefer?: PHPUnit
  3. Would you like to initialize a Git repository?: No
  4. Which database will your application use?: MySQL

プロジェクトディレクトリへ移動

cd test_laravel

作成されたプロジェクトルート下で、Laravelのバージョンを確認する:

php artisan --version
Laravel Framework 10.43.0

プロジェクトサイトへアクセスする

ローカルサーバーの起動

Laravel開発サーバーを起動するには、プロジェクトルート下で次を実行:

php artisan serve

Webブラウザで http://localhost:8000/ へアクセスし、下図のようにサイトが表示されればLaravel開発環境構築の成功。

サイトトップページ

プロジェクトを編集して、Hello world を表示してみる

routes/web.php ファイルを開き、以下のように編集:

Route::get('/', function () {
    return 'Hello World';
});

これで、ブラウザを開いて http://localhost:8000 にアクセスすると、“Hello World” と表示される。

Hello, world!

Laravelに登録されているコマンドリスト(バッチ用)を一覧表示する

Laravelでは php artisan list を使うと、php artisan で実行できるコマンドの一覧が表示されます。その中から、Command クラスを継承して作った独自コマンドだけを一覧表示する場合は、次のようにフィルタすると良いです(macOS、linux環境)。

php artisan list | grep 'command:'   

実行すると $signature に登録されたcommand:から始まるコマンドだけを一覧表示できます。

  command:batch-task1                        Command description
  command:batch-task2                        Command description
  command:batch-task3                        Command description

LaravelでTailwind CSSが反映されない

Laravelでレイアウト調整するために、bladeにTailwindのクラスを追加したのですが、反映されませんでした。結論から言うと npm run dev を起動してなかったのが原因でした。php artisan servenpm run dev は、Laravelアプリケーションの異なる側面を処理するために使用されるコマンドであり、それぞれのルートディレクトリやURLの違いについて説明します。

php artisan serve

php artisan serve

npm run dev

npm run dev

組み合わせて使用する方法

このスクリプトは、MySQLサーバー、フロントエンドの開発サーバー(npm run dev)、およびLaravelのサーバーを起動し、Control + Cで終了すると各サーバーを停止します。

#!/bin/bash

# MySQLサーバーを開始
mysql.server start

# 作業ディレクトリを移動
cd web/

# npm開発サーバーをバックグラウンドで起動
npm run dev &
NPM_PID=$!

# Laravelサーバーを特定のポートでバックグラウンドで起動
php artisan serve --port=8700 &
LARAVEL_PID=$!

# Ctrl+Cが押されたらサーバーを停止するためのトラップを設定
trap "echo 'Stopping servers...'; kill $NPM_PID $LARAVEL_PID; mysql.server stop; exit" INT

# サーバーのプロセスが終了するまで待機
wait $NPM_PID
wait $LARAVEL_PID

Laravel で実行されたSQLをログに表示したい

Laravelは、ORM機能によってSQLを直接書かずにデータベース操作を行えてコーディングがとても楽です。しかし、デバッグ作業で実行されたSQLを実際に見たい場合も多々あります。そういった場合に、logファイルに実行されたSQL文を出力する二つの方法をご紹介いたします。

enableQueryLog() を使った方法

こちらは最も手軽なSQLのプリントデバッグ方法です。

    public function index()
    {
        // SQLログを有効にする
        DB::enableQueryLog();

        $perPage = config('constants.pagination.per_page');
        $persons = Person::where('delete_flg', 0)->paginate($perPage);

        // 実行されたSQLを取得
        $queries = DB::getQueryLog();
        Log::info($queries);

        return view('persons.index', compact('persons'));
    }

tail -f ./storage/logs/laravel.log を実行すれば、次のような形で出力されるはずです。

[2024-07-23 20:07:59] development.INFO: array (
  0 => 
  array (
    'query' => 'select count(*) as aggregate from `a_m_person` where `delete_flg` = ? and `user_id` = ?',
    'bindings' => 
    array (
      0 => 0,
      1 => 7,
    ),
    'time' => 0.61,
  ),
  1 => 
  array (
    'query' => 'select * from `a_m_person` where `delete_flg` = ? and `user_id` = ? limit 2 offset 2',
    'bindings' => 
    array (
      0 => 0,
      1 => 7,
    ),
    'time' => 0.72,
  ),
)  

しかしながら、毎回囲うのは手間ですし、管理もしずらくなります。

        DB::enableQueryLog();
        
        ...

        $queries = DB::getQueryLog();
        Log::info($queries);

そこで次に紹介する方は、ミドルウェアを使って、ルーターレベルで一括管理できる方法を紹介いたします。

ミドルウェアの作成

まず、ミドルウェアを作成します。

php artisan make:middleware LogSqlQueries

生成されたミドルウェアファイルに以下のコードを追加します。

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class LogSqlQueries
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // SQLログを有効にする
        DB::enableQueryLog();

        $response = $next($request);

        // 実行されたSQLを取得
        $queries = DB::getQueryLog();
        Log::info('SQL Queries:', $queries);

        return $response;
    }
}

ミドルウェアをルートに適用

次に、ミドルウェアを適用したいルートにミドルウェアを登録します。routes/web.phproutes/api.phpにて、以下のようにミドルウェアを適用します。

use App\Http\Middleware\LogSqlQueries;

Route::middleware([LogSqlQueries::class])->group(function () {
    Route::get('/persons', [PersonController::class, 'index']);
    // 他のルート
});

twig の使い方

twig(ツイッグ)は、PHPで人気の、テンプレートエンジン。

Symfony(2007年)を開発した、Sensio labs社が2009年に開発。

何も考えずに開発すると、PHPとHTMLでスパゲッティ状態になるところ、twigを使うことで、PHPとHTMLを分離することができる。 つまり、ロジックとUIの分離ができ、メンテナンス性や開発速度の向上を図れる。

開発環境

macOS環境にて以下の通り、composerとphpがインストール済み。

$ composer --version
Composer version 2.6.6 2023-12-08 18:32:26

$ php --version
PHP 8.3.2 (cli) (built: Jan 16 2024 13:46:41) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.2, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.2, Copyright (c), by Zend Technologies

ComposerはPHPの依存関係管理ツール

プロジェクトに必要なライブラリやパッケージを管理し、インストールや更新を容易に行える。

Twigを使って”Hello, World!“をブラウザに表示するまで

まずは使って twig の感触と便利さを体験する。

適当な場所に空のプロジェクトディレクトリを作成。そこのルートへ移動して、以下のコマンドを実行:

composer require twig/twig

これで twig がPHPで使えるようになった。

必要なディレクトリを作成

ファイルを管理しやすいように、プロジェクト配下に templatescompilation_cache ディレクトリを作成:

mkdir templates compilation_cache

テンプレートファイルを作成

templates ディレクトリ内に hello.html.twig という名前のテンプレートファイルを以下内容で作成:

<html>
    <body>
        <p>Hello, {{ name }}!</p>
    </body>
</html>

Twigの環境を設定

index.php を作成し、というファイルにTwigの基本設定を記述した例です。

<?php
require_once './vendor/autoload.php';

$loader = new \Twig\Loader\FilesystemLoader('./templates');
$twig = new \Twig\Environment($loader, [
    'cache' => './compilation_cache',
    'auto_reload' => true,
]);

Twigがテンプレートをコンパイルした後のキャッシュを ./compilation_cache へ格納される。キャッシュ機能を使用しない場合は、このオプションを省略する。

Twigテンプレートをレンダリング

最後に、index.php にTwigテンプレートをレンダリングするコードを追加:

// ... 前のTwig設定コード ...

echo $twig->render('hello.html.twig', ['name' => 'World']);

結果をブラウザに表示

phpコマンドで、ローカルサーバーを起動:

php -S localhost:8000

http://localhost:8000/index.php へアクセスして「Hello, World!」が表示されれば成功。

Composer を使った Guzzle HTTP

PHPのCompoerへの理解を深めるために、ライブラリをインストールしてプログラミングしてみます。今回はGuzzle HTTPクライアントをインストールして、HTTPリクエストしてみました。

開発環境

ソフトウェア バージョン
macOS 14.2
php 8.3.2
composer 2.6.6

guzzlehttp/guzzle とは

guzzlehttp/guzzle とは、Comoser でインストール可能な PHP の HTTP client library です。公式ページによれば、次のようにしてHTTPリクエストを行うことが可能になります。

$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle');

echo $response->getStatusCode(); // 200
echo $response->getHeaderLine('content-type'); // 'application/json; charset=utf8'
echo $response->getBody(); // '{"id": 1420053, "name": "guzzle", ...}'

// Send an asynchronous request.
$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
$promise = $client->sendAsync($request)->then(function ($response) {
    echo 'I completed! ' . $response->getBody();
});

$promise->wait();

guzzlehttp/guzzle のインストール

適当なワーキングディレクトリを作成して、そこへ移動してから composer require guzzlehttp/guzzle を実行します。

$ composer require guzzlehttp/guzzle
./composer.json has been created
Running composer update guzzlehttp/guzzle
Loading composer repositories with package information
Updating dependencies
Lock file operations: 8 installs, 0 updates, 0 removals
  - Locking guzzlehttp/guzzle (7.8.1)
  - Locking guzzlehttp/promises (2.0.2)
  - Locking guzzlehttp/psr7 (2.6.2)
  - Locking psr/http-client (1.0.3)
  - Locking psr/http-factory (1.0.2)
  - Locking psr/http-message (2.0)
  - Locking ralouphie/getallheaders (3.0.3)
  - Locking symfony/deprecation-contracts (v3.4.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 8 installs, 0 updates, 0 removals
  - Installing symfony/deprecation-contracts (v3.4.0): Extracting archive
  - Installing psr/http-message (2.0): Extracting archive
  - Installing psr/http-client (1.0.3): Extracting archive
  - Installing ralouphie/getallheaders (3.0.3): Extracting archive
  - Installing psr/http-factory (1.0.2): Extracting archive
  - Installing guzzlehttp/psr7 (2.6.2): Extracting archive
  - Installing guzzlehttp/promises (2.0.2): Extracting archive
  - Installing guzzlehttp/guzzle (7.8.1): Extracting archive
2 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
4 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
No security vulnerability advisories found.
Using version ^7.8 for guzzlehttp/guzzle

実行結果の通り、guzzlehttp/guzzle に依存するライブラリも同時にインストールされていることがわかります。

インストール後のディレクトリ

composerを使ってライブラリをインストールすると、composer.jsoncomposer.lockといったファイルが生成されます。そしてvendorディテクトりに、ライブラリが格納されます。

$ tree -L 2                                                                                    9:02:57
.
├── composer.json
├── composer.lock
└── vendor
    ├── autoload.php
    ├── composer
    ├── guzzlehttp
    ├── psr
    ├── ralouphie
    └── symfony

composer.json

生成された composer.json パッケージのバージョンがキャレット演算子で指定されています。これは、次のメジャーバージョンまでのすべての変更を利用可能にするものです。下記の場合 >=7.8 <8.0 に相当します。

{
    "require": {
        "guzzlehttp/guzzle": "^7.8"
    }
}

Guzzle HTTPクライアントを使ったリクエスト

それでは実際にPHPでGuzzle HTTPクライアントを使ったテストを行います。

<?php

require 'vendor/autoload.php';

use GuzzleHttp\Client;

$client = new Client();
$response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle');

echo $response->getStatusCode();
echo $response->getHeaderLine('content-type');
echo $response->getBody();

このようにGuzzle HTTPクライアントを使うことで、HTTPリクエストをわかりやすく記述することができます。 require 'vendor/autoload.php'; はオートローダーという仕組みで、インストールしたパッケージから外部ファイルやクラスなどを自動で読み込んでくれ流ため、いちいち必要なクラスを取り込むために includerequire で記述せずに済みます。 use GuzzleHttp\Client; で名前空間を指定して、クラスやメソッドを使いやすくします。

今度は、PSR-7リクエストを使って非同期でHTTPリクエストを実行してみます。

<?php

require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;

$client = new Client();
$request = new Request('GET', 'http://apppppp.com/jojo.json');

$promise = $client->sendAsync($request)->then(function ($response) {
    // 非同期で実行される
    echo '②I completed! ' . $response->getBody();
});

echo "①先に処理する\n";

$promise->wait();

echo "③待ってから処理する\n";

sendAsync で非同期でHTTPリクエストを投げ、レスポンスが完了したところでthenメソッド内のクロージャ(無名関数)が実行されます。よって、echo "①先に処理する\n";が先に実行されます。その後の記述 $promise->wait(); では、非同期リクエストが完了するまで待機します。ですから、クロージャが実行された後に、echo "③待ってから処理する\n";が実行されるわけです。

このプログラムを実行すると、次のような結果となるはずです。

$ php main.php
①先に処理する
②I completed! { 
"name":"Jyotaro",
"stand":"The World"
}
③待ってから処理する

Amazonで探す