在本教程中,我们将使用Laravel和新的Eloquent API资源功能建立一个完整的json crud api。当你想用Laravel构建整个应用程序的后端,而把前端留给Vue,React或Angular时,就可以采用这种方法。通过使用Laravel和Eloquent API资源,你可以随意格式化你的数据响应,同时还可以添加元信息,分页链接和信息,自定义数据包装,条件属性,以及关系。
本地开发环境
在这里,我们只是建立了一个新的项目,名为 lpgvue来开始。为什么叫这个名字?它是Laravel Play Ground / Vue的缩写。从本质上讲,我们只是想有一个本地的开发环境,让我们建立起最新和最伟大的Lavavel版本,这样我们就可以随心所欲地构建和修补。
composer global require laravel/installer
composer create-project -prefer-dist laravel/laravel lpgvue
composer require -dev barryvdh/laravel-ide-helper
php artisan id-helper:generate
通过上面的命令, 我们现在有了一个漂亮的Laravel项目可以使用.
创建数据来工作
Laravel可以很容易地建立表格来保存样本数据.除了Migrations, 你还可以设置数据库播种,以快速填充这些表的数据,以便你可以测试你的API。
添加一个数据库
首先,我们使用一个图形用户界面工具在我们的服务器上添加一个数据库。查看Laravel的数据库设置,了解更多信息。

当然,我们使用的是Homestead,这是一个用于本地开发环境的主要的Vagrant Box。这意味着我们的用户名和密码分别是homestead和secret。不过我们需要在env文件中指定我们刚刚创建的数据库。

添加迁移
我们可以使用Artisan生成一个迁移,为post表提供一个模式,用来存放我们的帖子。
php artisan make:migration create_posts_table -create=posts
在下面突出显示的几行中, 我们添加了以下字段 title和 body
database/migrations/xxxx_xx_xx_xxxxxx_create_posts_table.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePostsTable extends Migration
{
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title');
$table->text('body');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('posts');
}
}
数据库播种
为了启用数据库播种,我们可以再次使用Artisan为我们制作一个播种器类。
php artisan make:searer PostsTableSeeder
我们在下面添加的代码指定要在数据库中制作25个样本帖子。
database/seeds/PostsTableSeeder.php
<?php
use Illuminate\Database\Seeder;
class PostsTableSeeder extends Seeder
{
public function run()
{
factory(App\Post::class, 25)->create();
}
}
我们还需要更新DatabaseSeeder.php文件,以便现在使用PostTableSeeder类。
database/seeds/DatabaseSeeder.php
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run()
{
$this->call(PostsTableSeeder::class);
}
}
php artisan make:factory PostFactory
最后,我们需要一个工厂,允许我们指定填充到数据库中的数据的形状。我们希望标题使用40个字符,正文使用200个字符。要设置这样的东西,只需使用这些步骤。
database/factories/PostFactory.php
<?php
/* @var $factory \Illuminate\Database\Eloquent\Factory */
use App\Post;
use Faker\Generator as Faker;
$factory->define(Post::class, function (Faker $faker) {
return [
'title' => $faker->text(40),
'body' => $faker->text(200)
];
});
创建一个数据模型
该应用程序还需要一个帖子表的模型。我们可以一次完成模型和迁移,但这种方法也可以。
php artisan make:model Post
就这样,所有的数据设置都完成了。现在,我们要迁移数据库,然后对数据库进行播种。我们开始吧!
php artisan migrate
php artisan db:seed
你应该看到关于迁移成功和数据库播种成功的信息。现在,当使用GUI工具查看数据库时,所有的样本数据都已经填充完毕。

