Closed9

速習PHP

ぱんだぱんだ

PHPを使いそうになる気配があるので入門するメモ。
最低限PHPの基本文法とLaravelを使ったAPI実装ができるくらいを想定。

ぱんだぱんだ

Hello World

とりあえず。
PHPを動かすにはdocker必須みたいなことを聞いたことがある気がしたけどとりあえずなので普通にbrewでインストールする。

brew install php
% 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

PHPの最新の安定バージョンは8系ということなのかな、たぶん。

PHPのプログラムを動かすのにwebサーバーを用意してPHPファイルを配置する方法勧められるけどwebサーバー立てるのがめんどくさいのでCLIで動かす。

test.php
<?php
  echo 'Hello World';
php test.php
Hello World%
ぱんだぱんだ

基本文法

VSCode

VSCode使うので拡張機能をインストールする。いろいろあるけどPHP Intelephenseをインストールしておけば良さそう。必要に応じてデバッグやテストの拡張も必要かも。

拡張機能をインストールしたらVSCodeの標準のPHP拡張を無効にするらしい。

開始タグ

<?php ?>

このタグ内にプログラムを書いていくらしい。終了タグは省略可。

他の言語にはないこのタグがなぜ必要なのかを馬鹿のふりしてChatGPTに聞いてみた。

なぜタグが必要なのか
PHPで `<?php ?>` のようなタグを使用する理由は、PHPが元々HTMLドキュメントに組み込まれて動作するように設計されたスクリプト言語だからです。このタグは、PHPコードとHTMLコードを区別するために使用されます。

### PHPの設計哲学

PHPは「Personal Home Page Tools」(個人的なホームページツール)として開始され、最初はHTMLドキュメント内で簡単な動的コンテンツを生成するためのツールでした。従来のHTMLは静的なコンテンツしか扱えなかったため、PHPはHTMLに動的な要素を加える役割を果たしました。

### PHPとHTMLの混在

- **区別の必要性**: HTMLファイル内でPHPコードを記述する場合、サーバーはどの部分がPHPで処理すべきコードであり、どの部分がそのままクライアントに送信すべきHTMLなのかを知る必要があります。`<?php` タグは、PHP処理が開始される部分を、`?>` タグはPHP処理が終了する部分を示します。

- **柔軟性**: これにより、PHP開発者は同じドキュメント内でPHPコードとHTMLを柔軟に混在させることができます。例えば、データベースから取得したデータに基づいてHTMLコンテンツを動的に生成する場合などに有用です。

### モダンなPHP開発

現代のPHP開発では、MVC(Model-View-Controller)パターンのような設計パターンを使用して、PHPコードとHTMLの分離を促進することが一般的です。この場合、PHPコードは主に「Model」と「Controller」の部分に集中し、「View」部分ではHTMLとPHPを組み合わせてコンテンツを表示しますが、PHPコードはより構造化され、管理しやすくなっています。

このように、`<?php ?>` タグはPHPの根底にあるHTMLとの統合のための基本的な仕組みであり、PHPコードの開始と終了を明示的に示すために使用されています。

とりあえずPHPの設計思想としてもともとHTMLに組み込まれて使用されることを想定しているため、HTMLタグと区別できるように開始タグは必要らしい。

セミコロン

PHPでは末尾セミコロンが必須

変数

基本は以下のように宣言する。

$name = 'Yamanaka';

これは再代入可能な変数宣言である。

定数は以下のようにconstで宣言できる。

const CONST = 'CONST';

ローカル変数を再代入不可な変数として宣言したくなるができないよう。constを使ってしまうとスコープがグローバルになるので、厳密な関数内でのローカル変数宣言はできなそう。それは動的言語ということで諦める。

文字列

シングルクォーテーションでくくる。ダブルクォーテーションで特定のエスケープシーケンスや変数を展開できる。文字列の結合は以下のように.でできるらしい。

<?php
echo "Hello World\n";
$name = 'Yamanaka';
echo 'Hello' . ' ' . $name . "\n";

複数行の文字列を宣言するにはヒアドキュメントもしくはナウドキュメントがある。

// これはヒアドキュメント
$json = <<<EOT
{
  "value": 1
}
EOT;

// これはナウドキュメント
$json = <<<'EOT'
{
  "value": 1
}
EOT;

ナウドキュメントは変数やエスケープシーケンスが展開されない。

PHPは動的言語なので型付はない。しかし、PHP7以降くらいで型注釈の機能が入っている。主に関数の引数や戻り値につけれる。

function add(int $num, int $num2): int
{
  return $num + $num2;
}

