前言
雖然開發了幾年,但一直沒有使用 Laravel
作為專案框架,在 Laravel
裡,屬於不折不扣的新人,正好新專案用了 Laravel
,在這分享一下 Laravel
開發 API
心得,希望能給想使用 Laravel
開發的人帶來幫助。
透過實現「使用者註冊」介面,來介紹每個功能的使用場景。
PS:「使用者註冊」介面純粹是為了方便介紹,所以沒有使用自帶的 Auth 模組.
大綱
開發環境
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) 底層原理
- 服務容器
Laravel 學習筆記 —— 神奇的服務容器
Laravel 服務容器的初始化 - 中介軟體
Laravel 中介軟體原理 - 管道流
Laravel 管道流原理
Laravel Pipeline 元件的實現原理 - 異常處理
Laravel 原始碼閱讀指南–異常處理
Laravel Exceptions——異常與錯誤處理 - 佇列
剖析 Laravel 佇列系統–Worker - 認證
Laravel 認證原理及完全自定義認證 - 其它原理
leoyang 的部落格
Laravel之道
2) 架構
- 基於 Module 的 Laravel API 架構
- Laravel 模組化開發
- 打造 Laravel 優美架構 談可維護性與彈性設計
- Laravel 程式架構設計思路:使用動作類
- 什麼?快來開啟 MVC 的擴充模式
- 程式架構討論:你的應用就是一個擴充套件的容器
- Laravel - 服務設計模式
- 如何使用Service模式
- 關於 Repository 的設計模式
- 在 Laravel 5.8 中正確地應用 Repository 設計模式
3) API 規範相關討論
4) 表單場景驗證
5) 模型相關
- 如何更快的找到自己所需的模型關聯型別?
- Laravel Eloquent 模型關聯速查表
- 模型關聯給我們帶來了哪些便利
- Laravel 模型過濾(Filter)設計
- 關於模型全域性作用域是否支援動態傳參?
- 對於專案中簡單的多條件查詢的一些心得體會
- whereHas效能調優——採用 where in 語法實現最佳化 查詢關聯
- 給 Eloquent 的 whereHas 加個 where in 的最佳化
- 如何註冊一個最後執行的模型事件監聽器
- ORM Observer 使用小結
6) API 資源
7) 佇列
補充點
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
不需要新增,框架自帶
本作品採用《CC 協議》,轉載必須註明作者和本文連結