轉:Laravel 項目開發規範

文件介紹很好 值得細細看看php

https://www.jianshu.com/p/e464a35e5ed2css

https://learnku.com/docs/laravel-specification/5.5html

一. 說明

如下內容大部分引用Laravel China社區的文章 - 分享下團隊的開發規範 ——《Laravel 項目開發規範》
相對而言,上面引用的文章的規範更加嚴格,但考慮到目前的狀況,會適當地對一些規範進行更改和增刪。前端

二. 目的

暫無vue

三. 優勢

規範有一下優勢:laravel

  • 高效編碼 - 避免了過多的選擇形成的『決策時間』浪費;
  • 風格統一 - 最大程度統一了開發團隊成員代碼書寫風格和思路,代碼閱讀起來一模一樣;
  • 減小錯誤 - 減少初級工程師的犯錯概率。

四. 開發哲學

  • DRY –「Don't Repeat Yourself」不寫重複的邏輯代碼;
  • 約定俗成 - 「Convention Over Configuration」,優先選擇框架提倡的作法,不過分配置;
  • KISS - 「Keep it Simple, Stupid」提倡簡單易讀的代碼,不寫高深、晦澀難懂的代碼,不過分設計;
  • 主廚精選 - 讓有經驗的人來爲你選擇方案,不首創方案;
  • 官方提倡 - 優先選擇官方推崇的方案。

五. 設計理念

如下是一些優秀的『程序設計理念』:git

  • MVC - Model, View, Controller ,以 MVC 爲核心,嚴格控制 Controller 的可讀性和代碼行數;
  • Restful - 利用『資源化概念』和標準的 HTTP 動詞來組織你的程序;

在此規範中,咱們會將使用這兩套理念做爲程序設計基礎。這些設計理念爲咱們設計程序提供了依據,遵循這些理念,能讓程序變得清晰易讀。github

六. 能願動詞

爲了不歧義,文檔大量使用了「能願動詞」,對應的解釋以下:web

  • 必須(Must) - 只能這樣子作,請無條件遵循,沒有別的選項;
  • 毫不(Must Not)- 嚴令禁止,在任何狀況下都不能這樣作;
  • 應該(Should) - 強烈建議這樣作,可是不強求;
  • 不該該(Should Not) - 強烈建議不這樣作,可是不強求;
  • 能夠(May) - 選擇性高一點,在這個文檔內,此詞語使用較少;

七. 關於Laravel版本的選擇

選擇Laravel版本時,應該 優先選擇LTS版本,除非有特殊緣由,如生產服務器的PHP版本不是PHP7以上,而是PHP5.*,且爲了穩定不升級到PHP7,那麼 能夠 考慮使用上一個版本的發行版。
好比Laravel 5.5是最新的LTS可是隻支持PHP7以上,那麼 能夠 考慮使用Laravel 5.4。固然比較可使用Laravel 5.1 LTS版本,可是該版本比較舊,沒有新版本的一些新特性。ajax

請使用如下命令來建立指定版本的 Laravel 項目:

composer create-project laravel/laravel project-name --prefer-dist "5.5.*"

毫不 也禁止使用複製粘貼項目文件的方式來建立項目。

八. 環境說明

通常狀況下,一個項目 應該 有如下三個基本的項目環境:

  • Local - 開發環境
  • Staging - 線上測試環境,對應git的test分支
  • Production - 線上生產環境,對應git的master分支

九. git分支

在建立git倉庫後,建議最好分開三個分支

  • 主分支 - master,對應開發環境
  • 測試分支 - test,對應線上測試環境
  • 開發分支 - develop,對應線上生產環境

全部功能都是從develop分支新建分支,按功能模塊命名

  • 新的功能模塊,使用 features/功能名稱 來命名

  • 修復bug,使用 fix/bug名稱 來命名

  • 功能開發後,合併到develop

  • 開發測試經過後,將develop合併到test分支

  • 測試環境功能測試經過後,將test合併到master

十. 配置信息與環境變量

在 Laravel 中有如下幾種方法:

  1. 硬代碼,直接寫死。- ❌ 可維護性低
  2. 寫死在 config/app.php 文件中。 - ❌ 沒法區分環境進行配置
  3. 存儲於 .env 文件中,使用 env() 方法直接讀取。 - ❌ 雖然解決了環境變量問題可是不推薦
  4. 存儲在 .env 和 config/app.php 文件中,而後使用 config() 函數來讀取。- ✅ 最佳實踐