対応している型はintstringのような基本データ型、arrayobjectといった複合型、iterablevoidといった特殊型、クラスやインターフェースの型、?を先頭につけることで宣言できるnullableといった型がある。

PHPの型チェックは弱い型チェックがデフォルトで暗黙的な型変換が行われる。

echo add(1.5, 2); // これができてしまう

以下の記述をファイルの先頭にすることで厳密な型チェックができる。

declare(strict_types=1);

また、関数だけでなくクラスのプロパティにも型注釈をつけることはできる。変数宣言時に型注釈をつけることはできない。

class User
{
  public string $name;

  public function __construct(string $name)
  {
    $this->name = $name;
  }
}

他言語でいうany型のようなどのような型でも受け入れるような型はPHPではmixedで宣言できる。また、PHPはユニオン型もサポートしているので以下のような感じで使える

function getType(mixed $val): string|null
{
  if (is_string($val)) {
    return 'string';
  } elseif (is_int($val)) {
    return 'int';
  } else {
    return null;
  }
}

上記の場合、戻り値がnullableなので?stringと書いた方がいい。
ちなみに、この関数の戻り値の型注釈をstringにしても特に警告は出ないが、引数にbool値などを指定するとnullが返り実行時エラーになる。これを事前に検知するには静的解析を利用するしかなさそうなのでもし本気でPHPやるなら静的解析は入れたい。

また、PHPではジェネリクスのサポートはない。より厳格な型のサポートを望むならPHPDocによるコメントと静的解析を組み合わせることで実現することができるかもしれない。

型のキャストについて。PHPでは以下のように比較的簡単にキャストができる。

$var = "100";
$intVar = (int)$var; // または (integer)$var
ぱんだぱんだ

基本文法2

配列と連想配列

$arr = [1, 2, 3];
$members = [
  'name' => 'Yamanaka',
  'age' => 33
];

ちなみに上記の$membersの型が何になるかというと(string|int)[]となる。phpの連想配列はかなり柔軟な表現ができるのでobjectとして扱ったほうがピンとくるかもしれない。

アクセス方法は以下のような感じ。

echo $arr[0];
echo $members['name'];

演算子

基本的な演算子は他の言語と一緒。ただし、等価比較がJavaScriptと同じで===による厳密な比較が推奨されている。また、phpは三項演算子も対応している。

if文

function fizzBuzz(int $num): string
{
  if ($num % 15 === 0) {
    $result = "FizzBuzz!!";
  } elseif ($num % 5 === 0) {
    $result = "Buzz!!";
  } elseif ($num % 3 === 0) {
    $result = "Fizz!!";
  } else {
    $result = "Unkown";
  }
  return $result;
}

特に書くこともないけどelseifelse ifの2種類書き方があるらしいけど、一応elseifが推奨らしい。ほとんど挙動は変わらないけど後者はelseifの2単語になるので挙動が変わるときもある。

ループ

foreachforwhileがある。

foreach ($arr as $num) {
  echo "$num\n";
}

foreach ($arr as $i => $num) {
  echo "$i => $num\n";
}
1
2
3
0 => 1
1 => 2
2 => 3

for文を使うと以下のようにも書ける

for ($i = 0; $i < count($arr); $i++) {
  echo "$arr[$i]\n";
}

for ($i = 0; $i < count($arr); $i++) {
  echo "$i => $arr[$i]\n";
}

forとforeachは結果としては同じだが、foreachの方が配列のコピーを内部的に作るためわずかにオーバーヘッドが生じるよう。要素数が多い場合はfor文の方がいいかもしれない。

$a = 0;
while ($a < 10) {
  $a++;
  echo $a;
}

whileはこんな感じ

switch文は以下のような感じ

$variable = 'value';
switch ($variable) {
  case 'value':
    echo "value\n";
    break;

  default:
    echo "no value\n";
    break;
}

ただ、PHPのswitch文はbreakが必要なため、記載漏れでバグを引き起こしかねない。さらに、switch文のcaseでの評価は===の厳密な等価評価ではなく==になるため、あまり積極的に使用されるものではないかもしれない。

ぱんだぱんだ

基本文法3

組み込み関数

PHPは多くの組み込み関数を持ち文字列や配列操作、CSVなんかの処理もできる。詳しくは以下の関数リファレンスを

https://www.php.net/manual/ja/funcref.php

例えば、組み込み関数の中には以下のようにJSONを扱う関数がある。

$json = <<<EOT
{
  "value": 1
}
EOT;

$decoded = json_decode($json);
$encoded = json_encode($decoded);

スコープ

PHPのスコープにはグローバルスコープローカルスコープスタティックスコープクラススコープがある。

