Laravel 开发 API 心得

Laravel框架
497
0
0
2022-04-11

开发环境

Docker - PHP7.4 + MySQL5.7 + Nginx1.19

IDE:PhpStorm

项目搭建

一、安装 Laravel

$ composer create-project --prefer-dist laravel/laravel:^6.2 test

二、Laravel - User 相关文件处理

1) 删除以下文件

app/Http/Controllers/Auth 目录

database/factories/UserFactory.php

database/migrations/2014_10_12_000000_create_users_table.php

database/migrations/2014_10_12_100000_create_password_resets_table.php

resources/views/welcome.blade.php

2) 移动 app/User.php 到 app/Models/User/User.php

三、配置

1) .env - 配置数据库

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=123456

2) config/app.php - 配置时区

'timezone' => 'Asia/Shanghai'

四、中间件 AcceptHeader

Accept 决定了响应返回的格式,设置为 application/json, 遇到的所有报错 Laravel 会默认处理为 JSON 格式

1) 新建 app/Http/Middleware/AcceptHeader.php

<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class AcceptHeader
{
  /**
   * Handle an incoming request.
   *
   * @param Request $request
   * @param Closure $next
   *
   * @return mixed
   */
  public function handle($request, Closure $next)
  {
    $request->headers->set('Accept', 'application/json');
    return $next($request);
  }
}

2) app/Http/Kernel.php

...

protected $middlewareGroups = [
  ...
  'api' => [
    \App\Http\Middleware\AcceptHeader::class,
    ...
  ],
];

...

五、创建 users 表

1) 创建迁移

php artisan make:migration create_users_table --create=users

2) 编辑 database/migrations/2021_04_29_070350_create_users_table.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;

class CreateUsersTable extends Migration
{
  private const TABLE = 'users';

  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create(self::TABLE, function (Blueprint $table) {
      $table->integerIncrements('id');
      $table->string('account', 32)->comment('账号');
      $table->string('password')->comment('密码');
      $table->string('register_address')->comment('注册地址');
      $table->string('last_location')->default('')->comment('最后登录位置');
      $table->unsignedInteger('last_ip')->comment('最后登录IP');
      $table->timestamps();
    });
    DB::statement('ALTER TABLE ' . self::TABLE . ' COMMENT "用户表"');
  }

  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::dropIfExists(self::TABLE);
  }
}

3) 执行迁移

$ php artisan migrate

六、安装扩展包

1) laravel-ide-helper - IDE 智能提示插件

$ composer require "barryvdh/laravel-ide-helper:2.8.*" --dev

$ php artisan ide-helper:generate

$ php artisan ide-helper:models

2) laravel-telescope - 调试工具

$ composer require "laravel/telescope:^3.0" --dev

$ php artisan telescope:install

$ php artisan migrate --path=./vendor/laravel/telescope/src/Storage/migrations/

# 一般也不需要配置 `laravel-telescope`, 删除以下文件

config/telescope.php

config/app.php -> TelescopeServiceProvider 服务注册

app/Providers/TelescopeServiceProvider.php

七、取消扩展包自动发现,并在 AppServiceProvider 注册

1) composer.json

...

"extra": {

  "laravel": {

    "dont-discover": [

      "barryvdh/laravel-ide-helper",

      "laravel/telescope"

    ]

  }

}

...

2) app/Providers/AppServiceProvider.php - 仅本地环境注册服务

...

public function register()
{
  if ($this->app->isLocal()) {
    $this->app->register('Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider');
    $this->app->register('Laravel\Telescope\TelescopeServiceProvider');
  }
}

...

目录结构

app 目录

Console - 自定义的 Artisan 命令目录

Enums - 枚举目录

Exceptions - 异常处理目录

Http - 包含控制器、中间件以及表单请求等目录

Jobs - 队列任务目录

Libraries - 第三方包目录 (包含扩展包封装类)

Listeners - 事件监听目录

ModelFilters - 模型过滤器目录

Models - 模型目录

Observers - 模型观察者目录

Providers - 服务提供者目录

Services - 业务服务类目录

route 目录

api - 接口目录

路由加载

1) 删除 routes/api.php & routes/web.php 路由文件