第一種方法是最古老的方法,代碼可維護性極低,一旦域名變動就只能全局替換。
第二種方法沒法區分環境,例如本地使用開發環境域名測試,線上纔是正式的域名。
第三種方法雖然解決了環境變量的問題,而且也具有必定的靈活性,可是不夠靈活,假如你的網站流量巨大,須要配置幾個域名,使其在加載靜態資源時隨機支配域名,這種作法就沒法知足需求了。
第四種方法既支持環境變量,又具有極高的靈活性,假如遇到一樣的多域名隨機問題,你只須要寫一個輔助方法,而後在 config/app.php 中調用便可,不須要動到任何一行業務邏輯代碼。

代碼示例

.env 文件中設置:

DOMAIN=018eighteen.test

config/app.php 文件中設置:

'domain' => env('DOMAIN', '018eighteen.com'), 

程序中兩種獲取 相同配置 的方法:

  1. env('DOMAIN')
  2. config('app.domain')

在此統一規定:全部程序配置信息 必須 經過 config() 來讀取,全部的 .env 配置信息 必須 經過 config() 來讀取,毫不 在配置文件之外的範圍使用 env()

這樣作主要有如下幾個優點:

  1. 定義分明,config() 是配置信息,env() 只是用來區分不一樣環境;
  2. 統一放置於 config 中還能夠利用框架的 配置信息緩存功能 來提升運行效率;
  3. 代碼健壯性, config() 在 env() 之上多出來一個抽象層,會使代碼更加健壯,更加靈活。

十一. 路由器

1. 路由閉包

毫不 在路由配置文件裏書寫『閉包路由』或者其餘業務邏輯代碼,由於一旦使用將沒法使用 路由緩存 。
路由器要保持乾淨整潔,毫不 放置除路由配置之外的其餘程序邏輯。

2. Restful 路由

必須 優先使用 Restful 路由,配合資源控制器使用,見 文檔

 

 

 
file

 

超出 Restful 路由的,應該 模仿上圖的方式來定義路由。

3. resource 方法正確使用

通常資源路由定義:

Route::resource('photos', 'PhotosController'); 

等於如下路由定義:

Route::get('/photos', 'PhotosController@index')->name('photos.index'); Route::get('/photos/create', 'PhotosController@create')->name('photos.create'); Route::post('/photos', 'PhotosController@store')->name('photos.store'); Route::get('/photos/{photo}', 'PhotosController@show')->name('photos.show'); Route::get('/photos/{photo}/edit', 'PhotosController@edit')->name('photos.edit'); Route::put('/photos/{photo}', 'PhotosController@update')->name('photos.update'); Route::delete('/photos/{photo}', 'PhotosController@destroy')->name('photos.destroy'); 

使用 resource 方法時,若是僅使用到部分路由,必須 使用 only 列出全部可用路由:

Route::resource('photos', 'PhotosController', ['only' => ['index', 'show']]); 

毫不 使用 except,由於 only 至關於白名單,相對於 except 更加直觀。路由使用白名單有利於養成『安全習慣』。

4. 單數 or 複數?

資源路由路由 URI 必須 使用複數形式,如:

  • /photos/create
  • /photos/{photo}

錯誤的例子如:

  • /photo/create
  • /photo/{photo}

5. 路由命名

除了 resource 資源路由之外,其餘全部路由都 必須 使用 name 方法進行命名。
必須 使用『資源前綴』做爲命名規範,以下的 users.follow,資源前綴的值是 users.

Route::post('users/{id}/follow', 'UsersController@follow')->name('users.follow'); 

6. 獲取 URL

獲取 URL 必須 遵循如下優先級:

  1. $model->link()
  2. route 方法
  3. url 方法

在 Model 中建立 link() 方法:

public function link($params = []) { $params = array_merge([$this->id], $params); return route('models.show', $params); } 

全部單個模型數據連接使用:

$model->link(); // 或者添加參數 $model->link($params = ['source' => 'list']) 

『單個模型 URI』常常會發生變化,這樣作將會讓程序更加靈活。

除了『單個模型 URI』,其餘路由 必須 使用 route 來獲取 URL(這也是目前使用次數最多的方法):

