2016年9月20日 星期二

COMPLETE GUIDE FOR BUILDING RESTFUL APIS IN LARAVEL 5.2

If there is one single core feature that demonstrates Laravel’s superiority, it would be the ability to quickly and easily create a RESTful API. With the arrival of Laravel 5, several new features have been added; however, the ability to create application models and controllers via the Artisan command-line tool remains the most useful feature.
This feature is what initially encouraged me and so many others to abandon frameworks such as CodeIgniter, which at the time that Laravel 4 was in beta, did not natively have the same integrated functionality. Laravel provides the basic CRUD methods: create, read, update, delete, and also lists all.
Requests that arrive via HTTP to a Laravel URL are managed through their verbs and subsequently, the routes.php file, which is located at app/Http/routes.php. There are two ways in which the requests are handled. One way is that the request is handled directly via a closure, and the code is entirely inside the routes file. Another way is that it routes the request to a controller, where a method will be executed.
Also, the basic paradigm used is convention-over-configuration, where the method names are ready to handle the various requests, without too much extra effort.

RESTful APIs in Laravel

The list of RESTful API requests handled by the RESTful API are as follows:
HTTP VERBFunctionUR
1GETThis lists all accommodations/accommodations
2GETThis shows (reads) a single accommodation/accommodations/{id}
3POSTThis creates a new accommodation/accommodations
4PUTThis entirely modifies (updates) an accommodation/accommodations/{id}
5PATCHThis partially modifies (updates) an accommodation/accommodations/{id}
6DELETEThis deletes an accommodation/accommodations/{id}
Most RESTful API best practices suggest using the plural form of the model name. Laravel’s documentation uses the singular format. Most practices agree that consistent plural naming, that is,/accommodations/{id} refers to a single accommodation and /accommodations refers to more than one accommodation, both using the plural form are preferred over a mixed, but grammatically correct/accommodation/{id} (singular form) and /accommodations (plural form).


Essential CRUD

For simplicity, I have numbered each of the rows. The first and second items represent the read part of CRUD.
The first item, which is a GET call to the plural form of the model name, is rather simple; it displays all of the items. Sometimes, this is called a list to differentiate it from the read of a single record. Adding alist would thus expand the acronym to CRUDL. They could be paginated or require authorization.
The second item, also a GET call, adds the ID of the model to the end of the URL, displaying a single model with that corresponding ID. This could also require authentication but not paging.
The third item represents the create part of CRUD. It uses the POST verb to create a new model. Note that the URL format is the same as the first item; this demonstrates the importance of the verb to distinguish between the actions.
The fourth, fifth, and sixth items use the new HTTP verbs that were not supported by all browsers. Whether or not the verbs are supported, JavaScript libraries and frameworks, such as jQuery, will send the verb in a way that Laravel can properly handle.
The fourth item is the update part of CRUD and updates the model using the PUTverb. Note that it has the same URL format as the second, as it needs to know which model to update. It is also idempotent, which means that the entire model must be updated.
The fifth item is similar to the fourth item; it updates the model, but uses thePATCH verb. This is used to indicate that the model will be partially modified, which means that one or more of the model’s attributes have to be changed.
The sixth item deletes a single model and thus requires the model’s ID, using the self-explanatoryDELETE verb.

Bonus features

Laravel adds two additional methods that are not usually part of a standard RESTful API. A GETmethod on the model URL, adding create is used to display a form to create the model. A GETmethod on the model URL with its ID, addingedit is used to display a form to create the model. These two functions are useful for providing a URL that will load a form, even though this type of usage is not a standard RESTful:
HTTP VERBFunctionURL
GETThis displays an accommodation creation form/accommodations/create
GETThis displays an accommodation modification/update form/accommodations/{id}/edit

Controller creation

To create a controller for the accommodations, the following Artisan command is used:
$ php artisan make:controller AccommodationsController

    php namespace MyCompany\Http\Controllers;
    
    use MyCompany\Http\Requests;
    use MyCompany\Http\Controllers\Controller;
    use Illuminate\Http\Request;
    
    class AccommodationController extends Controller {
    
        /**
        * Display a listing of the resource.
        * @return Response
        */
        public function index()
        {
        }
        
        /**
        * Show the form for creating a new resource.
        * @return Response
        */
        public function create()
        {
        }
        
        /**
        * Store a newly created resource in storage.
        * @return Response
        */
        public function store()
        {
        }
    
        /**
        * Display the specified resource.
        * @param  int  $id
        * @return Response
        */
        public function show($id)
        {
        }
    
        /**
        * Show the form for editing the specified resource.
        * @param  int  $id
        * @return Response
        */
        public function edit($id)
        {
        }
    
        /**
        * Update the specified resource in storage.
        *
        * @param  int  $id
        * @return Response
        */
        public function update($id)
        {
        }
    
        /**
        * Remove the specified resource from storage.
        * @param  int  $id
        * @return Response
        */
        public function destroy($id)
        {
        }
    }