2) 编辑 app/Providers/RouteServiceProvider.php, 加载 routes/api 目录下的路由文件

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
  /**
   * This namespace is applied to your controller routes.
   *
   * In addition, it is set as the URL generator's root namespace.
   *
   * @var string
   */
  protected $namespace = 'App\Http\Controllers\V1';

  /**
   * The path to the "home" route for your application.
   *
   * @var string
   */
  public const HOME = '/';

  /**
   * Define your route model bindings, pattern filters, etc.
   *
   * [@return](https://learnku.com/users/31554) void
   */
  public function boot()
  {
    parent::boot();
  }

  /**
   * Define the routes for the application.
   *
   * [@return](https://learnku.com/users/31554) void
   */
  public function map()
  {
    $this->mapApiRoutes();
  }

  /**
   * Define the "api" routes for the application.
   *
   * These routes are typically stateless.
   *
   * [@return](https://learnku.com/users/31554) void
   */
  protected function mapApiRoutes()
  {
    Route::prefix('v1')
      ->middleware('api')
      ->namespace($this->namespace)
      ->name('v1.')
      ->group(function () {
        foreach (glob(base_path('routes') . '/api/*.php') as $file) {
          require $file;
        }
      });
  }
}

3) 新建「路由」 routes/api/user.php

<?php

Route::prefix('user')
  ->namespace('User')
  ->name('user.')
  ->group(function () {
    Route::post('register', 'UserController@register')->name('user.register');
  });

4) 新建「控制器」 app/Http/Controllers/V1/User/UserController.php

<?php

namespace App\Http\Controllers\V1\User;

use App\Http\Controllers\Controller;

class UserController extends Controller
{
  public function register()
  {
  }
}

API 规范

参考 Jiannei/laravel-response

由于个人并不太喜欢用 RESTful 规范的 HTTP 状态码以及 Enum 的定义,就整了个新包 sevming/laravel-response, 默认 HTTP 返回状态码为 200

1) 安装

$ composer require sevming/laravel-response:^1.0

2) 编辑 app/Exceptions/Handler.php

class Handler extends ExceptionHandler
{
  use \Sevming\LaravelResponse\Support\Traits\ExceptionTrait;
}

Enum 枚举

1) 新建 app/Enums/ResponseEnum.php

<?php

namespace App\Enums;

class ResponseEnum
{
  // sevming/laravel-response 默认以 '|' 作为分割错误码与错误信息的字符串
  public const INVALID_REQUEST = '无效请求|21001';
}

2) 编辑 app/Http/Controllers/V1/User/UserController.php

...

use Sevming\LaravelResponse\Support\Facades\Response;
use App\Enums\ResponseEnum;
...
public function register()
{
  Response::fail(ResponseEnum::INVALID_REQUEST);
}

...

3) POST 请求 /v1/user/register

{
  "status": "fail",
  "code": "21001",
  "message": "无效请求",
  "data": {},
  "errors": {}
}

表单场景验证

1) 新建「表单验证基类」 app/Http/Requests/FormRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest as BaseFormRequest;

class FormRequest extends BaseFormRequest
{
  use SceneValidator;

  /**
   * Determine if the user is authorized to make this request.
   *
   * @return bool
   */
  public function authorize()
  {
    return true;
  }
}

2) 新建 app/Http/Requests/SceneValidator.php

<?php

namespace App\Http\Requests;

use Illuminate\Contracts\Validation\{Factory, Validator};

trait SceneValidator
{
  protected $scene = null;

  protected $onlyRule = [];

  protected $autoValidate = true;

  /**
   * Validate.
   *
   * @param string|array $scene
   */
  public function validate($scene = '')
  {
    if (!$this->autoValidate) {
      if (is_array($scene)) {
        $this->onlyRule = $scene;
      } else {
        $this->scene = $scene;
      }

      $this->handleValidate();
    }
  }

  /**
   * 覆盖 ValidatesWhenResolvedTrait->validateResolved
   */
  public function validateResolved()
  {
    if (method_exists($this, 'autoValidate')) {
      $this->autoValidate = $this->container->call([$this, 'autoValidate']);
    }

    if ($this->autoValidate) {
      $this->handleValidate();
    }
  }

  /**
   * Handle validate.
   */
  protected function handleValidate()
  {
    parent::validateResolved();
  }

  /**
   * 定义 FormRequest->getValidatorInstance 下 validator 验证器
   *
   * @param Factory $factory
   *
   * @return Validator
   */
  public function validator(Factory $factory)
  {
    $validationData = $this->isMethod('GET') ? $this->query() : $this->post();
    return $factory->make($validationData, $this->getRules(), $this->messages(), $this->attributes());
  }