グローバルスコープはスクリプトの最上位で宣言された変数などが属する。グローバルスコープに関数スコープなどからアクセスするにはglobal修飾子が必要。

$val = 'World';
hello();

function hello(): void {
    global $val;
    echo "Hello $val\n";
}

ローカルスコープはいわゆる関数スコープ。関数内で宣言された変数は外から参照することはできない。
スタティックスコープはstatic修飾子をつけることでその修飾子がついた変数は破棄されず保持されたり、クラスのインスタンスを作成することなく関数を呼び出したりができる。

function countup(): void {
    static $counter = 0;
    $counter++;
    echo $counter;
}

countup(); // 1
countup(); // 2
class User {
    public string $name;

    private function __construct(string $name) {
        $this->name = $name;
    }

    static public function newUser(string $name): User {
        return new User($name);
    }
}

クラススコープはクラス内に宣言されたプロパティや関数へのアクセスはインスタンスを経由しないとできない。

そもそも、PHPにはブロックスコープの概念がない。なのでforやif文のブロックでスコープは生成されないので外からアクセスできてしまう。

ぱんだぱんだ

基本文法4

無名関数と高階関数

PHPは無名関数と高階関数をサポートしている。

$add = function(int $x, int $y): int {
    return $x + $y;
};

echo $add(1, 2);
$add = function(int $x, int $y): int {
    return $x + $y;
};

echo calc(1, 2, $add);

function calc(int $x, int $y, callable $callback): int {
    return $callback($x, $y);
}

無名関数はアロー関数を使って以下のように書くこともできる。

array_map(fn(int $num) => $num * 2, [1, 2, 3]);

モジュール

PHPにおいて別のファイルを読み込むには以下の四つのキーワードを使用することができる。

  • include
  • require
  • include_once
  • require_once

includeは読み込むファイルがなかったときに警告になるがrequireはエラーになる。

<?php

require './user.php';

パスは相対パスでも絶対パスでも指定できる。絶対パスで指定するなら__DIR____FILE__という実行しているディレクトリやファイルパスを取得することができる定数が用意されている。

null

PHPには値が存在しないことをnullで表現する。明示的にnullを変数に代入することも可能だし、宣言していない変数の参照はnullとなる。

echo is_null($undefined); // 1
ぱんだぱんだ

クラス、インターフェース、トレイト

PHPはJavaなどと同様オブジェクト指向でプログラムを書ける。

クラス

<?php

class User {
    public string $name;

    private function __construct(string $name) {
        $this->name = $name;
    }

    static public function newUser(string $name): User {
        return new User($name);
    }
}

上記のようにプロパティと関数を定義できる。コンストラクタ関数はconstruct()を定義する。定義したクラスは以下のようにインスタンス化することができる。

$user = new User($name);

インスタンスのプロパティにアクセスするには以下のようにする。

echo $user->name;

クラスに定義されたインスタンス関数を実行するには以下のようにする。

$user->hello();

また、static修飾子で定義したstaticメソッドを呼び出すには以下のようにする。

$user = User::newUser("user");

PHPには抽象クラスを以下のように定義することもできる。

abstract class AbstractPerson {
    public string $name;

    public function hello(): string {
        return "hello";
    }

    abstract public function greet(): string;
}

class Person extends AbstractPerson {
    public function __construct(string $name) {
        $this->name = $name;
    }

    public function greet(): string {
        return 'Hi!!';
    }
}

PHPの抽象クラスには普通のクラス同様プロパティや関数の実態が定義することができるが、abstract修飾子をつけて関数宣言をすることで継承元に関数の実装を強制することができる。

子クラスから親クラスのプロパティへのアクセスは$thisでできる。

PHPには振る舞いを定義するためのinterfaceもあり、以下のように使える。

interface UpperName {
    public function upperName(): string;
}

class Person extends AbstractPerson implements UpperName {
    public function __construct(string $name) {
        $this->name = $name;
    }

    public function greet(): string {
        return 'Hi!!';
    }

    public function upperName(): string {
        return strtoupper($this->name);
    }
}

また、PHPにはトレイトというものも存在する。これは継承よりも柔軟に共通処理を使いまわすための機能と思えば良さそう。

trait Japanese {
    public function helloJa(): string {
        return 'こんにちは';
    }
}

class Person extends AbstractPerson implements UpperName {
    use Japanese;

    public function __construct(string $name) {
        $this->name = $name;
    }

    public function greet(): string {
        return 'Hi!!';
    }

    public function upperName(): string {
        return strtoupper($this->name);
    }
}

トレイトはいくつでも使うことができる。

アクセス修飾子