构建控制器和路由
现在,我们开始构建实际的代码和文件,为REST API提供动力。首先,我们需要一个基于资源的Post控制器。Artisan让这一切变得简单。
创建一个控制器
php artisan make:controller PostController -resource
最终的文件如下,其中删除了注释和一些方法。在我们的小教程中,我们只使用index(), store(), show(), and destroy()。
app/Http/Controllers/PostController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index()
{
//
}
public function store(Request $request)
{
//
}
public function show($id)
{
//
}
public function destroy($id)
{
//
}
}
配置路由文件
Laravel为你提供了两个路由的入口.一个是标准的web请求, 另一个是为api请求定制的.我们在这里建立一个API,所以我们需要在api.php文件中填充我们的路由。代码如下,并附有注释,解释每个路由的目的。
routes/api.php
<?php
use Illuminate\Http\Request;
// List all posts
Route::get('posts', 'PostController@index');
// List a single post
Route::get('post/{id}', 'PostController@show');
// Create a new post
Route::post('post', 'PostController@store');
// Update a post
Route::put('post', 'PostController@store');
// Delete a post
Route::delete('post/{id}', 'PostController@destroy');
请注意,有两条路由指向同一个store()方法。这是因为store()方法可以在使用PUT http动词时更新一个帖子,或者在使用POST http动词时创建一个新帖子。很酷的东西。
创建一个API资源
现在我们可以开始在Laravel中使用API资源.文档中把这个功能描述为一种转换层,位于数据模型层和JSON响应层之间。我们可以继续创建一个新的API资源,像这样。
php artisan make:resource Post
上面的命令创建了一个新的资源文件夹,并填充了以下文件。
app/Http/Resources/Post.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Post extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}
这个文件是你的转化器,它允许你以多种方式定制你的api的JSON响应数据。我们现在就可以使用它了。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Post;
use App\Http\Resources\Post as PostResource;
class PostController extends Controller
{
public function index()
{
// Get the posts
$posts = Post::paginate(5);
// Return collection of posts as a resource
return PostResource::collection($posts);
}
public function store(Request $request)
{
//
}
public function show($id)
{
//
}
public function destroy($id)
{
//
}
}
现在我们可以访问 http://lpgvue.io/api/posts浏览器,并看到格式良好的JSON输出。数据被很好地包含在数据属性中,同时还有分页和元信息的链接。非常酷
{
"data": [
{
"id": 1,
"title": "Sunt quis iure est deserunt ut.",
"body": "Eos eum accusantium totam maiores ipsum autem recusandae. Dolores in qui officiis culpa eligendi quas. Cupiditate earum perferendis qui quia.",
"created_at": "2019-05-13 15:43:06",
"updated_at": "2019-05-13 15:43:06"
},
{
"id": 2,
"title": "Enim ut cupiditate dolorum quos enim.",
"body": "Mollitia sequi tempora cupiditate quia. Mollitia aliquid nesciunt provident. Nam reiciendis delectus a est repellendus omnis ut occaecati.",
"created_at": "2019-05-13 15:43:06",
"updated_at": "2019-05-13 15:43:06"
},
{
"id": 3,
"title": "Nihil libero nam quisquam optio.",
"body": "Aut vitae ut deserunt beatae magnam eaque. Eaque quam rerum dolores ea tempore sint ipsa. Velit mollitia sapiente a. Et ratione quam soluta odit ullam fugit dolores voluptatum.",
"created_at": "2019-05-13 15:43:06",
"updated_at": "2019-05-13 15:43:06"
},
{
"id": 4,
"title": "Quis esse a sit est.",
"body": "Corporis tempore autem optio odit atque sed. Cupiditate adipisci recusandae doloribus quas. Tempora qui sequi error rerum aperiam voluptas.",
"created_at": "2019-05-13 15:43:06",
"updated_at": "2019-05-13 15:43:06"
},
{
"id": 5,
"title": "Nam velit doloribus cumque qui cumque.",
"body": "Voluptates culpa quidem omnis soluta animi eaque. Nihil voluptates voluptas quis cum adipisci tenetur. Eveniet voluptatem quo velit ea occaecati ducimus sed.",
"created_at": "2019-05-13 15:43:06",
"updated_at": "2019-05-13 15:43:06"
}
],
"links": {
"first": "http://lpgvue.io/api/posts?page=1",
"last": "http://lpgvue.io/api/posts?page=5",
"prev": null,
"next": "http://lpgvue.io/api/posts?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 5,
"path": "http://lpgvue.io/api/posts",
"per_page": 5,
"to": 5,
"total": 25
}
}
定制返回的数据
我们上面创建的生成的资源可以被定制,这样你就可以从API中准确地返回你喜欢的东西。通过像下面这样修改返回语句,现在只发送id、title和body,这使事情变得简单了。
app/Http/Resources/Post.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Post extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body
];
}
}
下面是访问时的输出结果 http://lpgvue.io/api/posts
{
"data": [
{
"id": 1,
"title": "Sunt quis iure est deserunt ut.",
"body": "Eos eum accusantium totam maiores ipsum autem recusandae. Dolores in qui officiis culpa eligendi quas. Cupiditate earum perferendis qui quia."
},
{
"id": 2,
"title": "Enim ut cupiditate dolorum quos enim.",
"body": "Mollitia sequi tempora cupiditate quia. Mollitia aliquid nesciunt provident. Nam reiciendis delectus a est repellendus omnis ut occaecati."
},
{
"id": 3,
"title": "Nihil libero nam quisquam optio.",
"body": "Aut vitae ut deserunt beatae magnam eaque. Eaque quam rerum dolores ea tempore sint ipsa. Velit mollitia sapiente a. Et ratione quam soluta odit ullam fugit dolores voluptatum."
},
{
"id": 4,
"title": "Quis esse a sit est.",
"body": "Corporis tempore autem optio odit atque sed. Cupiditate adipisci recusandae doloribus quas. Tempora qui sequi error rerum aperiam voluptas."
},
{
"id": 5,
"title": "Nam velit doloribus cumque qui cumque.",
"body": "Voluptates culpa quidem omnis soluta animi eaque. Nihil voluptates voluptas quis cum adipisci tenetur. Eveniet voluptatem quo velit ea occaecati ducimus sed."
}
],
"links": {
"first": "http://lpgvue.io/api/posts?page=1",
"last": "http://lpgvue.io/api/posts?page=5",
"prev": null,
"next": "http://lpgvue.io/api/posts?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 5,
"path": "http://lpgvue.io/api/posts",
"per_page": 5,
"to": 5,
"total": 25
}
}
数据库字段到API响应的映射
Laravel的API资源的这个功能真的很不错。你的数据库字段不必与JSON响应中使用的键完全匹配。当然, 为了保证每个人的理智, 最好的办法是让他们这样做.然而,如果你需要在响应数据中使用自定义属性,你可以这么做。让我们看看这是如何工作的。
app/Http/Resources/Post.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Post extends JsonResource
{
public function toArray($request)
{
return [
'the_custom_id' => $this->id,
'the_custom_title' => $this->title,
'the_custom_body' => $this->body
];
}
}
来自API的JSON响应现在使用这些自定义属性。
{
"data": [
{
"the_custom_id": 1,
"the_custom_title": "This Post Has Been Updated!",
"the_custom_body": "Some fresh new updates for you."
},
{
"the_custom_id": 2,
"the_custom_title": "Enim ut cupiditate dolorum quos enim.",
"the_custom_body": "Mollitia sequi tempora cupiditate quia. Mollitia aliquid nesciunt provident. Nam reiciendis delectus a est repellendus omnis ut occaecati."
},
{
"the_custom_id": 3,
"the_custom_title": "Nihil libero nam quisquam optio.",
"the_custom_body": "Aut vitae ut deserunt beatae magnam eaque. Eaque quam rerum dolores ea tempore sint ipsa. Velit mollitia sapiente a. Et ratione quam soluta odit ullam fugit dolores voluptatum."
},
{
"the_custom_id": 4,
"the_custom_title": "Quis esse a sit est.",
"the_custom_body": "Corporis tempore autem optio odit atque sed. Cupiditate adipisci recusandae doloribus quas. Tempora qui sequi error rerum aperiam voluptas."
},
{
"the_custom_id": 6,
"the_custom_title": "Eum soluta qui quas.",
"the_custom_body": "Omnis dolor quos unde dolor. Quam ex non voluptates qui harum cumque soluta. Natus rerum deleniti eveniet ad."
}
],
"links": {
"first": "http://lpgvue.io/api/posts?page=1",
"last": "http://lpgvue.io/api/posts?page=5",
"prev": null,
"next": "http://lpgvue.io/api/posts?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 5,
"path": "http://lpgvue.io/api/posts",
"per_page": 5,
"to": 5,
"total": 25
}
}
返回一个单一的资源
现在让我们看看在控制器的show()方法中返回一个单一资源。
app/Http/Controllers/PostController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Post;
use App\Http\Resources\Post as PostResource;
class PostController extends Controller
{
public function index()
{
// Get the posts
$posts = Post::paginate(5);
// Return collection of posts as a resource
return PostResource::collection($posts);
}
public function store(Request $request)
{
//
}
public function show($id)
{
// Get a single post
$post = Post::findOrFail($id);
// Return a single post as a resource
return new PostResource($post);
}
public function destroy($id)
{
//
}
}
这是访问时的输出结果 http://lpgvue.io/api/post/5
{
"data": {
"id": 5,
"title": "Nam velit doloribus cumque qui cumque.",
"body": "Voluptates culpa quidem omnis soluta animi eaque. Nihil voluptates voluptas quis cum adipisci tenetur. Eveniet voluptatem quo velit ea occaecati ducimus sed."
}
}
添加额外的数据
要在发送的响应中添加一些额外的数据,比如api版本什么的,你可以像我们这里一样设置with()方法。
app/Http/Resources/Post.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Post extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body
];
}
public function with($request)
{
return [
'version' => '1.0.0',
'api_url' => url('http://lpgvue/api')
];
}
}
当访问时 http://lpgvue.io/api/post/5的时候,我们现在看到响应中的额外元数据。所以我们可以看到这是一个非常强大的方法,可以准确地定制你希望API产生的东西。
{
"data": {
"id": 5,
"title": "Nam velit doloribus cumque qui cumque.",
"body": "Voluptates culpa quidem omnis soluta animi eaque. Nihil voluptates voluptas quis cum adipisci tenetur. Eveniet voluptatem quo velit ea occaecati ducimus sed."
},
"version": "1.0.0",
"api_url": "http://lpgvue/api"
}
创建和更新
同样的端点可以用来更新一个帖子,或者完全创建一个新的帖子。唯一的区别是提出请求时使用的HTTP动词。如果使用的是PUT请求,那么就会认为将更新一个帖子。如果使用的是POST请求,那就意味着要创建一个新的帖子。这里的代码允许使用PostController中相同的store()方法进行帖子更新或帖子创建。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Post;
use App\Http\Resources\Post as PostResource;
class PostController extends Controller
{
public function index()
{
// Get the posts
$posts = Post::paginate(5);
// Return collection of posts as a resource
return PostResource::collection($posts);
}
public function store(Request $request)
{
// Allow for post update *or* create a new post
$post = $request->isMethod('put') ? Post::findOrFail($request->id) : new Post;
$post->id = $request->input('id');
$post->title = $request->input('title');
$post->body = $request->input('body');
if ($post->save()) {
return new PostResource($post);
}
}
public function show($id)
{
// Get a single post
$post = Post::findOrFail($id);
// Return a single post as a resource
return new PostResource($post);
}
public function destroy($id)
{
//
}
}
测试一个帖子的更新
当你还没有建立一个正式的前端时,为了测试一个API,你可以使用像Postman这样的伟大工具。使用Postman测试允许我们使用我们选择的HTTP动词向应用程序发送API请求。这里我们将设置一个PUT请求到 http://lpgvue.io/api/post endpoint.这将使我们能够更新一个帖子。第一步是将请求类型设置为PUT,并在请求中添加一个头,指定内容类型为application/json。