  /**
   * Get rules.
   *
   * @return array
   */
  protected function getRules()
  {
    return $this->handleScene($this->container->call([$this, 'rules']));
  }

  /**
   * Handle scene.
   *
   * @param array $rules
   *
   * @return array
   */
  protected function handleScene(array $rules)
  {
    if ($this->onlyRule) {
      return $this->handleRules($this->onlyRule, $rules);
    }

    if (!empty($this->scene) && method_exists($this, 'scene')) {
      $scene = $this->container->call([$this, 'scene']);
      if (array_key_exists($this->scene, $scene)) {
        return $this->handleRules($scene[$this->scene], $rules);
      }
    }

    return $rules;
  }

  /**
   * Handle rules.
   *
   * @param array $sceneRules
   * @param array $rules
   *
   * @return array
   */
  protected function handleRules(array $sceneRules, array $rules)
  {
    $result = [];
    foreach ($sceneRules as $key => $value) {
      if (is_numeric($key) && array_key_exists($value, $rules)) {
        $result[$value] = $rules[$value];
      } else {
        $result[$key] = $value;
      }
    }

    return $result;
  }
}

3) 新建「请求类」 app/Http/Requests/User/UserRequest.php

<?php

namespace App\Http\Requests\User;

use App\Http\Requests\FormRequest;

class UserRequest extends FormRequest
{
  protected $autoValidate = false;

  public function rules()
  {
    return [
      'account' => 'required|string',
      'password' => 'required|string|min:6',
    ];
  }

  public function scene()
  {
    return [
      'register' => [
        'account',
        'password',
      ],
    ];
  }
}

4) 编辑 app/Http/Controllers/V1/User/UserController.php

...

use App\Http\Requests\User\UserRequest;

...

public function register(UserRequest $request)
{
  $params = $request->post();
  $request->validate('register');
  dd($params);
}

...

5) POST 请求 /v1/user/register

# 请求数据

account:test

password:12345

# 返回数据 - 错误提示为英文

{

  "status": "fail",

  "code": "20002",

  "message": "Unprocessable Entity",

  "data": {},

  "errors": {

    "password": [

      "The password must be at least 6 characters."

    ]

  }

}

6) 安装 overtrue/laravel-lang - 语言包

composer require "overtrue/laravel-lang:~3.0"

# 1. 配置 config/app.php

Illuminate\Translation\TranslationServiceProvider::class

替换

Overtrue\LaravelLang\TranslationServiceProvider::class

# 2. 配置 config/app.php

'locale' => 'zh-CN'

7) 重新请求接口,返回数据如下:

{
  "status": "fail",
  "code": "20002",
  "message": "Unprocessable Entity",
  "data": {},
  "errors": {
    "password": [
      "密码 至少为 6 个字符。"
    ]
  }
}

事件监听

QueryListener - SQL 日志记录

1) 新建 app/Listeners/QueryListener.php

<?php

namespace App\Listeners;

use Illuminate\Database\Events\QueryExecuted;

class QueryListener
{
  /**
   * Create the event listener.
   *
   * @return void
   */
  public function __construct()
  {
  }

  /**
   * Handle the event.
   *
   * @param QueryExecuted $event
   * @return void
   */
  public function handle(QueryExecuted $event)
  {
    if (true === config('app.debug')) {
      foreach ($event->bindings as $key => $value) {
        if ($value instanceof \DateTimeInterface) {
          $event->bindings[$key] = $value->format('Y-m-d H:i:s');
        } elseif (is_bool($value)) {
          $event->bindings[$key] = (int)$value;
        }
      }

      $sql = str_replace('?', "'%s'", $event->sql);
      logger("[{$event->time}ms] " . vsprintf($sql, $event->bindings));
    }
  }
}

2) 编辑 app/Providers/EventServiceProvider.php

<?php
...
protected $listen = [
  \Illuminate\Database\Events\QueryExecuted::class => [
    \App\Listeners\QueryListener::class
  ],
];
...

用户注册

1) 新建「腾讯地图类」 app/Libraries/TencentMapLibrary.php

<?php

namespace App\Libraries;

use \Exception;

