Laravel PHPでRESTful APIを構築する方法

June 25, 2019
執筆者
Michael Okoh
寄稿者
Twilio の寄稿者によって表明された意見は彼ら自身のものです
レビュー担当者

この記事はMichael Okohこちらで公開した記事(英語)を日本語化したものです。

ソーシャルネットワークから銀行アプリケーションまで、現代社会は多くのAPIで動いています。本稿では、Laravel PHPを使ってRESTful APIと、それを実装するアプリケーションを構築する方法について学びます。

必要条件

このチュートリアルでは、PHP言語とLaravelフレームワークの基本的な知識と、以下の項目が必要です。

  • PHP 7.1以降
  • Composer
  • MySQL
  • Laravel 5.6以降
  • Postman

作成するアプリケーションについて

本稿では、学生に関するデータを取り扱うCRUD APIを構築します。CRUDは、Create(作成)、Read(読み取り)、Update(更新)、Delete(削除)を意味します。このAPIには、次のエンドポイントがあります。

  • GET /api/studentsは、すべての学生レコードを返し、GETリクエストを受け入れます。
  • GET /api/students/{id}は、学生レコードのidを参照して学生レコードを返し、GETリクエストを受け入れます。
  • POST /api/studentsは、新しい学生レコードを作成し、POSTリクエストを受け入れます。
  • PUT /api/students/{id}は、学生レコードのidを参照して既存の学生レコードを更新し、PUTリクエストを受け入れます。
  • DELETE /api/students/{id}は、学生レコードのidを参照して学生レコードを削除し、DELETEリクエストを受け入れます。

学生レコードには、namecourseのみが詳細情報として含まれます。これらのエンドポイントの開発が完了したら、エンドポイントを使用して、実際の学生レコードに関するデータを取り扱うアプリケーションを開発します。

Laravelアプリケーションの設定をする

まず、Laravelアプリケーションを作成する必要があります。これを行うには、ターミナルで次のコマンドを実行します。

$ laravel new api-project

次に、以下のコマンドで現在のディレクトリをプロジェクトのルートフォルダに変更します。

$ cd api-project

Laravelサーバーがまだ実行されていない場合は、以下のコマンドでLaravelサーバーを起動します。

$ php artisan serve

アプリケーションにはhttps://localhost:8000からアクセスできます。

Laravel default landing

次に、以下のコマンドを実行し、アプリケーションの新しいデータベースを作成します。

$ mysql -uroot -p

MySQLで認証する際に、すでにパスワードを設定している場合は、MySQLパスワードを入力するように求められます。次のコマンドを実行し、api-projectという名前の新しいデータベースを作成します。

CREATE DATABASE `api-project`;

MySQL root in terminal

移行しながらモデルの作成を進めることができます。これを行うには、次のコマンドを実行します。
$ php artisan make:model Student -m

Student.phpという名前の新しいファイルがappディレクトリに作成されます。

: ファイルを編集して、対話したいデータベーステーブルと書き込み可能なフィールドを指定する必要があります。

<?php
  
namespace App;
  
use Illuminate\Database\Eloquent\Model;
  
class Student extends Model
{
    protected $table = 'students';
  
    protected $fillable = ['name', 'course'];
}

さらに、移行ファイルがdatabase/migrationsディレクトリに作成され、テーブルが生成されます。移行ファイルを変更し、文字列値を受け入れるnamecourseの列を作成します。

...
public function up()
{
    Schema::create('students', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->string('course');
        $table->timestamps();
    });
}
…

次に、テキストエディタでプロジェクトフォルダを開き、.envファイルを以下のように変更して適切なデータベース認証情報を入力できるようにします。これにより、作成したデータベースにアプリケーションを正しく接続できます。

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=<your-database-name>
DB_USERNAME=<your-database-username>
DB_PASSWORD=<your-database-password>

次に、以下のコマンドを使用して、移行を実行します。

$ php artisan migrate

ルートを設定する

アプリケーションの基本を設定したら、次のコマンドを実行して、APIのメソッドが含まれるコントローラーの作成を進めます。

$ php artisan make:controller ApiController

app\http\controllersディレクトリに、ApiController.phpファイルがあります。以下のメソッドを追加します。

...
class ApiController extends Controller
{
    public function getAllStudents() {
      // logic to get all students goes here
    }
  
    public function createStudent(Request $request) {
      // logic to create a student record goes here
    }
  