PHPにおけるアクセス修飾子はpublicprotectedprivateがある。PHPにおいてアクセス修飾子をつけれるのは関数やクラスのプロパティに対してだけでclassにはつけることはできない。

ぱんだぱんだ

モダンPHP

composer

PHPのパッケージマネージャーのようなもの。以下のようにバイナリをインストールしてパスを通す。

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'e21205b207c3ff031906575712edab6f13eb0b361f2085f1f1237b7126d785e826a450292b6cfd1d64d92e6563bbde02') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

sudo mv composer.phar /usr/local/bin/composer
$ composer --version
Composer version 2.6.6 2023-12-08 18:32:26

以下のようにしてcomposerの設定ファイルを作成することができる。

$ composert init

$ tree -L 2 .
.
├── composer.json
├── src
└── vendor
    ├── autoload.php
    └── composer
composer.json
{
    "name": "20240124/myapp",
    "autoload": {
        "psr-4": {
            "MyApp\\": "src/"
        }
    },
    "require": {}
}

composerを使用して公開されているパッケージを使うには以下のようにする。

composer require guzzlehttp/guzzle

guzzleはPHPのHTTPクライアント。パッケージをインストールするとcomposer.lockが作成される。これはJSのnpmでいうpackage.jsonやpackage.lock.jsonと同じようなもの。

composerを使うとcomposer.jsonにオートロードの設定が自動的に記載されている。

    "autoload": {
        "psr-4": {
            "MyApp\\": "src/"
        }

この部分。psr-4はPHPのコード規約のようなものでそのうちの4でこれはオートロードについて定められているらしい。MyApp\\が名前空間でsrc配下のMyAPP名前空間にあるクラスを自動で読み込んでくれる。ただし、PHPファイルのどこかでautoload.phpを読み込んでおく必要はある。

require 'vendor/autoload.php';
src/User/User.php
<?php

namespace MyApp\User;
class User {
    public string $name;

    public function __construct(string $name) {
        $this->name = $name;
    }
}
main.php
<?php

require_once __DIR__ . "/vendor/autoload.php";

use MyApp\User\User;

$user = new User('user');
echo $user->name;
ぱんだぱんだ

Laravel

PHPフレームワーク。2022年2月にLaravel9.0がLTSになり、現在は1年ごとにリリースされているよう。執筆時点での最新版は10系。

以下のコマンドでプロジェクト作成

% composer create-project laravel/laravel task_test --prefer-dist

--prefer-distはファイルを圧縮できて少し早くなるとかなんとか

作成されると以下のように作られる。

% tree -a -L 1 .        
.
├── .editorconfig
├── .env
├── .env.example
├── .gitattributes
├── .gitignore
├── README.md
├── app
├── artisan
├── bootstrap
├── composer.json
├── composer.lock
├── config
├── database
├── package.json
├── phpunit.xml
├── public
├── resources
├── routes
├── storage
├── tests
├── vendor
└── vite.config.js

思ってたよりファイル数がかなり多い。

  • ほぼRails
  • cssとjsのバンドルにviteが使われている
  • viewはかなり柔軟でテンプレートエンジンを使う方法からvueやReactなどと併用したりなどが選べる
  • DBとのやり取りはORMであるEloquentとSQLのクエリビルダーがある
  • テンプレートエンジンにBladeと呼ばれるものが使われている。
  • コマンドラインからマイグレーションやサーバー起動などの開発タスクを実行できるArtisanというツールが用意されている。

API開発

railsみたいにAPIモードみたいなのはなさそうなので普通にプロジェクトを作成して、API実装していく感じらしい。DBに関してはdockerすらめんどくさいのでsqliteでやりたいと思ってたら、デフォルトのDBがMySQLからsqliteに変更されたらしい。

https://github.com/laravel/laravel/pull/6322

対応前のバージョンだったので手動で変更する。

$ touch database/database.sqlite

環境変数を以下のように変更する。

.env
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=

デフォルトでusersテーブル作成のマイグレーションファイルがあるのでmigrateを実行する。

$ php artisan migrate

APIのルートファイルが以下に用意されていた。

routes/api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Laravelのルーティングファイルはweb.phpapi.phpの2種類がありAPIのルーティングはapi.phpに書くようになっている。api.phpに書いたルーティングはプレフィックスにapiがパスにつく。上記のデフォルトのルーティングは正確にはapi/userとなる。

Controller

以下のコマンドでControllerを生成する。

$ php artisan make:controller UserController --api
app/Http/Controllers/UserController
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     */
    public function show(string $id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, string $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(string $id)
    {
        //
    }
}

まずはユーザー作成のエンドポイントを実装。ルーティングの設定から。

routes/api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

+Route::apiResource('/users', UserController::class);

これで新しく/usersのrouteが作成されているはず。

$ php artisan route:list

  GET|HEAD        / ............................................................................................................................................................
  POST            _ignition/execute-solution ..................................................... ignition.executeSolution › Spatie\LaravelIgnition › ExecuteSolutionController
  GET|HEAD        _ignition/health-check ................................................................. ignition.healthCheck › Spatie\LaravelIgnition › HealthCheckController
  POST            _ignition/update-config .............................................................. ignition.updateConfig › Spatie\LaravelIgnition › UpdateConfigController
  GET|HEAD        api/user .....................................................................................................................................................
  GET|HEAD        api/users ................................................................................................................. users.index › UserController@index
  POST            api/users ................................................................................................................. users.store › UserController@store
  GET|HEAD        api/users/{user} ............................................................................................................ users.show › UserController@show
  PUT|PATCH       api/users/{user} ........................................................................................................ users.update › UserController@update
  DELETE          api/users/{user} ...................................................................................................... users.destroy › UserController@destroy
  GET|HEAD        sanctum/csrf-cookie ........................................................................ sanctum.csrf-cookie › Laravel\Sanctum › CsrfCookieController@show

あとはControllerに処理を書いていく。

app/Http/Controllers/UserController
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Log;

class UserController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        return User::all();
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        Log::info('リクエストがきたよ');
        $user = User::create([
            'name' => $request->input('name'),
            'email' => $request->input('email'),
            'password' => bcrypt($request->input('password'))
        ]);
        Log::info(json_encode($user));
        return $user;
    }

    /**
     * Display the specified resource.
     */
    public function show(string $id)
    {
        return User::where('id', $id)->get();
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, string $id)
    {
        return User::where('id', $id)->update($request->all());
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(string $id)
    {
        return User::where('id', $id)->delete();
    }
}