CRUD(L) by example

We have seen this controller before, but here are a few examples. The single most simple example of a RESTful call would be as shown in the following sections.

cRudl – read

Create a GET call to http://www.dunebookdemo.com/accommmodations/1, where 1would be the ID of the room:
/**
 * Display the specified resource.
 *
 * @param  int  $id
 * @return Response
 */
public function show($id)
{
    return \MyCompany\Accommodation::findOrFail($id);
}
This would return a single model as a JSON-encoded object:
{
    "id": 1,
    "name": "Hotel On The Hill","description":"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
    "location_id": 1,
    "created_at": "2015-02-08 20:13:10",
    "updated_at": "2015-02-08 20:13:10",
    "deleted_at": null
}

crudL – list

Create a GET call to http://www.dunebookdemo.com/accommmodations.
This is similar to the preceding code, yet slightly different:
/** Display a listing of the resource.
    * @return Response
 */
public function index()
{
    return Accommodation::all();
}
This would return all of the models, automatically encoded as JSON objects; there is nothing else that is required. Formatting has been added so that the JSON results are more easily readable, but basically, the entire model is returned:
[{ 
    "id": 1,
    "name": "Hotel On The Hill","description":"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
    "location_id": 1,
    "created_at": "2015-02-08 20:13:10",
    "updated_at": "2015-02-08 20:13:10",
    "deleted_at": null
} 
{   "id": 2,
    "name": "Patterson Place",
    "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
    "location_id": 2,
    "created_at": "2015-02-08 20:15:02",
    "updated_at": "2015-02-08 20:15:02",
    "deleted_at": null
},
{
    "id": 3,
    "name": "Neat and Tidy Hotel",
    "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
    "location_id": 3,
    "created_at": "2015-02-08 20:17:34",
    "updated_at": "2015-02-08 20:17:34",
    "deleted_at": null
}
]

Tip

The deleted_at field is a soft delete or the recycle bin mechanism. It is eithernull for not deleted or a date/time stamp for deleted.

Pagination

