What ist Lavarel?
Just a few years ago web applications were still built in such a way that interactions with the database and view were executed directly by the application itself. Yet with the increasing popularity of mobile apps and JavaScript frameworks, central30
Laravel is an open-source framework written in PHP that was initiated by Taylor Otwell in 2011. It has subsequently enjoyed great popularity thanks to its simplicity and flexibility, and it also scores with its community spread around the world.
REST – what this contains
The term ‘RESTful API’ is surely a known expression for many of you. To examine this more closely, however, we first need to understand what ‘REST’ actually stands for and what principles it follows.
REST is short for ‘REpresentational State Transfer’, and it designates a programming paradigm for communication between the applications via a stateless protocol (usually HTTP). This is particularly relevant for distributed systems, particularly for web services.
HTTP methods define actions
In REST-compatible APIs the resources represent the endpoints and HTTP methods represent the associated actions. Which action should be initiated with which mode of access is defined precisely:
- GET: Read resource
Loads information of the desired resource from the server, causing no side effects. According to the specification, GET requests must be ‘secure’, i.e. a request may not have any effects on the resource. This behavior is termed nullipotent. - POST: Create new resource
Creates a new resource in the appropriate resource master using the data transmitted. With each invocation it creates new resources rather than returning the equivalents again. This behavior is termed not idempotent. - PUT: Update existing resource
Updates an existing resource using the data transmitted. Subsequent invocations do not cause further side effects. In contrast to POST, this behavior is here designated as idempotent. - DELETE: Delete resource
Deletes the requested resource, and similarly to PUT, its behavior is also idempotent.
The HTTP methods HEAD, OPTIONS, CONNECT and TRACE are optional, and are normally not required for CRUD operations. It is also important to point out here that adding an implementation of CONNECT or TRACE could affect the security of the application.
Storing data: POST vs. PUT
When it comes to data storage, be it a new create or an update, opinions are divided as to whether one should use POST, PUT or indeed PATCH (partial update).
The following examples are based on making updates to resources using PUT. PUT means creating or updating a resource at a defined location.
What also defines PUT is its idempotence. In other words, repeated requests using the same data will in fact only result in a single change.
Integration of REST in Laravel
Originally this framework was solely intended to be an improved alternative to the already popular CodeIgniter framework, since this lacked certain features such as authentication-related functions.
Over time, however, its scope of functions increased substantially, bringing with it both support for dependency injections and its own template engine (‘Blade’).
A further breakthrough occurred in 2012 with the ‘Artisan’ command-line tool, which enabled all interactions to be executed directly with the framework via the console. This was also followed by support for a still wider range of database systems.
With Version 4 the Laravel framework could then be loaded and managed wholly via Composer, which increased its expandability accordingly and so gave developers still more freedoms.
Further information on this is available directly on the Laravel framework homepage.
For the following examples we assume that the Laravel framework has already been installed and is executable. It is of no relevance whether this is done via Homestead, Composer or indeed by means of a manual installation of a published release from GitHub. The database connection should also already be configured and functioning.
Creating a resource
For the following example we assume that the model name is identical to the resource name. This is not strictly necessary but it is very helpful for promoting traceability.
As the resource, we will create a ‘note’ that features a ‘Subject’ and a ‘Body’ of text.
Let us start by creating the model, including migration using the ‘Artisan’ command-line tool supplied with Laravel.
To do this we navigate, using the terminal (Linux) or the prompt (Windows) directly to the project folder and enter the following command:
php artisan make:model Note -m
This command generates a new model called ‘Note’, and the optional parameter ‘m’ specifies that an appropriate migration is to be created along with it. The model just created is located at ./app/Note.php and the related migration is at ./database/migrations/YYYY_MM_DD_XXXXXX_create_notes_table.php.
Let us first look at the migration.
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateNotesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('notes', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('notes'); } }
The up() and down() methods are used to execute the migration and to reverse it, respectively.
The line $table->increments(‘id’) specifies that an INT field is created for this, with Auto Increment as the primary key. The instruction $table->timestamps() ensures that the ‘created_at’ and ‘updated_at’ fields, which are managed by Laravel, are also created. Whether these should in fact be used or not can also be configured directly within the model, but the above is used as standard.
Let us now add our two fields, ‘Subject’ and ‘Body’, to the migration just created, so that the up() method finally looks as follows:
public function up() { Schema::create('notes', function (Blueprint $table) { $table->increments('id'); $table->string('subject'); $table->text('body'); $table->timestamps(); }); }
We can now run the migration using the following command:
php artisan migrate
Should you run into an error such as ‘Specified key was too long error’, the following step may help.
For this, we extend the app service provider at ./app/Providers/AppServiceProvider.php as follows and then repeat the previous step:
use Illuminate\Support\Facades\Schema; public function boot() { Schema::defaultStringLength(191); }
Let us now return to our model, ‘Note’. We will add the ‘protected’-attribute ‘fillable’ to this and specify which of its fields can be populated in the database.
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Note extends Model { /** * @var array */ protected $fillable = ['subject', 'body']; }
Database seeding
Database seeding is understood as the filling (populating) of a database or database table (in relational systems) with test data. These data are normally generated dynamically using the random principle.
Let us now use the following command to create a seeder class that will fill our Notes table with test data:
php artisan make:seeder NotesSeeder
Next we open the seeder class just created at ./database/seeds/NotesSeeder.php and enter the following:
<?php use Illuminate\Database\Seeder; use App\Note; class NotesSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { // truncate already existing data Note::truncate(); // make instance of the Faker-class $faker = \Faker\Factory::create(); // insert some random generated records for ($i = 0; $i < 200; $i++) { Note::create([ 'subject' => $faker->sentence(), 'body' => $faker->paragraph(), ]); } } }
The faker class enables random pseudo-content to be generated, selectable by type. First, the database table is cleared, so that with each execution of the seeder we can start afresh. A loop is now used to generate and store 200 random entries. Next, to run the seeder, let us make use of Artisan again:
php artisan db:seed –class=NotesSeeder
Should we have multiple seeder classes, it would be simpler to add these to the central ‘database seeder’ by registering the individual seeder classes we have created in the run() method of the DatabaseSeeder() class in the same directory.
<?php use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $this->call( NotesSeeder::class ); } }
Now we can use the Artisan seed command to execute all the seeders registered in the DatabaseSeeder() without specifying a particular class.
php artisan db:seed
Creating a resource controller
In order for us to interact with the data, we require a controller to manage the communication between the client and the database. In this case, however, we require what is known as a resource controller – that is, one that implements at least the following actions: index(), store(), update() and destroy(). To do this, we use Artisan again:
php artisan make:controller NotesController
The newly created NotesController() is located at ./app/Http/Controllers/NotesController.php. Let us now create the actions listed above and implement the basic behavior.
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Note; class NotesController extends Controller { /** * Index function for general listing. * * @param Request $request * * @return \Illuminate\Http\JsonResponse */ public function index(Request $request) { $notes = Note::all(); return response()->json($notes); } /** * Store-Action * * @param Request $request * * @return \Illuminate\Http\JsonResponse */ public function store(Request $request) { $note = Note::create($request->all()); return response()->json($note); } /** * Show-Action * * @param Request $request * @param int $id * * @return \Illuminate\Http\JsonResponse */ public function show(Request $request, $id) { $note = Note::find($id); return response()->json($note); } /** * Update-Action * * @param Request $request * @param int $id * * @return \Illuminate\Http\JsonResponse */ public function update(Request $request, $id) { $note = Note::findOrFail($id); $note->update($request->all()); return response()->json($note); } /** * Destroy-Action * * @param Request $request * @param int $id * * @return \Illuminate\Http\JsonResponse */ public function destroy(Request $request, $id) { Note::find($id)->delete(); return response()->json([], 204); } }
If there are more than one resource controller, it would make sense to create a base resource controller and allow the various resource controllers to inherit from this. This gives the advantage of being able to use the base behavior in each controller while also allowing specific actions to be overwritten for individual controllers.
Registering API routes
Now that the basic CRUD behavior is implemented, the routes must be defined. To do this we open the file ./routes/api.php and add the following lines to it:
Route::resource('notes', 'NotesController');
The route configuration of Laravel makes it possible by this means to route an endpoint directly to a resource controller.For this purpose Laravel has reserved specific actions for itself with each controller. These are those that we created previously with NotesController(). The following table gives an indication of the URL structure for such a resource controller and of which action is present behind which HTTP method.
HTTP connection type | URI | Action | Route name |
GET | /notes | index | notes.index |
GET | /notes/create | create | notes.create |
POST | /notes | store | notes.store |
GET | /notes/{id} | show | notes.show |
GET | /notes/{id}/edit | edit | notes.edit |
PUT/PATCH | /notes/{id} | update | notes.update |
DELETE | /notes/{id} | destroy | notes.destroy |
It should be noted here that the actions create() and edit() have not been implemented in our resource controller. The reason for this is, that with both these actions, only the specific (HTML) form should be delivered via GET. Once our resource endpoint delivers only JSON data, however, this would then be void.
HTTP status codes
With REST APIs, in addition to the HTTP methods, other factors including the HTTP status codes and their meanings are also defined. The following listing gives a brief overview.
- 200: OK, no errors occurred
- 201: Object has been created
- 204: No contents – indicates that an action was successful but did not return any content
- 206: Partial contents – used e.g. with paginated content
- 400: Invalid request – standard response code if request was not valid
- 401: Unauthorized – either the user is not authorized or a log-in is required
- 403: Denied – the user is logged in but is not authorized to perform this action
- 404: Not found – the desired resource or endpoint does not exist
- 500: Internal server error – should not normally occur, but indicates that an unexpected error has occurred at the server while processing the request
- 503: Service not available – indicates that the requested resource or endpoint is temporarily unavailable
The use of these status codes is not mandatory, but if development is to be REST-compliant, they are indispensable.
Summary
In a time in which rich internet applications (RIA) and single-page applications such as Angular or even React are enjoying immense popularity and in which apps for mobile devices must also access central master data ever more frequently, it is increasingly important that consistent communications channels are used.
The use of REST-compliant (RESTful) APIs makes this relatively straightforward to realize in practice.
What was not discussed in these examples, however, for the sake of simplicity, is securing the APIs. This point is particularly relevant where data can be modified. There is a very wide spectrum of possible variants – from simple tokens (such as JSON web tokens, JWTs) through to complex OAuth authentication.