class TencentMapLibrary
{
  /**
   * 通过IP获取用户位置信息
   *
   * @param string $ip
   *
   * @return string
   * @throws Exception
   */
  public static function getLocationByIp(string $ip)
  {
    return '福建省厦门市';
  }
}

2) 新建「服务类」 app/Services/User/UserService.php

<?php

namespace App\Services\User;

use \Exception;
use App\Libraries\TencentMapLibrary;
use App\Models\User\User;

class UserService
{
  /**
   * @param array   $params
   *
   * @return User
   * @throws Exception
   */
  public function createUser(array $params)
  {
    $userData = [
      'account' => $params['account'],
      'password' => bcrypt($params['password']),
      'last_ip' => request()->ip(),
    ];
    $location = TencentMapLibrary::getLocationByIp($userData['last_ip']);
    if (!empty($location)) {
      $userData['register_address'] = $location;
    }

    return User::create($userData);
  }
}

3) 编辑 app/Models/User/User.php

<?php

namespace App\Models\User;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
  protected $guarded = [];

  public static function findByAccount(string $account)
  {
    return static::query()
      ->where('account', $account)
      ->first();
  }

  public function setLastIpAttribute($value)
  {
    $this->attributes['last_ip'] = ip2long($value);
  }

  public function getLastIpAttribute($value)
  {
    return long2ip($value);
  }
}

4) 编辑 app/Enums/ResponseEnum.php

...

public const USER_ACCOUNT_REGISTERED = '账号已注册|23001';

...

5) 编辑 app/Http/Controllers/V1/User/UserController.php

<?php

namespace App\Http\Controllers\V1\User;

use \Exception;
use Illuminate\Http\JsonResponse;
use Sevming\LaravelResponse\Support\Facades\Response;
use App\Enums\ResponseEnum;
use App\Http\Controllers\Controller;
use App\Http\Requests\User\UserRequest;
use App\Services\User\UserService;
use App\Models\User\User;

class UserController extends Controller
{
  /**
   * @var UserService
   */
  protected $service;

  /**
   * Constructor.
   */
  public function __construct(UserService $userService)
  {
    $this->service = $userService;
  }

  /**
   * @param UserRequest $request
   *
   * @return JsonResponse
   * @throws Exception
   */
  public function register(UserRequest $request)
  {
    $params = $request->post();
    $request->validate('register');
    $user = User::findByAccount($params['account']);
    if (!empty($user)) {
      Response::fail(ResponseEnum::USER_ACCOUNT_REGISTERED);
    }

    $user = $this->service->createUser($params);

    return Response::success([
      'id' => $user->id
    ]);
  }
}

Eloquent 条件查询

1) 安装

$ composer require tucker-eric/eloquentfilter:^2.4

2) 新建 app/Models/ModelTrait.php

<?php

namespace App\Models;

trait ModelTrait
{
  protected function modelFilter()
  {
    return config('eloquentfilter.namespace', 'App\\ModelFilters\\')
      . str_replace(__NAMESPACE__ . '\\', '', get_class($this)) . 'Filter';
  }
}

3) 新建「模型过滤类」 app/ModelFilters/User/UserFilter.php

<?php

namespace App\ModelFilters\User;

use EloquentFilter\ModelFilter;

class UserFilter extends ModelFilter
{
  public function account($account)
  {
    return $this->where('account', $account);
  }
}

4) 编辑 app/Models/User/User.php

...
use EloquentFilter\Filterable;
use App\Models\ModelTrait;

class User extends Authenticatable
{
 use Filterable, ModelTrait;
 ...  
 public static function findByAccount(string $account)
 {
   return static::filter([
     'account' => $account
   ])
     ->first();
  }  
  ...
}

模型事件

优化用户注册时的地址获取

1) 新建「观察者类」 app/Observers/User/UserObserver.php

<?php

namespace App\Observers\User;

use Illuminate\Support\Facades\DB;
use App\Libraries\TencentMapLibrary;
use App\Models\User\User;

class UserObserver
{
  public function saved(User $user)
  {
    rescue(function () use ($user) {
      $wasRecentlyCreated = $user->wasRecentlyCreated;
      if ($wasRecentlyCreated || $user->wasChanged('last_ip')) {
        $location = TencentMapLibrary::getLocationByIp($user->last_ip);
        if (!empty($location)) {
          $data = ['last_location' => $location];
          $wasRecentlyCreated && ($data['register_address'] = $data['last_location']);
          DB::table('users')->where('id', $user->id)->update($data);
        }
      }
    });
  }
}