To add pagination, simply substitute all() with paginate():
public function index()
{
    return Accommodation::paginate();
}
The results will now look like this. The eloquent collection array is now moved inside a date attribute:
{"total":15,
"per_page":15,
"current_page":1,
"last_page":1,
"next_page_url":null,
"prev_page_url":null,
"from":1,
"to":15,
"data":[{"id":9,
"name":"Lovely Hotel",
"description":"Lovely Hotel Greater Pittsburgh",
….

Crudl – create

Create a POST call to .
To create a new model, a POST call will be sent to /accommodations. A JSON would be sent from the frontend as follows:
{
    "name": "Lovely Hotel",
    "description": "Lovely Hotel Greater Pittsburgh",
    "location_id":1
}
The store function might look something like this:
public function store()
{
    $input = \Input::json();
    $accommodation = new Accommodation;
    $accommodation->name = $input->get('name');
    $accommodation->description = $input->get('description');
    $accommodation->location_id = $input->get('location_id');
    $accommodation->save();
    return response($accommodation, 201)
;
}

Tip

201 is the HTTP status code (HTTP/1.1 201 created) for created.
In this example, we returned the model as a JSON-encoded object. The object will include the ID that was inserted:
{
    "name":"Lovely Hotel",
    "description":"Lovely Hotel Greater Pittsburgh",
    "location_id":1,
    "updated_at":"2015-03-13 20:48:19",
    "created_at":"2015-03-13 20:48:19",
    "id":26
}

crUdl – update

Create a PUT call to , where 1 is the ID to be updated:
/**
    * Update the specified resource in storage.
    *
    * @param  int  $id
    * @return Response
    */
    public function update($id)
    {
        $input = \Input::json();
        $accommodation = \MyCompany\Accommodation::findOrFail($id);
        $accommodation->name = $input->get('name');
        $accommodation->description = $input->get('description');
        $accommodation->location_id = $input->get('location_id');
        $accommodation->save();
        return response($accommodation, 200)
            ->header('Content-Type', 'application/json');
    }
To update an existing model, the code is exactly the same as we used earlier, except that the following line is used to find the existing model:
$accommodation = Accommodation::find($id);
The PUT verb would be sent to /accommodations/{id}, where id would be the numeric ID of the accommodations table.

cruDl – delete

To delete a model, create a DELETE call tohttp://www.hotelwebsite.com/accommmodation/1, where 1 is the ID to be deleted:
/**
 * Remove the specified resource from storage.
 *
 * @param  int  $id
 * @return Response
 */
public function destroy($id)
{
    $accommodation = Accommodation::find($id);
    $accommodation->delete();
    return response('Deleted.', 200)
;
}

Tip

There seems to be some disagreement about what the proper status code should be for a deleted model.


Moving beyond CRUD

If one of the requirements of the software application to be built is able to search for an accommodation, then we can easily add a search function. The search function will find accommodations by using a name string. One way to do this is to add a route to the routes.php file. This will map a GET call to search for a newsearch() function contained withinAccommodationsController:
Route::get('search', 'AccommodationsController@search');
Route::resource('accommodations', 'AccommodationsController');

Tip

In this case, the GET method would be preferred instead of the POST method, as it can be bookmarked and recalled later.
Now, we will write our search function:
public function search(Request $request, Accommodation $accommodation)
{
    return $accommodation
        ->where('name',
          'like',
          '%'.$request->get('name').'%')
        ->get();
    }
There are several mechanisms here:
  • The Request object that contains the variables from the GET request is type-hinted and then injected into the search function
  • The Accommodation model is type-hinted and then injected into the searchfunction
  • The where() method from the fluent query builder is called on the eloquent model$accommodation
  • The name parameter is used from the request object
  • The get() method is used to actually perform the SQL query

    Tip

    Note that some of the query builder and eloquent methods return an instance of the query builder, while the others execute the query and return the result. The where() method returns an instance of the query builder, while the get() method executes the query.
  • The resulting eloquent collection is returned and automatically encoded into JSON
The GET request, therefore, is as follows:
http://www.hotelwebsite.com/search-accommodation?name=Lovely
The resultant JSON would look something like this:
[{"id":3,
"name":"Lovely Hotel",
"description":"Lovely Hotel Greater Pittsburgh",
"location_id":1,
"created_at":"2015-03-13 22:00:23",
"updated_at":"2015-03-13 22:00:23",
"deleted_at":null},
{"id":4,
"name":"Lovely Hotel",
"description":"Lovely Hotel Greater Philadelphia",
"location_id":2,
"created_at":"2015-03-11 21:43:31",
"updated_at":"2015-03-11 21:43:31",
"deleted_at":null}]


Nested controllers

Nested controllers is a new feature in Laravel 5 and is used to handle all of the RESTful actions that deal with relationships. For example, we can take advantage of this feature for the relationship between accommodations and rooms.
The relationship between accommodation and room is as follows:
  • An accommodation may have one or more rooms (one-to-many)
  • A room belongs to one and only one accommodation (one-to-one)
In our models, we will now write the code to enable the one-to-one and one-to-many relationships to be skillfully handled by Laravel.

Accommodation hasMany rooms

First, we will add the code that is needed by the Accomodation.php file that represents theaccommodation model as follows:
class Accommodation extends Model {
    public function rooms(){
        return $this->hasMany('\MyCompany\Accommodation\Room');
    }
}
The rooms() method creates an easy way to access the relationship from inside the accommodation model. The relation states that “the accommodationhasMany rooms”. The hasMany function, when residing inside the Accommodationclass, without any additional parameters, expects a column namedaccommodation_id to exist in the Room model’s table, which in this case is rooms.

Room belongsTo accommodation

Now, we will add the code that is needed by the Room.php file that represents theRoom model:
class Room extends Model
{
    public function accommodation(){
        return $this->belongsTo('\MyCompany\Accommodation');
    }
}
This code states that “a room belongsTo an accommodation”. The belongsTomethod inside the Roomclass, without any additional parameters, expects a field in the room model’s table; in this case,rooms, named accommodation_id.

Tip

If the tables in the application database have followed the active record conventions, then most of the eloquent relation functionalities will automatically function. All of the parameters can be easily configured.
The command to create a nested controller is as follows:
$php artisan make:controller AccommodationsRoomsController
Then, the following line would be added to the app/Http/routes.php file:
Route::resource('accommodations.rooms', 'AccommodationsRoomsController');
To display the routes created, the following command should be executed:
$php artisan route:list
The following table lists the HTTP verbs and their functions:
HTTP verbFunctionURL
1GETThis shows the accommodation and room relations/accommodations/{accommodations}/rooms
2GETThis shows an accommodation and room relation/accommodations/{accommodations}/rooms/{rooms}
3POSTThis creates a new accommodation and room relation/accommodations/{accommodations}/rooms
4PUTThis entirely modifies (updates) an accommodation and room relation/accommodations/{accommodations}/rooms/{rooms}
5PATCHThis partially modifies (updates) an accommodation and room relation/accommodations/{accommodations}/rooms/{rooms}
6DELETEThis deletes an accommodation and room relation/accommodations/{accommodations}/rooms/{rooms}

Eloquent relations

A nice mechanism used to illustrate an Eloquent relation directly inside the controller is performed through the use of a nested relation, where two models are connected firstly through the route and secondly through their controller method’s parameters via model dependency injection.

Nested update

Let’s investigate the update/modify PUT nested controller command. The URL looks like this:http://www.hotelwebsite.com/accommodations/21/rooms/13.
Here, 21 would be the ID of the accommodation and 13 would be ID of the room. The parameters are the type-hinted models. This allows us to easily update the relationship as follows:
public function update(Accommodation $accommodation, Room $room)
{
    $room->accommodation()->associate($accommodation);
    $room->save();
}

Nested create

Similarly, it is easy to perform the nested create operation with a POST body tohttp://www.hotelwebsite.com/accommodations/21/rooms. The POST body is a JSON formatted object:
{"roomNumber":"123"}
Note that there is no ID needed for the room since we are creating it:
public function store(Accommodation $accommodation)
{
    $input = \Input::json();
    $room = new Room();
    $room->room_number = $input->get('roomNumber');
    $room->save();
    $accommodation->rooms()->save($room);
}



Route caching

Laravel 5 has a new mechanism for caching the routes as the routes.php file can easily grow very large and will quickly slow down the request process. To enable the caching mechanism, type the following artisan command:
$ php artisan route:cache
This creates another routes.php file in /storage/framework/routes.php. If this file exists, then it is used instead of the routes.php file, which is located inapp/Http/routes.php. The structure of the file is as follows:
php

/*
|--------------------------------------------------------------------------
| Load The Cached Routes
|
…
*/

app('router')->setRoutes(
unserialize(base64_decode('TzozNDoiSWxsdW1pbmF0ZVxSb3V0aW5nXFJvdXRlQ29sbGVjdGlvbiI6NDp7czo5OiIAKgByb3V0ZXMiO2E6Njp7czozOiJHRVQiO2E6M
…
... VyQGluZGV4IjtzOjk6Im5hbWVzcGFjZSI7czoyNjoiTXlDb21wYWbXBhbnlcSHR0cFxDb250cm9sbGVyc1xIb3RlbENvbnRyb2xsZXJAZGVzdHJveSI7cjo4Mzg7fX0='))
);
Notice that an interesting technique is used here. The routes are serialized, then base64 is encoded. Obviously, to read the routes, the reverse is used,base64_decode(), and then unserialize().
If the routes.php cached file exists, then every time a change is made to theroutes.php file, the route cache artisan command must be executed. This will clear the file and then recreate it. If you later decide to no longer use this mechanism, then the following artisan command can be used to eliminate the file:
$ php artisan route:clear
Laravel is useful for building several distinctly different types of applications. When building traditional web applications, there is often a tight integration between the controllers and the views. It is also useful when building an app that can be used on a smartphone. In this case, the frontend will be created for the smartphone’s operating system using another programming language and/or framework. In this case, only the controllers and model will most likely be used. In either case, however, having a well-documented RESTful API is an essential part of a well-designed modern software.
Nested controllers helps developers right away to read the code—it is an easy way to understand that the particular controller deals with the “nesting” or the concept that one class is related another.
Type-hinting the models and objects into the controller also improves the readability and, at the same time, reduces the amount of code necessary to perform the basic operations on the objects.
Also, eloquent model casting creates an easy way to transform the attributes of a model, without having to rely on external packages or tedious accessor functions, as was the case in Laravel 4.
Now it is rather clear to us why Laravel is becoming the choice of many developers. Learning and repeating some of the steps illustrated in this Turorial will allow a RESTful API to get created in under an hour for a small-to-medium size program.

Wrapping up

A RESTful API provides an easy way to expand the program in the future and also integrates with third-party programs and software that exist within a company that might need to communicate with the application. The RESTful API is the front-most shell of the inner part of the program and provides the bridge between the outside world and the application itself. The inner part of the program will be where all of the business logic and database connections will reside, so fundamentally, the controllers simply have the job of connecting the routes to the application.
Laravel follows the RESTful best practices, thus documenting the API should be easy enough for other developers and third-party integrators to understand. Laravel 5 has brought a few features in to the framework to enable the code to be more readable.
In future articles, middleware will be discussed. Middleware adds various “middle” layers between the route and the controller. Middleware can provide features such as authentication. Middleware will enrich, protect, and help organize the routes into logical and functional groups.
We will also discuss DocBlock annotations. Annotations, while not natively supported in PHP, can be enabled via a Laravel community package. Then, inside the DocBlock of the controller and controller functions, the routing for each controller is automatically created, without having to actually modify theapp/Http/routes.php file. This is another great community concept that Laravel easily adapts to, in the same manner as phpspec and Behat.

from : https://www.dunebook.com/complete-guide-for-building-restful-apis-in-laravel-5-2/

沒有留言:

wibiya widget