    public function getStudent($id) {
      // logic to get a student record goes here
    }
  
    public function updateStudent(Request $request, $id) {
      // logic to update a student record goes here
    }
  
    public function deleteStudent ($id) {
      // logic to delete a student record goes here
    }
}

routesディレクトリに進み、api.phpファイルを開き、ApiControllerで作成済みのメソッドを参照するエンドポイントを作成します。

...
Route::get('students', 'ApiController@getAllStudents');
Route::get('students/{id}', 'ApiController@getStudent');
Route::post('students, 'ApiController@createStudent');
Route::put('students/{id}', 'ApiController@updateStudent');
Route::delete('students/{id}','ApiController@deleteStudent');

api.phpのすべてのルートには、デフォルトで先頭に/apiが付きます。

学生レコードを作成する

ApiControllercreateStudentメソッドを使います。

public function createStudent(Request $request) {
  // logic to create a student record goes here
}

エンドポイントに渡されるデータを取得するには、Laravelリクエストクラスを使用します。エンドポイントでは、string型のnamestring型のcourseも必要になります。データを正常に取得できたら、データベースにデータを保存します。

...
use App\Student;
  
class ApiController extends Controller
{
  ...
  public function createStudent(Request $request) {
    $student = new Student;
    $student->name = $request->name;
    $student->course = $request->course;
    $student->save();
  
    return response()->json([
        "message" => "student record created"
    ], 201);
  }
  ...
}

上記のスニペットでは、データベースのstudentsテーブルと対話するStudentモデルをインポートします。createStudentメソッドでは、メソッドパラメーターの新しいRequestオブジェクトをインスタンス化し、続いてStudentオブジェクトをインスタンス化します。最後に、$student->ごとに同等のリクエストが取得され、保存されます。

操作が成功すると、student record createdメッセージとレスポンスコード201でJSONレスポンスがAPIユーザーに返信されます。

このメソッドは、routes/api.phpにあるルートのファイルにあらかじめ定義したため、すでに/api/studentsに紐付けられています。

...
Route::post('students, 'ApiController@createStudent');
…

テストする

テストする前に、アプリケーションが実行されていることを確認します。前述のように、ビルトインのコマンドを使用できます。

$ php artisan serve

または、PHPアプリケーションのプロキシパスを作成するためのツールであるValetを使用して、アプリケーションをローカルでテストするための.testまたは.devドメインを指定できます。

このエンドポイントをテストするには、Postmanを開き、http://localhost:8000/api/studentsまたはhttp://<folder-name>/api/students(Valetを使用する場合)にPOSTリクエストを実行します。次のスクリーンショットに示すよう​​に、form-dataオプションを選択し、次の値を渡します。

Postman view of POST request

成功メッセージと201レスポンスコードが返されれば正常です。次のタスク用に、データベースに後数件、レコードを追加してみてください。

すべての学生レコードを返す

次に、ApiControllergetAllStudentsメソッドを編集します。

public function getAllStudents() {
  // logic to get all students goes here
}

すでにインポートしたStudentモデルを使用し、データベースのすべての学生を返すためのシンプルなEloquentクエリを作成します。

class ApiController extends Controller
{
  public function getAllStudents() {
    $students = Student::get()->toJson(JSON_PRETTY_PRINT);
    return response($students, 200);
  }
  ...
}

Eloquentクエリは->toJson(JSON_PRETTY_PRINT);で終わります。Eloquentで返されるオブジェクトデータが、適切に書式設定されたJSONにシリアル化されます。JSONはレスポンスコード200と共に返されます。

このメソッドは、routes/api.phpにあるルートのファイルにあらかじめ定義したため、すでに/api/studentsルートに紐付けられています。

...
Route::get('students', 'ApiController@getAllStudents');
…

テストする

アプリケーションがバックグラウンドで実行されている場合は、Postmanの/api/studentsエンドポイントにGETリクエストを実行します。

 

Postman view of GET request

上のスクリーンショットに示すように、エンドポイントはデータベースのすべての学生レコードを返します。

学生レコードを返す

単一の学生レコードのみを返すためのエンドポイントを作成します。ApiControllergetStudentメソッドを使います。

public function getStudent($id) {
  // logic to get a student record goes here
}

学生レコードのidで学生レコードを取得し、これに対して、その学生レコードのidで学生レコードを返すためのEloquentクエリを作成します。

...
class ApiController extends Controller
{
  ...
  public function getStudent($id) {
    if (Student::where('id', $id)->exists()) {
        $student = Student::where('id', $id)->get()->toJson(JSON_PRETTY_PRINT);
        return response($student, 200);
      } else {
        return response()->json([
          "message" => "Student not found"
        ], 404);
      }
  }
  ...
}

前述のスニペットは、指定したidの学生レコードが存在するかどうかをチェックします。存在する場合は、Eloquentを使用してデータベースにクエリを実行し、idと一致するレコードをJSON形式にて、レスポンスレコード200と共に返します。指定したidがデータベースで見つからない場合、student not foundメッセージと404レスポンスコードを返します。

このメソッドは、routes/api.phpにあるルートのファイルにあらかじめ定義したため、すでに/api/students/{id}ルートに紐付けられています。

...
Route::get('students/{id}', 'ApiController@getStudent');
...

テストする

Postmanを開き、/api/students/{id}エンドポイントにGETリクエストを実行します。{id}は、データベースの既存レコードのidにします。

 

Postman view of GET request for a single record

上のスクリーンショットに示すように、http://api-project.test/api/students/3にリクエストを実行し、そのidに割り当てられた学生の詳細が返されました。次に、存在しない学生レコードをリクエストします。

Postman view of GET request for a single deleted record

上のスクリーンショットに示すように、id100の存在しない学生レコードの詳細を要求するリクエストがエンドポイントに送信されました返されました。APIが正常に実行され、エラーメッセージと404ステータスコードが返されました。

学生レコードを更新する

次に、既存の学生レコードの詳細を更新するためのエンドポイントを作成します。ApiControllerupdateStudentメソッドを使います。

public function updateStudent(Request $request, $id) {
  // logic to update a student record goes here
}

これを行うには、更新しようとしているレコードが存在するかどうかを確認する必要があります。存在する場合、指定したidと一致するレコードが更新され、ステータスコード204が返されます。存在しない場合、レコードが見つからないことを示すメッセージとステータスコード404が返されます。

public function updateStudent(Request $request, $id) {
    if (Student::where('id', $id)->exists()) {
        $student = Student::find($id);
        $student->name = is_null($request->name) ? $student->name : $request->name;
        $student->course = is_null($request->course) ? $student->course : $request->course;
        $student->save();
  
        return response()->json([
            "message" => "records updated successfully"
        ], 200);
        } else {
        return response()->json([
            "message" => "Student not found"
        ], 404);
          
    }
}

nameまたはcourseのいずれかの属性のみを更新する必要がある場合に備えて、検証を追加しています。リクエストを受け取ると、nameまたはcoursenullかどうかをチェックします。nullの場合は、データベースのレコードをを既存の値に置き換えます。nullでない場合は、nullが新しい値として渡されます。これはすべて三項演算子を使用して実行されます。

: 三項演算子の形式はcondition ? true : falseです。

このメソッドは、routes/api.phpにあるルートのファイルにあらかじめ定義したため、すでに/api/students/{id}ルートに紐付けられています。

...
Route::put('students/{id}', 'ApiController@updateStudent');
…

テストする

このエンドポイントをテストするには、/api/students/1GETリクエストを実行し、id1の学生レコードの詳細を返します。

 

Postman view of GET request for a single record

次のようなレコードが返されます。


[
    {
        "id": 1,
        "name": "Michael Okoh",
        "course": "Computer Science",
        "created_at": " 14:11:17",
        "updated_at": " 14:11:17"
    }
]

次に、/api/students/1PUTリクエストを実行し、courseを「Software Engineering」に変更します。PUTリクエストを実行するには、form-data経由でJSONペイロードを渡します。さらに、nameの値をTrojan Okohcourseの値を「Software Engineering」に変更します。

{
    "name": "Trojan Okoh",
    "course": "Software Engineering"
}

前述のスニペットはJSONペイロードで、レコードの更新に使用します。次に示すように、Postmanを開いてrawに変更し、型をJSON(application/json)に変更します。

Postman view for changing the form data type

次に、JSONペイロードをテキスト領域に貼り付け、エンドポイントにPUTリクエストを送信します。

Postman view of updated record

上のスクリーンショットに示すように、エンドポイントが成功メッセージを返しました。次に、/api/students/1にGETリクエストを実行し、レコードが実際に更新されたかどうかを確認します。

Postman view of GET request

学生レコードを削除する

最後に、学生レコードを削除するには、ApiControllerdeleteStudentメソッドを使います。

public function deleteStudent ($id) {
    // logic to delete a student record goes here
}

Eloquentを使用して、削除リクエストの対象となるレコードのidが存在するかどうかを確認します。存在する場合は、レコードを削除します。存在しない場合は、not foundメッセージと404ステータスコードを返します。

...
class ApiController extends Controller
{
    ...
    public function deleteStudent ($id) {
      if(Student::where('id', $id)->exists()) {
        $student = Student::find($id);
        $student->delete();
  
        return response()->json([
          "message" => "records deleted"
        ], 202);
      } else {
        return response()->json([
          "message" => "Student not found"
        ], 404);
      }
    }
}

このメソッドは、routes/api.phpにあるルートのファイルにあらかじめ定義したため、すでに/api/students/{id}ルートに紐付けられています。

...
Route::delete('students/{id}', 'ApiController@deleteStudent');

テストする

このエンドポイントをテストするには、/api/studentsエンドポイントにGETリクエストを実行し、データベースに存在するすべてのレコードをリストする必要があります。

 

Postman view of GET request

次に、/api/students/{id}DELETEリクエストを実行します。{id}は、削除リクエストをするレコードのidです。ここでは、テスト目的でid2のレコードを削除します。

Postman view of deleted record

リクエストが受け入れられたことを意味するステータスコード202と成功メッセージがエンドポイントから返されました。レコードが実際に削除されたかどうかを確認するには、/api/studentsエンドポイントにGETリクエストを実行し、データベースにあるすべての学生レコードをリストします。

Postman view of GET request for updated records

上のスクリーンショットに示すように、id2のレコードはもう存在しません。また、/api/students/{id}エンドポイントにGETリクエストを実行し、id2のレコードをリクエストして確認することもできます。レコードが見つからなかったことを示す404が返されます。

Postman view of GET request for missing record

まとめ

最後に、本稿でご紹介した重要なファイルの内容を確認します。

app\http\controllers\ApiController.php

 

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Student;

class ApiController extends Controller
{
    public function getAllStudents() {
      $students = Student::get()->toJson(JSON_PRETTY_PRINT);
      return response($students, 200);
    }

    public function createStudent(Request $request) {
      $student = new Student;
      $student->name = $request->name;
      $student->course = $request->course;
      $student->save();

      return response()->json([
        "message" => "student record created"
      ], 201);
    }

    public function getStudent($id) {
      if (Student::where('id', $id)->exists()) {
        $student = Student::where('id', $id)->get()->toJson(JSON_PRETTY_PRINT);
        return response($student, 200);
      } else {
        return response()->json([
          "message" => "Student not found"
        ], 404);
      }
    }

    public function updateStudent(Request $request, $id) {
      if (Student::where('id', $id)->exists()) {
        $student = Student::find($id);

        $student->name = is_null($request->name) ? $student->name : $request->name;
        $student->course = is_null($request->course) ? $student->course : $request->course;
        $student->save();

        return response()->json([
          "message" => "records updated successfully"
        ], 200);
      } else {
        return response()->json([
          "message" => "Student not found"
        ], 404);
      }
    }

    public function deleteStudent ($id) {
      if(Student::where('id', $id)->exists()) {
        $student = Student::find($id);
        $student->delete();

        return response()->json([
          "message" => "records deleted"
        ], 202);
      } else {
        return response()->json([
          "message" => "Student not found"
        ], 404);
      }
    }
}

routes\web.php

<?php

use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

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


Route::get('students', 'ApiController@getAllStudents');
Route::get('students/{id}', 'ApiController@getStudent');
Route::post('students, 'ApiController@createStudent');
Route::put('students/{id}', 'ApiController@updateStudent');
Route::delete('students/{id}', 'ApiController@deleteStudent');

app\Student.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Student extends Model
{
    protected $table = 'students';

    protected $fillable = ['name', 'course'];
}

Laravelを使用して、簡単なCRUD RESTful APIを構築できました。本稿では、Laravelを使ったCRUD RESTful APIの構築の基本について説明しましたが、リクエストの検証とAPIセキュリティについては取り上げませんでした。ぜひ、本稿をもとにリクエスト検証も試してみてください。

Twitter: [@ichtrojan](https://twitter.com/ichtrojan)

GitHub: [@ichtrojan](https://github.com/ichtrojan)

メール: michael@okoh.co.uk