めっちゃ簡単。ログ出力するのに設定修正する必要があったので以下にリンク貼っておく。

https://www.engilaboo.com/laravel-log-stdout/

あとエラー時のレスポンスが適切でないので今回はやらないけどちゃんと実装したほうがいい。というかwebの方で公開されているルートのパスもAPIとしてしか使わないなら無効化したほうがいい。

あとは少し認証のところ確認しておきたい。

認証

Laravelでは認証は大きくSPA認証とトークン認証の2つがある。SPA認証はその名の通りReactのようなSPAアプリケーションとAPI通信する時に使う認証でこれはCookieを使ったセッション認証になる。

モバイルとの通信認証などにはトークン認証が使え、Laravelではsanctumが使われる。ここではトークン認証を簡単に実装してみた。

流れとしてはlaravele/sanctomをプロジェクトにインストールする必要があるが、たぶんデフォルトで入ってる。はいってなければインストールと設定ファイルの公開とマイグレーションが必要。

次に認証に使うUserモデルにHasAPITokensトレイトを追加する。以下はデフォルトで用意されていたUserモデル。

<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
    ];
}

API通信をするには認証トークンを必須としたいためまずはトークンを生成するコントローラーを以下のように実装する。

app/Http/Controllers/AuthController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class AuthController extends Controller
{
    public function login(Request $request)
    {
        if (Auth::attempt(['email' => $request->email, 'password' => $request->password])) {
            $token = $request->user()->createToken('AccessToken')->plainTextToken;
            return response()->json(['token' => $token], 200);
        } else {
            return response()->json(['error' => '認証に失敗しました。'], 401);
        }
    }

    public function logout(Request $request)
    {
        $request->user()->currentAccessToken()->delete();
        return response()->json(['message' => 'ログアウトしました。'], 200);
    }
}

メールアドレスとパスワードでユーザーを探し、見つかればトークンを作成してレスポンスする。

コントローラーが作成できたらルートも追加する。

routes/api.php
<?php

use App\Http\Controllers\AuthController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Route::apiResource('/users', UserController::class);

+Route::post('/login', [AuthController::class, 'login'])->name('login');

これでログインするとトークンが発行されるのでそのトークンをAuthorizationヘッダーにBarerトークンとして貼り付けて毎回リクエストする。

するとルート定義でmiddlewareでトークン検証をはさんでいるのでトークンが不正であればAPI実行が失敗する。ちなみに、トークンが発行された時にDBのpersonal_access_tokensテーブルに発行したトークンが保存される。トークンはハッシュ関数を通してハッシュ化されている。これをリクエストにくっついているトークンと比較することで検証している。

このスクラップは2024/01/26にクローズされました