环境:homestead 5.3.2
版本:laravel 5.4
composer config -g repo.packagist composer https://packagist.laravel-china.org
composer create-project --prefer-dist laravel/laravel api 5.4.*
将 App\User
移动移动到 App\Models\User
目录
<?php
// App\Models\User.php
namespace App\Models;
<?php
// config\auth.php
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
],
创建数据库表
php artisan make:migration create_lessons_table --create=lessons
创建模型和控制器(这里将模型放到 Models 目录下)
php artisan make:controller LessonController -m Models\\Lesson
修改 migrate
<?php
// database\migrations\2017_07_01_182454_create_lessons_table.php
Schema::create('lessons', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('body');
$table->boolean('free');
$table->timestamps();
});
解决导入错误
<?php
// app\Providers\AppServiceProvider.php
use Illuminate\Support\Facades\Schema;
public function boot()
{
Schema::defaultStringLength(191);
}
生成数据表
php artisan migrate
http://d.laravel-china.org/docs/5.4/database-testing#writing-factories
修改 ModelFactory.php
<?php
// database\factories\ModelFactory.php
$factory->define(App\Models\Lesson::class, function (Faker\Generator $faker) {
static $password;
return [
'title' => $faker->sentence,
'body' => $faker->paragraph,
'free' => $faker->boolean(),
];
});
方法一:使用 tinker 生成数据
php artisan tinker
>>>factory(App\Models\Lesson::class,60)->create();
>>> quit
方法二:编写 Seeders 生成数据
php artisan make:seeder LessonsTableSeeder
<?php
// database\seeds\LessonsTableSeeder.php
public function run()
{
factory(App\Lesson::class, 50)->create();
}
php artisan db:seed --class=LessonsTableSeeder
php artisan migrate
php artisan tinker
>>>factory(App\Models\Lesson::class,60)->create();
>>> quit
<?php
// routes\api.php
Route::group(['prefix'=>'v1'], function (){
Route::resource('lesson','LessonController');
});
查看已注册的路由
# 注意这里不是 api:route
php artisan route:list
http://d.laravel-china.org/docs/5.4/controllers
http://d.laravel-china.org/docs/5.4/eloquent
<?php
// app\Http\Controllers\LessonController.php
public function index()
{
return Lesson::all();
}
public function show(Lesson $lesson)
{
// 旧版本
//return Lesson::findOrfail($id);
return $lesson;
}
访问:http://api.dev/api/v1/lesson 和 http://api.dev/api/v1/lesson/1
<?php
// app\Http\Controllers\LessonController.php
public function index()
{
return \Response::json([
'status'=>'Success',
'status_code'=>'200',
'data'=>Lesson::all()->toArray()
]);
}
public function show(Lesson $lesson)
{
return \Response::json([
'status'=>'Success',
'status_code'=>'200',
'data'=>$lesson->toArray()
]);
}
<?php
// app\Http\Controllers\LessonController.php
public function index()
{
$lessons = Lesson::all();
return \Response::json([
'status'=>'Success',
'status_code'=>'200',
'data'=>$this->transformCollection($lessons)
]);
}
public function show(Lesson $lesson)
{
return \Response::json([
'status'=>'Success',
'status_code'=>'200',
'data'=>$this->transform($lesson->toArray())
]);
}
public function transformCollection($lessons)
{
return array_map([$this, 'transform'],$lessons->toArray());
}
public function transform($lessons)
{
return [
'title'=>$lessons['title'],
'content'=>$lessons['body'],
'is_free'=>(boolean) $lessons['free']
];
}
<?php
// app\Transformer\LessonTransfromer.php
namespace App\Transformer;
class LessonTransfromer extends Transformer
{
public function transform($lessons)
{
return [
'title'=>$lessons['title'],
'content'=>$lessons['body'],
'is_free'=>(boolean) $lessons['free']
];
}
}
<?php
// app\Transformer\Transformer.php
namespace App\Transformer;
abstract class Transformer
{
public function transformCollection($items)
{
return array_map([$this, 'transform'],$items->toArray());
}
public abstract function transform($items);
}
<?php
// app\Http\Controllers\LessonController.php
use App\Transformer\LessonTransfromer;
class LessonController extends Controller
{
protected $lessonTransfromer;
// 依赖注入
public function __construct(LessonTransfromer $lessonTransfromer)
{
$this->lessonTransfromer = $lessonTransfromer;
}
public function index()
{
$lessons = Lesson::all();
return \Response::json([
'status'=>'Success',
'status_code'=>'200',
'data'=>$this->lessonTransfromer->transformCollection($lessons)
]);
}
public function show(Lesson $lesson)
{
return \Response::json([
'status'=>'Success',
'status_code'=>'200',
'data'=>$this->lessonTransfromer->transform($lesson->toArray())
]);
}
}
生成 ApiController
php artisan make:controller ApiController
<?php
// App\Http\Controllers\ApiController
class ApiController extends Controller
{
protected $statusCode = 200;
public function getStatusCode()
{
return $this->statusCode;
}
public function setStatusCode($statusCode)
{
$this->statusCode = $statusCode;
return $this;
}
public function responseNotFound($message = 'Not Found')
{
return $this->setStatusCode(404)->responseError($message);
}
public function responseError($message)
{
return $this->response([
'status'=>'failed',
'error'=>[
'status_code'=>$this->getStatusCode(),
'message'=>$message
]
]);
}
public function response($data)
{
return \Response::json($data);
}
}
<?php
// LessonController.php
class LessonController extends ApiController
{
public function index()
{
$lessons = Lesson::all();
return $this->response([
'status'=>'Success',
'status_code'=>'200',
'data'=>$this->lessonTransfromer->transformCollection($lessons)
]);
}
public function show(Lesson $lesson)
{
return $this->response([
'status'=>'Success',
'status_code'=>'200',
'data'=>$this->lessonTransfromer->transform($lesson->toArray())
]);
}
}
http://d.laravel-china.org/docs/5.4/errors
<?php
// app\Exceptions\Handler.php
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
public function render($request, Exception $exception)
{
if ($exception instanceof ModelNotFoundException) {
$api = new \App\Http\Controllers\ApiController;
return $api->responseNotFound('404 not found');
}
return parent::render($request, $exception);
}
生成用户验证模块
php artisan make:auth
<?php
// app\Http\Controllers\Auth\RegisterController.php
use App\Models\User;
编辑控制器
<?php
// app\Http\Controllers\LessonController.php
public function __construct(LessonTransfromer $lessonTransfromer)
{
$this->middleware('auth.basic',['only'=>['index', 'store']]);
}
public function store(Request $request)
{
if (! $request->get('title') or ! $request->get('body') or ! $request->get('free')){
return $this->setStatusCode(422)->response('validata fails');
}
Lesson::create($request->all());
return $this->setStatusCode(201)->response([
'status'=>'success',
'message'=>'lesson created'
]);
}
<?php
// app\Models\Lesson.php
class Lesson extends Model
{
protected $fillable = ['title','body','free'];
}
最后使用 postman 调试 http://api.dev/api/v1/lesson/
官方: https://github.com/dingo/api
中文: https://github.com/liyu001989/dingo-api-wiki-zh
<?php
// api\composer.json
"require": {
"dingo/api": "1.0.*@dev"
}
composer update
注册 provider
# config\app.php
'providers' => [
Dingo\Api\Provider\LaravelServiceProvider::class,
]
添加 Facades
# config\app.php
'aliases' => [
'API' => Dingo\Api\Facade\API::class,
'DingoRoute' => Dingo\Api\Facade\Route::class,
]
发布配置文件
php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"
主页 https://github.com/tymondesigns/jwt-auth/
安装文档 https://github.com/tymondesigns/jwt-auth/wiki/Installation
<?php
// api\composer.json
"require": {
"tymon/jwt-auth": "0.5.*"
}
composer update
注册 provider
# config\app.php
'providers' => [
Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,
]
添加 Facades
# config\app.php
'aliases' => [
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
]
发布配置文件
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
生成key
php artisan jwt:generate
https://github.com/liyu001989/dingo-api-wiki-zh/blob/master/Configuration.md
# api.dev\.env
API_STANDARDS_TREE=vnd
API_PREFIX=api
API_VERSION=v1
API_DEBUG=true
https://github.com/liyu001989/dingo-api-wiki-zh/blob/master/Authentication.md
# config\api.php
// 仅供参考
'auth' => [
'basic' => function($app) {
return new Dingo\Api\Auth\Provider\Basic($app['auth']);
},
'jwt' => function ($app){
return new Dingo\Api\Auth\Provider\JWT($app['Tymon\JWTAuth\JWTAuth']);
},
],
'auth' => [
'basic' => 'Dingo\Api\Auth\Provider\Basic',
'jwt' => 'Dingo\Api\Auth\Provider\JWT',
],
添加路由中间件
# app\Http\Kernel.php
protected $routeMiddleware = [
'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,
'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class,
];
这里的路由就是 Dingo API Endpoints
,端点是路由的另一种说法,详细用法见下面文档。
请注意:
v1
不能乱用,它已经在你的环境配置中定义好了。API_VERSION=v1
<?php
$api = app('Dingo\Api\Routing\Router');
$api->version('v1', function ($api) {
$api->group(['namespace' => 'App\Api\Controllers'], function ($api) {
$api->get('lessons', 'LessonController@index');
});
});
查看路由,注意区别 route:lists
php artisan api:route
地址是 /api/lessons
没有 v1
前缀,访问 http://api.dev/api/lessons/
看到已经正常。
下面例子演示如何使用响应生成器 (Response Builder)
为了使用 Dingo\Api\Routing\Helpers trait
首先要创建基础控制器
<?php
// app\Api\Controllers\BaseController.php
namespace App\Api\Controllers;
use App\Http\Controllers\Controller;
use Dingo\Api\Routing\Helpers;
class BaseController extends Controller
{
use Helpers;
}
意思是直接以数组形式返回 $lessons 原始数据
创建 LessonController 继承上面的 BaseController
<?php
// app\Api\Controllers\LessonController.php
namespace App\Api\Controllers;
use App\Models\Lesson;
class LessonController extends BaseController
{
public function index()
{
$lessons = Lesson::all();
return $this->response->array($lessons->toArray());
}
}
通过实例化 LessonTransformer 来处理 $lesson 数据,然后再返回经过处理的数据。
首先,新建 LessonController
类
<?php
// app\Api\Controllers\LessonController.php
namespace App\Api\Controllers;
use App\Api\Transformer\LessonTransformer;
use App\Models\Lesson;
class LessonController extends BaseController
{
/**
* 通过实例化 LessonTransformer 来处理 $lesson 数据,然后再返回
* LessonTransformer 类必须继承 TransformerAbstract 抽象类
* 因为 TransformerAbstract 类提供了 item 和 collection 方法
* 此外 LessonTransformer 类中必须有 transform 方法
*/
public function show($id)
{
$lesson = Lesson::findOrFail($id);
return $this->response->item($lesson, new LessonTransformer);
}
}
接着,新建 LessonTransformer
,命名空间是 App\Api\Transformer
<?php
// app\Api\Transformer\LessonTransformer.php
namespace App\Api\Transformer;
use App\Models\Lesson;
use League\Fractal\TransformerAbstract;
class LessonTransformer extends TransformerAbstract
{
public function transform(Lesson $lesson)
{
return [
'title'=>$lesson['title'],
'content'=>$lesson['body'],
'is_free'=>(boolean) $lesson['free']
];
}
}
collection
会自动调用 new LessonTransformer()
中的 transform
方法
意思就是上面 LessonTransformer
类中必须有 transform
方法
<?php
// app\Api\Controllers\LessonController.php
namespace App\Api\Controllers;
use App\Api\Transformer\LessonTransformer;
use App\Models\Lesson;
class LessonController extends BaseController
{
public function index()
{
$lessons = Lesson::all();
return $this->collection($lessons,new LessonTransformer());
}
}
<?php
// app\Api\Controllers\LessonController.php
namespace App\Api\Controllers;
use App\Api\Transformer\LessonTransformer;
use App\Lesson;
class LessonController extends BaseController
{
public function index()
{
$lessons = Lesson::paginate(10);
return $this->paginator($lessons, new LessonTransformer);
}
}
<?php
// routes\api.php
$api = app('Dingo\Api\Routing\Router');
$api->version('v2', function ($api) {
$api->group(['namespace' => 'App\Api\Controllers'], function ($api) {
$api->post('user/login', 'AuthController@authenticate');
$api->post('user/register', 'AuthController@register');
$api->group(['middleware' => 'jwt.auth'], function ($api) {
$api->get('lessons', 'LessonController@index');
$api->get('lesson/{id}', 'LessonController@show');
});
});
});
定义 user 模型位置
<?php
// api\config\jwt.php
'user' => 'App\Models\User',
创建 AuthController,注意这里 use 了 Request
<?php
// app\Api\Controllers\AuthController.php
namespace App\Api\Controllers;
use App\Models\User;
// 注意这里 use 了 Request
use Illuminate\Http\Request;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
class AuthController extends BaseController
{
public function authenticate(Request $request)
{
// 返回请求中 email 和 password 的值
$credentials = $request->only('email', 'password');
try {
// attempt to verify the credentials and create a token for the user
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(['error' => 'could_not_create_token'], 500);
}
// 登录成功后,以 json 形式返回 token 值
return response()->json(compact('token'));
}
public function register(Request $request)
{
$newUser = [
'email' => $request->get('email'),
'name' => $request->get('name'),
'password' => $request->get('password'),
];
$user = User::create($newUser);
$token = JWTAuth::fromUser($user);
// 注册成功后返回 token
return response()->json(compact('token'));
}
}
登录调试:post 请求,body 携带 emial 和 password 参数
注册调试:post 请求,body 携带 emial、name 和 password 参数
先登录获取 token ,然后显示内容
http://api.dev/api/lesson/1?token=xxx http://api.dev/api/lessons?token=xxx
路由
<?php
// routes\api.php
$api->group(['middleware' => 'jwt.refresh'], function ($api) {
$api->get('user/info', 'AuthController@getAuthenticatedUser');
});
AuthController
<?php
//app\Api\Controllers\AuthController.php
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
public function getAuthenticatedUser()
{
try {
if (! $user = JWTAuth::parseToken()->authenticate()) {
return response()->json(['user_not_found'], 404);
}
} catch (TokenExpiredException $e) {
return response()->json(['token_expired'], $e->getStatusCode());
} catch (TokenInvalidException $e) {
return response()->json(['token_invalid'], $e->getStatusCode());
} catch (JWTException $e) {
return response()->json(['token_absent'], $e->getStatusCode());
}
return response()->json(compact('user'));
}
调试:http://api.dev/api/user/info?token=xx
注意 password 没有变
<?php
//app\Api\Controllers\AuthController.php
$credentials = [
'user_email' => $request->get('user_email'),
'password' => $request->get('user_password'),
];
增加 getAuthPassword
方法
<?php
// api\app\Models\User.php
public function getAuthPassword()
{
return $this->user_password;
}