第二步是点击Body标签,选择raw,并在请求中加入json有效载荷。为了更新一个帖子,我们需要指定要更新的数据的ID、标题和正文。

最后一步是点击发送按钮,如果一切顺利,你将看到一个响应,在有效载荷中包含更新的帖子。

测试一个帖子的创建
现在我们已经熟悉了Postman的基本工作流程,我们也应该能够轻松地创建一个新的帖子。要做到这一点,我们可以设置一个POST请求到相同的端点,即 **http://lpgvue.io/api/post**这个端点,我们刚刚在更新的例子中使用过。注意,我们设置了json的有效载荷,以包括一个新的帖子的标题和正文。我们不需要包括一个ID,因为应用程序会自动分配一个新的ID。

当你点击发送时,Postman将发出POST请求,你应该得到一个201创建的响应,在响应的正文中包含新创建的帖子。

直接查看我们数据库中的帖子表也会显示新创建的帖子。

删除一个资源
为了完善我们的API并允许通过JSON Rest Api实现完整的CRUD功能,我们可以增加删除帖子的功能。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Post;
use App\Http\Resources\Post as PostResource;
class PostController extends Controller
{
public function index()
{
// Get the posts
$posts = Post::paginate(5);
// Return collection of posts as a resource
return PostResource::collection($posts);
}
public function store(Request $request)
{
// Allow for post update *or* create a new post
$post = $request->isMethod('put') ? Post::findOrFail($request->id) : new Post;
$post->id = $request->input('id');
$post->title = $request->input('title');
$post->body = $request->input('body');
if ($post->save()) {
return new PostResource($post);
}
}
public function show($id)
{
// Get a single post
$post = Post::findOrFail($id);
// Return a single post as a resource
return new PostResource($post);
}
public function destroy($id)
{
// Get the post
$post = Post::findOrFail($id);
// Delete the post, return as confirmation
if ($post->delete()) {
return new PostResource($post);
}
}
}
现在,让我们试着删除id为5的帖子。删除请求是非常简单的。你所要做的就是提供要点击的端点,包括ID,然后点击发送。确保HTTP动词类型被设置为DELETE。