2) 编辑 app/Models/User/User.php

...
use App\Observers\User\UserObserver;

class User extends Authenticatable
{
  ...
  public static function boot()
  {
    parent::boot();
    static::observe(UserObserver::class);
  }
  ...
}

3) 编辑 app/Services/User/UserService.php

...

public function createUser(array $params)
{
  $userData = [
    'account' => $params['account'],
    'password' => bcrypt($params['password']),
    'register_address' => '',
    'last_ip' => request()->ip(),
  ];
  return User::create($userData);
}

...

队列

用户注册地址获取修改为队列方式

1) .env 队列配置

QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=123456
REDIS_PORT=6379

2) 新建「任务类」 app/Jobs/User/UserLocation.php

<?php

namespace App\Jobs\User;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\{InteractsWithQueue, SerializesModels};
use App\Libraries\TencentMapLibrary;
use Illuminate\Support\Facades\DB;
use App\Models\User\User;

class UserLocation implements ShouldQueue
{
  use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

  protected $user;

  protected $wasRecentlyCreated;

  public function __construct(User $user, bool $wasRecentlyCreated)
  {
    $this->user = $user;
    $this->wasRecentlyCreated = $wasRecentlyCreated;
  }

  public function handle()
  {
    $location = TencentMapLibrary::getLocationByIp($this->user->last_ip);
    if (!empty($location)) {
      $data = ['last_location' => $location];
      $this->wasRecentlyCreated && ($data['register_address'] = $data['last_location']);
      DB::table('users')->where('id', $this->user->id)->update($data);
    }
  }
}

3) 编辑 app/Observers/User/UserObserver.php

<?php

namespace App\Observers\User;

use App\Jobs\User\UserLocation;
use App\Models\User\User;

class UserObserver
{
  public function saved(User $user)
  {
    rescue(function () use ($user) {
      $wasRecentlyCreated = $user->wasRecentlyCreated;
      if ($wasRecentlyCreated || $user->wasChanged('last_ip')) {
        dispatch(new UserLocation($user, $wasRecentlyCreated));
      }
    });
  }
}

4) 开启队列监听

$ php artisan queue:listen

Passport 认证

实现用户注册后返回 Token, 以及删除无效 Token

1) 安装 & 配置

$ composer require laravel/passport:^9.4.0

$ php artisan migrate --path=./vendor/laravel/passport/database/migrations/

$ php artisan passport:keys

$ php artisan passport:client --personal

$ php artisan vendor:publish --tag=passport-config

# 安装报错

laravel/passport[v9.4.0, ..., 9.x-dev] require phpseclib/phpseclib ^2.0 -> found phpseclib/phpseclib

# 删除 laravel/telescope 包后再安装, 之后再重新安装 laravel/telescope

$ composer remove laravel/telescope --dev

2) .env 配置,保存刚刚生成的「个人客户端」CLIENT_ID & CLIENT_SECRET

PASSPORT_PERSONAL_ACCESS_CLIENT_ID=1

PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET=jToNhXPoQVxVkBrdljCwk5oWzO7hroozHkqQ9Kja

3) 配置 app/Providers/AuthServiceProvider.php, 设置 TOKEN 有效期一周

...
use Laravel\Passport\Passport;
...
public function boot()
{
  ...
  Passport::personalAccessClientId(config('passport.personal_access_client.id'));
  Passport::personalAccessTokensExpireIn(now()->addWeek());
}
...
4) 编辑 app/Providers/EventServiceProvider.php 监听 Token 创建事件

...
protected $listen = [
  ...
  \Laravel\Passport\Events\AccessTokenCreated::class => [
    \App\Listeners\PassportAccessTokenCreated::class
  ]
];

...

5) 新建 app/Listeners/PassportAccessTokenCreated.php 删除过期 Token

<?php

namespace App\Listeners;

use Laravel\Passport\Events\AccessTokenCreated;
use Laravel\Passport\Token;

class PassportAccessTokenCreated
{
  public function handle(AccessTokenCreated $event)
  {
    Token::query()
      ->where('id', '<>', $event->tokenId)
      ->where('user_id', $event->userId)
      ->where('client_id', $event->clientId)
      ->where('revoked', 0)
      ->where('expires_at', '<=', now()->toDateTimeString())
      ->delete();
  }
}