$url = route('profile', ['id' => 1]); 

沒法使用 route 的狀況下,能夠 使用 url 方法來獲取 URL:

url('profile', [1]);

十二. 數據模型

1. 放置位置

全部的數據模型文件,都 必須 存放在:app/Models/ 文件夾中。

命名空間:

namespace App\Models; 

2. 使用基類

全部的 Eloquent 數據模型 都 必須 繼承統一的基類 App/Models/Model,此基類存放位置爲 /app/Models/Model.php,內容參考如下:

<?php namespace App\Models; use Illuminate\Database\Eloquent\Model as EloquentModel; class Model extends EloquentModel { public function scopeRecent($query) { return $query->orderBy('created_at', 'desc'); } } 

以 Photo 數據模型做爲例子繼承 Model 基類:

<?php namespace App\Models; class Photo extends Model { protected $fillable = ['id', 'user_id']; public function user() { return $this->belongsTo(User::class); } } 

3. 命名規範[#]

數據模型相關的命名規範:

  • 數據模型類名 必須 爲「單數」, 如:App\Models\Photo
  • 類文件名 必須 爲「單數」,如:app/Models/Photo.php
  • 數據庫表名字 必須 爲「複數」,多個單詞狀況下使用「Snake Case」 如:photosmy_photos (注:目前因爲和其餘團隊合做開發,因此這一條規範暫時不硬性要求)
  • 數據庫表遷移名字 必須 爲「複數」,如:2014_08_08_234417_create_photos_table.php
  • 數據填充文件名 必須 爲「複數」,如:PhotosTableSeeder.php
  • 數據庫字段名 必須 爲「Snake Case」,如:view_countis_vip
  • 數據庫表主鍵 必須 爲「id」(注:這條規範必定要嚴格執行,避免像018server的prouct表同樣出現product_id這樣的主鍵)
  • 數據庫表外鍵 必須 爲「resource_id」,如:user_idpost_id
  • 數據模型變量 必須 爲「resource_id」,如:$user_id$post_id

4. 利用 Trait 來擴展數據模型

有時候數據模型裏的代碼會變得很臃腫,應該 利用 Trait 來精簡邏輯代碼量,提升可讀性,相似於 Ruby China 源碼

借鑑於 Rails 的設計理念:「Fat Models, Skinny Controllers」。

存放於文件夾:app/Models/Traits 文件夾中。

5. Repository

分享下團隊的開發規範 ——《Laravel 項目開發規範》 提出不適用 Repository 模式進行開發,可是考慮到隨着功能愈來愈多,不適用 Repository 會使得控制器愈來愈臃腫,有些代碼也會不停地重複寫,另外之後有可能須要編寫單元測試,因此最後仍是決定啓用 Repository 。

具體參照爲何你應該使用 Repository

6. 關於 SQL 文件

  • 毫不 使用命令行或者 PHPMyAdmin 直接建立索引或表。必須 使用 數據庫遷移 去建立表結構,並提交版本控制器中;
  • 毫不 爲了共享對數據庫更改就直接導出 SQL,全部修改都 必須 使用 數據庫遷移 ,並提交版本控制器中;
  • 毫不 直接向數據庫手動寫入僞造的測試數據。必須 使用 數據填充 來插入假數據,並提交版本控制器中。

考慮到可能會和其餘團隊合做開發,因此具體仍是根據團隊的協定而定。可是若是是本身團隊開發的話,必須嚴格按照以上標準。

十三. 控制器

1. 資源控制器

必須 優先使用 Restful 資源控制器 。

2. 單數 or 複數?

必須 使用資源的複數形式,如:

  • 類名:PhotosController
  • 文件名:PhotosController.php

錯誤的例子:

  • 類名:PhotoController
  • 文件名:PhotoController.php

3. 保持短小精煉

必須 保持控制器文件代碼行數最小化,還有可讀性。

  • 不該該 爲「方法」書寫註釋,這要求方法取名要足夠合理,不須要過多註釋;
  • 應該 爲一些複雜的邏輯代碼塊書寫註釋,主要介紹產品邏輯 - 爲何要這麼作。
  • 不該該 在控制器中書寫「私有方法」,控制器裏 應該 只存放「路由動做方法」;
  • 毫不 遺留「死方法」,就是沒有用到的方法,控制器裏的全部方法,都應該被使用到,不然應該刪除;
  • 毫不 在控制器裏批量註釋掉代碼,無用的邏輯代碼就必須清除掉。
  • 應該 建立Service層創建對應的Service類,以實現控制器對應的邏輯,見下放關於Service

十四. Service

在上面提過決定啓用 Repository ,Repository主要是實現對model的增刪改查。
而 Service 則是介於 Controller 與 Repository 之間,是對 Controller 業務邏輯的實現,實現過程當中,經過 Repository 來對數據進行操做。

1. 建立 Service 文件夾

首先咱們須要在 app 文件夾建立本身 Service 文件夾 services,而後文件夾的每個文件都要設置相應的命名空間。

2. 建立對應的 Service 類

PostsController.php

<?php namespace App\Controllers; use App\Http\Controllers\Controller; use App\Services\PostsService; class PostsController extend Controller{ private $postsService; public function __construct (PostsService $posts) { $this->postsService = $posts; } public function addArticle (Request $request) { return $this->postsService->addArticle ($request->all()); } } 

PostsService.php

<?php namespace App\Services; class PostsService{ public function addArticle ($data) { //To add a article... } } 

十五. 視圖

在不進行先後端分離的狀況下,請使用視圖

1. 優先使用 Blade

視圖文件 必須 優先考慮使用 .blade.php 後綴來指定使用 Blade 模板引擎。

2. 保持目錄清晰

  • layouts - 頁面佈局文件 必須 放置於此目錄下;
  • common - 存放頁面通用元素;
  • pages - 簡單的頁面存放文件夾,如:about、contact 等;
  • resources - 對應 Restful 路由的資源路徑名稱,以 URI photos/create 爲例,對應 create.blade.php 文件,存放在文件夾 photos 下。

必須 避免在 resources/views 目錄下直接放置視圖文件。

3. 局部視圖

局部視圖文件 必須 使用 _ 前綴來命名,如:photos/_upload_form.blade.php

4. 視圖命名要釋義

爲了和 Restful 路由器和資源控制器保持一致,視圖命名也 必須 使用資源視圖的命名方式。以 photos 爲例:

  • photos/index.blade.php
    • 內容列表視圖
    • 對應路由器 /photos,命名 photos.index
    • 控制器方法 PhotosController@index
  • photos/show.blade.php
    • 單個內容視圖
    • 對應路由器 /photos/{id},命名 photos.show
    • 控制器方法 PhotosController@show
  • photos/create.blade.php
    • 內容建立視圖
    • 對應路由器 /photos/create,命名 photos.create
    • 控制器方法 PhotosController@create
  • photos/edit.blade.php
    • 內容編輯的視圖
    • 對應路由器 /photos/edit,命名 photos.edit
    • 控制器方法 PhotosController@edit

5. create_and_edit 視圖

不少狀況下,建立和編輯視圖裏的頁面結構接近類似,在這種狀況下,應該 使用 create_and_edit 視圖。以 photos 爲例:

  • PhotosController@create - 對應視圖:/photos/create_and_edit.blade.php
  • PhotosController@edit - 對應 視圖:/photos/create_and_edit.blade.php

這樣一來,一般狀況下,一個完整的 photos 資源對應的視圖文件爲如下:

├── photos
│   ├── create_and_edit.blade.php
│   ├── index.blade.php
│   └── show.blade.php

十六. 表單驗證

1. 表單請求驗證類

必須 使用 表單請求 - FormRequest 類 來處理控制器裏的表單驗證。

2. 使用基類

全部 FormRequest 表驗證類 必須 繼承 app/Http/Requests/Request.php 基類。基類文件以下:

<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class Request extends FormRequest { public function authorize() { // Using policy for Authorization return true; } } 

3. 驗證類命名

FormRequest 表驗證類 必須 遵循 資源路由 方式進行命名,photos 對應 app/Http/Requests/PhotoRequest.php 。

4. 類文件參考

FormRequest 表驗證類文件請參考如下:

<?php namespace App\Http\Requests; class PhotoRequest extends Request { public function rules() { switch($this->method()) { // CREATE case 'POST': { return [ // CREATE ROLES ]; } // UPDATE case 'PUT': case 'PATCH': { return [ // UPDATE ROLES ]; } case 'GET': case 'DELETE': default: { return []; }; } } public function messages() { return [ // Validation messages ]; } } 

十七. 數據填充

1. factory 輔助函數

必須 使用 factory 方法來作數據填充,由於是框架提倡的,而且能夠同時爲測試代碼服務。

2. 運行效率

開發數據填充時,必須 特別注意 php artisan db:seed 的運行效率,不然隨着項目的代碼量愈來愈大,db:seed 的運行時間會變得愈來愈長,有些項目多達幾分鐘甚至幾十分鐘。

原則是:

Keep it lighting speed.

只有當 db:seed 運行起來很快的時候,才能徹底利用數據填充工具帶來的便利,而不是累贅。

4. 批量入庫

全部假數據入庫操做,都 必須 是批量操做,配合 factory 使用如下方法:

$users = factory(User::class)->times(1000)->make(); User::insert($users->toArray()); 

以上只執行一條數據庫語句,推薦閱讀 大批量假數據填充的正確方法

十八. Artisan 命令行

全部的自定義命令,都 必須 有項目的命名空間。

如:

php artisan phphub:clear-token php artisan phphub:send-status-email ... 

錯誤的例子爲:

php artisan clear-token
php artisan send-status-email
...

十九. 日期和時間

必須 使用 Carbon 來處理日期和時間相關的操做。

Laravel 5.1 中文的 diffForHumans 可使用 jenssegers/date

Laravel 5.3 及以上版本的 diffForHumans,只須要在 config/app.php 文件中配置 locale 選項便可 :

'locale' => 'zh-CN', 

二十. 前端開發

根據 分享下團隊的開發規範 ——《Laravel 項目開發規範》,規範裏這麼寫的:

  • 必須 使用 Laravel 官方前端工具作前端開發自動化;
  • 必須 保證頁面只加載一個 .css 文件;
  • 必須 保證頁面只加載一個 .js 文件;
  • 必須 爲 .css 和 .js 增長 版本控制
  • 必須 使用 SASS 來書寫 CSS 代碼;

可是考慮到目前團隊的狀況,在不先後端分離的狀況下能夠不執行以上規範。
若是實行先後端分離,則前端必須使用vue腳手架,並生成靜態文件,增長版本控制。

二十一. Laravel 安全實踐

1. 說明

沒有絕對安全,只有相對安全。Laravel 相較於其餘框架在安全方面已經作得很優秀,不過做爲開發者,咱們要在平常開發中對『安全』需懷着敬畏之心,積極培養本身的安全意識。如下是一些 Laravel 安全相關的規範。

2. 關閉 DEBUG

Laravel Debug 開啓時,會暴露不少能被黑客利用的服務器信息,因此,生產環境下請 必須 確保:

APP_DEBUG=false

3. XSS

跨站腳本攻擊(cross-site scripting,簡稱 XSS),具體危害體如今黑客能控制你網站頁面,包括使用 JS 盜取 Cookie 等,關於 XSS 的介紹請前往 IBM 文檔庫:跨站點腳本攻擊深刻解析 。

默認狀況下,在沒法保證用戶提交內容是 100% 安全的狀況下,必須 使用 Blade 模板引擎的 {{ $content }} 語法會對用戶內容進行轉義。

Blade 的 {!! $content !!} 語法會直接對內容進行 非轉義 輸出,使用此語法時,必須 使用 HTMLPurifier for Laravel 5 來爲用戶輸入內容進行過濾。使用方法參見: 使用 HTMLPurifier 來解決 Laravel 5 中的 XSS 跨站腳本攻擊安全問題

4. SQL 注入

Laravel 的 查詢構造器 和 Eloquent 是基於 PHP 的 PDO,PDO 使用 prepared 來準備查詢語句,保障了安全性。

在使用 raw() 來編寫複雜查詢語句時,必須 使用數據綁定。

錯誤的作法:

Route::get('sql-injection', function() { $name = "admin"; // 假設用戶提交 $password = "xx' OR 1='1"; // // 假設用戶提交 $result = DB::select(DB::raw("SELECT * FROM users WHERE name ='$name' and password = '$password'")); dd($result); }); 

如下是正確的作法,利用 select 方法 的第二個參數作數據綁定:

Route::get('sql-injection', function() { $name = "admin"; // 假設用戶提交 $password = "xx' OR 1='1"; // // 假設用戶提交 $result = DB::select( DB::raw("SELECT * FROM users WHERE name =:name and password = :password"), [ 'name' => $name, 'password' => $password, ] ); dd($result); }); 

DB 類裏的大部分執行 SQL 的函數均可傳參第二個參數 $bindings ,詳見:API 文檔 。

(注:建議最好直接使用Laravel的Eloquent ORM來對數據庫進行操做)

5. 批量賦值

Laravel 提供白名單和黑名單過濾($fillable 和 $guarded),開發者 應該 清楚認識批量賦值安全威脅的狀況下合理靈活地運用。

批量賦值安全威脅,指的是用戶可更新原本不該有權限更新的字段。舉例,users 表裏的 is_admin 字段是用來標識用戶『是不是管理員』,某不懷好意的用戶,更改了『修改我的資料』的表單,增長了一個字段:

<input name="is_admin" value="1" /> 

這個時候若是你更新代碼以下:

Auth::user()->update(Request::all()); 

此用戶將獲取到管理員權限。能夠有不少種方法來避免這種狀況出現,最簡單的方法是經過設置 User 模型裏的 $guarded 字段來避免:

protected $guarded = ['id', 'is_admin']; 

6. CSRF

CSRF 跨站請求僞造是 Web 應用中最多見的安全威脅之一,具體請見 Wiki - 跨站請求僞造 或者 Web 應用程序常見漏洞 CSRF 的入侵檢測與防範

Laravel 默認對全部『非冪等的請求』強制使用 VerifyCsrfToken 中間件防禦,須要開發者作的,是區分清楚何時該使用『非冪等的請求』。

冪等請求指的是:'HEAD', 'GET', 'OPTIONS',既不管你執行多少次重複的操做都不會給資源形成變動。

  • 全部刪除的動做,必須 使用 DELETE 做爲請求方法;
  • 全部對數據更新的動做,必須 使用 POST、PUT 或者 PATCH 請求方法。

Laravel 自動爲每個被應用管理的有效用戶會話生成一個 CSRF 「令牌」,該令牌用於驗證受權用戶和發起請求者是不是同一我的。想要生成包含 CSRF 令牌的隱藏輸入字段,可使用幫助函數 csrf_field 來實現:

<?php echo csrf_field(); ?> 

輔助函數 csrf_field 會生成以下 HTML:

<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">

固然還可使用 Blade 模板引擎提供的方式:

{!! csrf_field() !!}

你不須要本身編寫代碼去驗證 POST、PUT 或者 DELETE 請求的 CSRF 令牌,由於 Laravel 自帶的 HTTP 中間件 VerifyCsrfToken 會爲咱們作這項工做:將請求中輸入的 token 值和 Session 中的存儲的 token 做對比來進行驗證。

X-CSRF-Token
除了將 CSRF 令牌做爲 POST 參數進行驗證外,還能夠經過設置 X-CSRF-Token 請求頭來實現驗證,VerifyCsrfToken 中間件會檢查 X-CSRF-TOKEN 請求頭,首先建立一個 meta 標籤並將令牌保存到該 meta 標籤:

<meta name="csrf-token" content="{{ csrf_token() }}"> 

而後在 js 庫(如 jQuery)中添加該令牌到全部請求頭,這爲基於 AJAX 的應用提供了簡單、方便的方式來避免 CSRF 攻擊:

$.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } }); 

二十三. Laravel 程序優化

1. 配置信息緩存

生產環境中的 應該 使用『配置信息緩存』來加速 Laravel 配置信息的讀取。

使用如下 Artisan 自帶命令,把 config 文件夾裏全部配置信息合併到一個文件裏,減小運行時文件的載入數量:

php artisan config:cache 

緩存文件存放在 bootstrap/cache/ 文件夾中。

可使用如下命令來取消配置信息緩存:

php artisan config:clear 

注意:配置信息緩存不會隨着更新而自動重載,因此,開發時候建議關閉配置信息緩存,通常在生產環境中使用。能夠配合 Envoy 任務運行器 使用,在每次上線代碼時執行 config:clear 命令。

2. 路由緩存

生產環境中的 應該 使用『路由緩存』來加速 Laravel 的路由註冊。

路由緩存能夠有效的提升路由器的註冊效率,在大型應用程序中效果越加明顯,可使用如下命令:

php artisan route:cache 

緩存文件存放在 bootstrap/cache/ 文件夾中。另外,路由緩存不支持路由匿名函數編寫邏輯,詳見:文檔 - 路由緩存

可使用下面命令清除路由緩存:

php artisan route:clear 

注意:路由緩存不會隨着更新而自動重載,因此,開發時候建議關閉路由緩存,通常在生產環境中使用。能夠配合 Envoy 任務運行器 使用,在每次上線代碼時執行 route:clear 命令。

3. 類映射加載優化

optimize 命令把經常使用加載的類合併到一個文件裏,經過減小文件的加載,來提升運行效率。生產環境中的 應該使用 optimize 命令來優化類的加載速度:

php artisan optimize --force

以上命令會在 bootstrap/cache/ 文件夾中生成緩存文件。你能夠能夠經過修改 config/compile.php 文件來添加要合併的類。在 production 環境中,參數 --force 不須要指定,文件就會自動生成。

要清除類映射加載優化,請運行如下命令:

php artisan clear-compiled

此命令會刪除上面 optimize 生成的兩個文件。

注意:此命令要運行在 php artisan config:cache 後,由於 optimize 命令是根據配置信息(如:config/app.php 文件的 providers 數組)來生成文件的。

4. 自動加載優化

此命令不止針對於 Laravel 程序,適用於全部使用 composer 來構建的程序。此命令會把 PSR-0 和 PSR-4轉換爲一個類映射表,來提升類的加載速度。

composer dumpautoload -o

注意:php artisan optimize --force 命令裏已經作了這個操做。

5. 使用 Memcached 來存儲會話

每個 Laravel 的請求,都會產生會話,修改會話的存儲方式能有效提升程序效率。會話的配置文件是 config/session.php。生產環境中的 必須 使用 Memcached 或者 Redis 等專業的緩存軟件來存儲會話,應該 優先選擇 Memcached(注:爲了服務器方便管理,也能夠用redis):

'driver' => 'memcached', 

6. 使用專業緩存驅動器

「緩存」是提升應用程序運行效率的法寶之一,Laravel 默認緩存驅動是 file 文件緩存,生產環境中的 必須 使用專業的緩存系統,如 Redis 或者 Memcached。應該 優先考慮 Redis。應該 避免使用數據庫緩存。

'default' => 'redis', 

7. 數據庫請求優化

關聯模型數據讀取時 必須 使用 延遲預加載 和 預加載 。

臨近上線時 必須 使用 Laravel Debugbar 或者 Clockwork 留意每個頁面的總 SQL 請求條數,進行數據庫請求調優。

8. 爲數據集書寫緩存邏輯

應該 合理的使用 Laravel 提供的緩存層操做,把從數據庫裏面拿出來的數據集合進行緩存,減小數據庫的壓力,運行在內存上的專業緩存軟件對數據的讀取也遠遠快於數據庫。

$hot_posts = Cache::remember('posts.hot_posts', $minutes = 30, function() { return Post::getHotPosts(); }); 

remember 甚至連數據關聯模型也都一併緩存了。

9. 使用即時編譯器

能夠 使用 OpCache 進行優化。OpCache 都能輕輕鬆鬆的讓你的應用程序在不用作任何修改的狀況下,直接提升 50% 或者更高的性能,PHPhub 以前作個一個實驗,具體請見:使用 OpCache 提高 PHP 5.5+ 程序性能

二十四. 項目文檔編寫規範

1. 說明

每個項目都 必須 包含一個 readme.md 文件,readme 裏書寫這個項目的簡單信息。做用主要有兩個,一個是團隊新成員可今後文件中快速獲悉項目大體狀況,另外一個是部署項目時能夠做爲參考。

2. 排版規範

文檔頁面排版 必須 遵循 中文文案排版指北 ,在此基礎上:

  • 中文文檔請使用全角標點符號;
  • 必須 遵循 Markdown 語法,勿讓代碼顯示錯亂;
  • 原文中的雙引號(" ")請代換成中文的引號(『』符號怎麼打出來見 這裏)。
  • 全部的 「加亮」、「加粗」和「連接」都須要在左右保持一個空格。

3. 行文規範

readme.md 文檔 應該 包含如下內容:

  • 「項目概述」- 介紹說明項目的一些狀況,相似於簡單的產品說明,簡單的功能描述,項目相關連接等,500 字之內;
  • 「運行環境」- 運行環境說明,系統要求等信息;
  • 「開發環境部署/安裝」- 一步一步引導說明,保證項目新成員能最快速的,沒有歧義的部署好開發環境;
  • 「服務器架構說明」- 最好能有服務器架構圖,從用戶瀏覽器請求開始,包括後端緩存服務使用等都描述清楚(主要體現爲軟件的使用),配合「運行環境」區塊內容,可做爲線上環境部署的依據;
  • 「代碼上線」- 介紹代碼上線流程,須要執行哪些步驟;
  • 「擴展包說明」- 表格列出全部使用的擴展包,還有在哪些業務邏輯或者用例中使用了此擴展包;
  • 「自定義 Artisan 命令列表」- 以表格形式羅列出全部自定義的命令,說明用途,指出調用場景;
  • 「隊列列表」- 以表格形式羅列出項目全部隊列接口,說明用途,指出調用場景。

範例見 附錄:readme-example.md

一些額外補充

1. 一個方法作一件事情

一個方法作一件事情,儘可能爲每一塊邏輯用一個方法寫起來,不要把所有邏輯放在同一個方法,否則很難維護,別人閱讀起來也很吃力。

  • 錯誤的例子:
    PostsController.php
<?php namespace App\Controllers; use App\Http\Controllers\Controller; class PostsController extend Controller { public function doSomething (Request $request) { //檢查數據 //... //查詢文章是否存在 $article = Article::find($request->input('id')); if (!$article) { //... } //添加文章記錄 //... //添加用戶文章發佈日誌記錄 //... } } 
  • 正確的例子:
    PostsController.php
<?php namespace App\Controllers; use App\Http\Controllers\Controller; use App\Services\PostsService; class PostsController extend Controller { private $postsService; public function __construct (PostsService $posts) { $this->postsService = $posts; } /** * 添加文章 */ public function doSomething (Request $request) { try { $this->postsService->addArticle($request->input('user_id'), $request->input('data')); return response()->json([...]); } catch (\Exception $e) { //捕捉拋出的異常處理 //... } } } 

PostsService.php

<?php namespace App\Services; class PostsService { /** * 添加文章 */ public function addArticle ($userId, $data) { //檢查數據 $check = $this->checkForAddArticle($userId, $data); //添加文章記錄 $article = $this->doAddArticle($userId, $data); //添加用戶文章發佈日誌記錄 $log = $this->addUserPublishLog($userId, $article); //... } /** * 檢查數據 */ private function checkForAddArticle ($userId, $data) { //檢查失敗,拋出異常,由控制器catch //... } /** * 添加文章記錄 */ private function doAddArticle($userId, $data) { //... } /** * 添加用戶文章發佈日誌記錄 */ private function addUserPublishLog($userId, $article) { //... } } 

2. 儘可能要多作註釋說明

儘可能多作註釋,這樣別人也能看得懂,本身須要維護後者修復bug的時候,也比較好找

3. 將經常使用數值寫入新建的配置文件中

通常數據庫會有一些狀態位,如 orders 表有 order_status 字段用來記錄訂單狀態

`order_status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '訂單狀態: 0未付款 1已支付 2待配送 3派送中 4座位使用中 5已完成 6已取消 7超時未付款 8待退款 9已退款' 

那麼能夠將這些值寫入一個新建的配置文件,如 config/params.php 中

<?php return [ 'order_status' => [ 1 => 'unpaid', 2 => 'paid', 3 => 'wait_for_delivery', ... 9 => 'refunded' ], //other config ]; 

而後經過用 config() 函數讀取

4. 一個請求一個方法

不要用一個方法執行兩種類型的請求,get和post分別用不一樣的方法,不要經過以下去寫

if (!empty($_POST)) {} 

5. 數據操做

不要直接執行原生sql,使用laravel的creat、insert、update、save等方法,防止sql注入而且能夠過濾掉非法數據,最好使用Laravel的Eloquent ORM。不要使用如下寫法:

\DB::select("SELECT * FROM users WHERE id = 1");
相關文章
相關標籤/搜索