现在,当我们访问 **http://lpgvue.io/api/posts**查看所有的帖子时,注意到它们被依次列为1、2、3、4和6。所以,这意味着我们对帖子ID 5的删除起了作用。
{
"data": [
{
"id": 1,
"title": "This Post Has Been Updated!",
"body": "Some fresh new updates for you."
},
{
"id": 2,
"title": "Enim ut cupiditate dolorum quos enim.",
"body": "Mollitia sequi tempora cupiditate quia. Mollitia aliquid nesciunt provident. Nam reiciendis delectus a est repellendus omnis ut occaecati."
},
{
"id": 3,
"title": "Nihil libero nam quisquam optio.",
"body": "Aut vitae ut deserunt beatae magnam eaque. Eaque quam rerum dolores ea tempore sint ipsa. Velit mollitia sapiente a. Et ratione quam soluta odit ullam fugit dolores voluptatum."
},
{
"id": 4,
"title": "Quis esse a sit est.",
"body": "Corporis tempore autem optio odit atque sed. Cupiditate adipisci recusandae doloribus quas. Tempora qui sequi error rerum aperiam voluptas."
},
{
"id": 6,
"title": "Eum soluta qui quas.",
"body": "Omnis dolor quos unde dolor. Quam ex non voluptates qui harum cumque soluta. Natus rerum deleniti eveniet ad."
}
],
"links": {
"first": "http://lpgvue.io/api/posts?page=1",
"last": "http://lpgvue.io/api/posts?page=5",
"prev": null,
"next": "http://lpgvue.io/api/posts?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 5,
"path": "http://lpgvue.io/api/posts",
"per_page": 5,
"to": 5,
"total": 25
}
}
Laravel API资源教程摘要
在本教程中,我们使用Laravel API资源功能建立了一个完整的JSON CRUD API。我们可以看到,使用Laravel和它提供的许多工具来建立一个后端API真的很容易。接下来,我们将看看如何建立一个Vue前端,可以消费我们的API。