6) 编辑 app/Models/User/User.php

...
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
  use Filterable, ModelTrait, HasApiTokens;
  ...
}

7) 编辑 app/Observers/User/UserObserver.php

...

public function saved(User $user)
{
  $user->setAttribute('token', $user->createToken($user->id)->accessToken);
  ...
}

...

8) 编辑 app/Http/Controllers/V1/User/UserController.php

...

public function register(UserRequest $request)
{
  ...
  return Response::success([
    'id' => $user->id,
    'token' => $user->token
  ]);
}

...

使用 Token 作为用户的登录凭证

1) 编辑 routes/api/user.php

<?php

Route::prefix('user')
  ->namespace('User')
  ->name('user.')
  ->group(function () {
    ...
    Route::middleware('auth:api')->group(function () {
      Route::get('mock', 'UserController@mock')->name('user.mock');
    });
  });

2) 编辑 app/Http/Controllers/V1/User/UserController.php

...
public function mock()
{
  dump(auth()->user());
}
...

3) 编辑 config/auth.php

return [
  ...
  'guards' => [
  ...
    'api' => [
      'driver' => 'passport',
      'provider' => 'users',
      'hash' => false,
     ],
   ],
   'providers' => [
     'users' => [
       'driver' => 'eloquent',
       'model' => App\Models\User\User::class,
     ],
   ],
   ...
];

4) Postman 配置 Header

Authorization: Bearer {token}

4) GET 请求接口 /v1/user/mock, 可以看到用户的信息

5) 通过查看 SQL 日志,发现 Passport 查询语句太多了,安装 overtrue/laravel-passport-cache-token, 支持配置缓存方式,具体可查看包文档说明

$ composer require overtrue/laravel-passport-cache-token:^2.1

结语

文笔不好,还是直接发代码来的直接。

有不足的地方,欢迎各位大佬指出,谢谢~

补充点

1) 关于 PhpStorm 中 Laravel 的路由跳转 (Windows 下 Ctrl + 鼠标左键 点击路由跳转至对应控制器),Demo 中无法跳转问题

编辑 routes/api/user.php

<?php
  Route::group([
    'prefix' => 'user',
    'namespace' => 'User'
    'as' => 'user.',
  ], function () {
    ...
  });

重新生成 ide-helper.php

$ php artisan ide-helper:generate

2) 跨域

安装 fruitcake/laravel-cors

$ composer require "fruitcake/laravel-cors:^2.0"

$ php artisan vendor:publish --tag="cors"

编辑 app/Http/Kernel.php

...

protected $middleware = [

  \App\Http\Middleware\TrustProxies::class,

  \Fruitcake\Cors\HandleCors::class,

  ...

];

...

编辑 config/cors.php - 查看更多配置

'paths' => ['v1/*']

3) 添加模型 saveQuietly 方法 - 保存给定的模型而不触发任何事件 (Laravel 8.x 框架自带)


编辑 app/Models/ModelTrait.php

...
/**
 * Save the model to the database without raising any events.
 *
 * @param array $options
 *
 * @return bool
 */
public function saveQuietly(array $options = [])
{
  return static::withoutEvents(function () use ($options) {
    return $this->save($options);
  });
}
...

升级 8.x

直接安装 8.x 版本,再按文档搭项目,相关包版本变化如下:

laravel/laravel:^6.2 to ^8.4.0

barryvdh/laravel-ide-helper:2.8.* to ^2.10

laravel/telescope:^3.0 to ^4.4

# migrations 路径变更

php artisan migrate --path=./vendor/laravel/telescope/database/migrations/

overtrue/laravel-lang:~3.0 to ~5.0

# 编辑 config/app.php

# 与 3.0 版本不同, 这个无需替换服务提供者

Illuminate\Translation\TranslationServiceProvider::class  

# 配置语言

'locale' => 'zh_CN'

tucker-eric/eloquentfilter:^2.4 to ^3.0

laravel/passport:^9.4.0 to ^10.1

# 与 9.x 版本不同, 不需要在 `AuthServiceProvider` 配置 personalAccessClientId(), 且此方法在新版本的包中已移除

sevming/laravel-response:^1.0 to ^2.0

fruitcake/laravel-cors - 无需安装,框架自带

app/Models/ModelTrait => saveQuietly 不需要添加,框架自带