Laravel做爲在國內國外都頗爲流行的PHP框架,風格優雅,其擁有本身的一些特色,且也發佈長期支持版(LTS)。php
一. 請求週期laravel
Laravel 採用了單一入口模式,應用的全部請求入口都是 public/index.php 文件。web
關於中間件:
(1)中間件比如一個過濾層,多箇中間件就是多個過濾層,且它們有前後順序。
(2)全局中間件可分發前執行,也可分發後執行;中間件組使用組key(如'web'、'api')來調用middleware(key)執行,中間件組僅僅是爲了使一次將多箇中間件指定給路由變得更加方便;路由中間件在(自定義)路由分發中或分發後執行,也是經過key(如'auth')來調用middleware(key)執行的。
(3)你也能夠自定義前置或後置中間件,它們的差異在於在請求執行前仍是執行後執行自定義動做。數據庫namespace App\Http\Middleware; use Closure; class AfterMiddleware { public function handle($request, Closure $next) { $response = $next($request); // Perform action return $response; } }
(4)若是你想在內核 handle 和 內核 terminate 時使用同一個中間件實例,可以使用容器的 singleton 方法向容器註冊中間件。bootstrap
二. 服務容器和服務提供者api
服務容器是 Laravel 管理類依賴和運行依賴注入的有力工具,在類中可經過 $this->app 來訪問容器,在類以外經過 $app 來訪問容器;服務提供者是 Laravel 應用程序引導啓動的中心,關係到服務提供者自身、事件監聽器、路由的啓動運行。由於應用程序中註冊的路由經過RouteServiceProvider實例來加載,而事件監聽器在EventServiceProvider類中進行註冊。在新建立的應用中,AppServiceProvider 文件中方法實現都是空的,這個提供者是你添加應用專屬的引導和服務的最佳位置,固然,對於大型應用你可能但願建立幾個服務提供者,每一個都具備粒度更精細的引導。服務提供者在 config/app.php 配置文件中的providers數組中進行註冊數組
<?php namespace App\Providers; use Riak\Connection; use Illuminate\Support\ServiceProvider; class RiakServiceProvider extends ServiceProvider { /** * 在容器中註冊綁定 * * @return void */ public function register() { $this->app->singleton(Connection::class, function ($app) { return new Connection(config('riak')); }); } }
三. 依賴注入緩存
Laravel 實現依賴注入方式有兩種:自動注入和主動註冊。自動注入經過參數類型提示由服務容器自動注入實現;主動註冊則需開發人員經過綁定機制來實現,即綁定服務提供者或類(參考: http://d.laravel-china.org/docs/5.4/container )。服務器
use Illuminate\Support\Facades\Storage; use App\Http\Controllers\PhotoController; use App\Http\Controllers\VideoController; use Illuminate\Contracts\Filesystem\Filesystem; $this->app->when(PhotoController::class) ->needs(Filesystem::class) ->give(function () { return Storage::disk('local'); }); $this->app->when(VideoController::class) ->needs(Filesystem::class) ->give(function () { return Storage::disk('s3'); });
<?php namespace App\Http\Controllers; use App\Users\Repository as UserRepository; class UserController extends Controller { /** * user repository 實例。 */ protected $users; /** * 控制器構造方法。 * * @param UserRepository $users * @return void */ public function __construct(UserRepository $users) { $this->users = $users; } /** * 儲存一個新用戶。 * * @param Request $request * @return Response */ public function store(Request $request) { $name = $request->input('name'); // } }
你的路由多是這樣定義的: Route::put('user/{id}', 'UserController@update'); 而控制器對路由參數id的依賴卻多是這樣實現的: <?php namespace App\Http\Controllers; use Illuminate\Http\Request; class UserController extends Controller { /** * 更新指定的用戶。 * * @param Request $request * @param string $id * @return Response */ public function update(Request $request, $id) { // } }
四. Artisan Console 閉包
Laravel利用PHP的CLI構建了強大的Console工具artisan,artisan幾乎可以建立任何你想要的模板類以及管理配置你的應用,在開發和運維管理中扮演着極其重要的角色,artisan是Laravel開發不可或缺的工具。在Laravel根目錄下運行:PHP artisan list可查看全部命令列表。用好artisan能極大地簡化開發工做,並減小錯誤發生;另外,還能夠編寫本身的命令。下面列舉部分比較經常使用的命令:
五. 表單驗證機制
表單驗證在web開發中是不可或缺的,其重要性也不言而喻,也算是每一個web框架的標配部件了。Laravel表單驗證擁有標準且龐大的規則集,經過規則調用來完成數據驗證,多個規則組合調用須以「|」符號鏈接,一旦驗證失敗將自動回退並可自動綁定視圖。
下例中,附加bail規則至title屬性,在第一次驗證required失敗後將當即中止驗證;「.」語法符號在Laravel中一般表示嵌套包含關係,這個在其餘語言或框架語法中也比較常見
$this->validate($request, [ 'title' => 'bail|required|unique:posts|max:255', 'author.name' => 'required', 'author.description' => 'required', ]);
Laravel驗證規則參考 http://d.laravel-china.org/docs/5.4/validation#可用的驗證規則 ;另外,在Laravel開發中還可採用以下擴展規則:
六. 事件機制
Laravel事件機制是一種很好的應用解耦方式,由於一個事件能夠擁有多個互不依賴的監聽器。事件類 (Event) 類一般保存在 app/Events
目錄下,而它們的監聽類 (Listener) 類被保存在 app/Listeners
目錄下,使用 Artisan 命令來生成事件和監聽器時他們會被自動建立。
1 <?php 2 3 namespace App\Events; 4 5 use App\Order; 6 use Illuminate\Queue\SerializesModels; 7 8 class OrderShipped 9 { 10 use SerializesModels; 11 12 public $order; 13 14 /** 15 * 建立一個事件實例。 16 * 17 * @param Order $order 18 * @return void 19 */ 20 public function __construct(Order $order) 21 { 22 $this->order = $order; 23 } 24 }
1 <?php 2 3 namespace App\Listeners; 4 5 use App\Events\OrderShipped; 6 7 class SendShipmentNotification 8 { 9 /** 10 * 建立事件監聽器。 11 * 12 * @return void 13 */ 14 public function __construct() 15 { 16 // 17 } 18 19 /** 20 * 處理事件 21 * 22 * @param OrderShipped $event 23 * @return void 24 */ 25 public function handle(OrderShipped $event) 26 { 27 // 使用 $event->order 來訪問 order ... 28 } 29 }
handle
方法中返回 false
來中止事件傳播到其餘的監聽器1 <?php 2 3 namespace App\Http\Controllers; 4 5 use App\Order; 6 use App\Events\OrderShipped; 7 use App\Http\Controllers\Controller; 8 9 class OrderController extends Controller 10 { 11 /** 12 * 將傳遞過來的訂單發貨。 13 * 14 * @param int $orderId 15 * @return Response 16 */ 17 public function ship($orderId) 18 { 19 $order = Order::findOrFail($orderId); 20 21 // 訂單的發貨邏輯... 22 23 event(new OrderShipped($order)); 24 } 25 }
$connection
和 $queue
屬性;若是隊列監聽器任務執行次數超過在工做隊列中定義的最大嘗試次數,監聽器的 failed 方法將會被自動調用
1 <?php 2 3 namespace App\Listeners; 4 5 use App\Events\OrderShipped; 6 use Illuminate\Contracts\Queue\ShouldQueue; 7 8 class SendShipmentNotification implements ShouldQueue 9 { 10 /** 11 * 隊列化任務使用的鏈接名稱。 12 * 13 * @var string|null 14 */ 15 public $connection = 'sqs'; 16 17 /** 18 * 隊列化任務使用的隊列名稱。 19 * 20 * @var string|null 21 */ 22 public $queue = 'listeners'; 23 24 public function failed(OrderShipped $event, $exception) 25 { 26 // 27 } 28 }
事件訂閱者:事件訂閱者容許在單個類中定義多個事件處理器,還應該定義一個 subscribe 方法,這個方法接受一個事件分發器的實例,經過調用事件分發器的 listen 方法來註冊事件監聽器,而後在 EventServiceProvider 類的 $subscribe 屬性中註冊訂閱者
1 <?php 2 3 namespace App\Listeners; 4 5 class UserEventSubscriber 6 { 7 /** 8 * 處理用戶登陸事件。 9 */ 10 public function onUserLogin($event) {} 11 12 /** 13 * 處理用戶註銷事件。 14 */ 15 public function onUserLogout($event) {} 16 17 /** 18 * 爲訂閱者註冊監聽器。 19 * 20 * @param Illuminate\Events\Dispatcher $events 21 */ 22 public function subscribe($events) 23 { 24 $events->listen( 25 'Illuminate\Auth\Events\Login', 26 'App\Listeners\UserEventSubscriber@onUserLogin' 27 ); 28 29 $events->listen( 30 'Illuminate\Auth\Events\Logout', 31 'App\Listeners\UserEventSubscriber@onUserLogout' 32 ); 33 } 34 35 }
七. Eloquent 模型
Eloquent ORM 以ActiveRecord形式來和數據庫進行交互,擁有所有的數據表操做定義,單個模型實例對應數據表中的一行
1 $flights = App\Flight::where('active', 1) 2 ->orderBy('name', 'desc') 3 ->take(10) 4 ->get();
config/database.php中包含了模型的相關配置項。Eloquent 模型約定:
1 // 用屬性取回航班,當結果不存在時建立它... 2 $flight = App\Flight::firstOrCreate(['name' => 'Flight 10']); 3 4 // 用屬性取回航班,當結果不存在時實例化一個新實例... 5 $flight = App\Flight::firstOrNew(['name' => 'Flight 10']);
1 <?php 2 3 namespace App; 4 5 use Illuminate\Database\Eloquent\Model; 6 use Illuminate\Database\Eloquent\SoftDeletes; 7 8 class Flight extends Model 9 { 10 use SoftDeletes; 11 12 /** 13 * 須要被轉換成日期的屬性。 14 * 15 * @var array 16 */ 17 protected $dates = ['deleted_at']; 18 }
scope
前綴)。做用域老是返回查詢構建器
1 全局做用域定義: 2 <?php 3 4 namespace App\Scopes; 5 6 use Illuminate\Database\Eloquent\Scope; 7 use Illuminate\Database\Eloquent\Model; 8 use Illuminate\Database\Eloquent\Builder; 9 10 class AgeScope implements Scope 11 { 12 /** 13 * 應用做用域 14 * 15 * @param \Illuminate\Database\Eloquent\Builder $builder 16 * @param \Illuminate\Database\Eloquent\Model $model 17 * @return void 18 */ 19 public function apply(Builder $builder, Model $model) 20 { 21 return $builder->where('age', '>', 200); 22 } 23 } 24 25 本地做用域: 26 <?php 27 28 namespace App; 29 30 use Illuminate\Database\Eloquent\Model; 31 32 class User extends Model 33 { 34 /** 35 * 限制查詢只包括受歡迎的用戶。 36 * 37 * @return \Illuminate\Database\Eloquent\Builder 38 */ 39 public function scopePopular($query) 40 { 41 return $query->where('votes', '>', 100); 42 } 43 44 /** 45 * 限制查詢只包括活躍的用戶。 46 * 47 * @return \Illuminate\Database\Eloquent\Builder 48 */ 49 public function scopeActive($query) 50 { 51 return $query->where('active', 1); 52 } 53 } 54 55 動態範圍: 56 <?php 57 58 namespace App; 59 60 use Illuminate\Database\Eloquent\Model; 61 62 class User extends Model 63 { 64 /** 65 * 限制查詢只包括指定類型的用戶。 66 * 67 * @return \Illuminate\Database\Eloquent\Builder 68 */ 69 public function scopeOfType($query, $type) 70 { 71 return $query->where('type', $type); 72 } 73 }
1 <?php 2 3 namespace App; 4 5 use Illuminate\Database\Eloquent\Model; 6 7 class User extends Model 8 { 9 /** 10 * 在數組中可見的屬性。 11 * 12 * @var array 13 */ 14 protected $visible = ['first_name', 'last_name']; 15 } 16 ?> 17 18 //makeVisible()用來臨時修改可見性 19 return $user->makeVisible('attribute')->toArray();
訪問器和修改器:訪問器(getFooAttribute)和修改器(setFooAttribute)可讓你修改 Eloquent 模型中的屬性或者設置它們的值,好比你想要使用 Laravel 加密器來加密一個被保存在數據庫中的值,當你從 Eloquent 模型訪問該屬性時該值將被自動解密。訪問器和修改器要遵循cameCase命名規範,修改器會設置值到 Eloquent 模型內部的 $attributes
屬性上
1 <?php 2 3 namespace App; 4 5 use Illuminate\Database\Eloquent\Model; 6 7 class User extends Model 8 { 9 /** 10 * 獲取用戶的名字。 11 * 12 * @param string $value 13 * @return string 14 */ 15 public function getFirstNameAttribute($value) 16 { 17 return ucfirst($value); 18 } 19 20 /** 21 * 設定用戶的名字。 22 * 23 * @param string $value 24 * @return void 25 */ 26 public function setFirstNameAttribute($value) 27 { 28 $this->attributes['first_name'] = strtolower($value); 29 } 30 }
而對於訪問器與修改器的調用將是模型對象自動進行的
1 $user = App\User::find(1); 2 $user->first_name = 'Sally';//將自動調用相應的修改器 3 $firstName = $user->first_name;//將自動調用相應的訪問器
1 <?php 2 3 namespace App; 4 5 use Illuminate\Database\Eloquent\Model; 6 7 class User extends Model 8 { 9 /** 10 * 訪問器被附加到模型數組的形式。 11 * 12 * @var array 13 */ 14 protected $appends = ['is_admin']; 15 16 /** 17 * 爲用戶獲取管理者的標記。 18 * 19 * @return bool 20 */ 21 public function getIsAdminAttribute() 22 { 23 return $this->attributes['admin'] == 'yes'; 24 } 25 }
1 <?php 2 3 namespace App; 4 5 use Illuminate\Database\Eloquent\Model; 6 7 class User extends Model 8 { 9 /** 10 * 應該被轉換成原生類型的屬性。 11 * 12 * @var array 13 */ 14 protected $casts = [ 15 'is_admin' => 'boolean',//is_admin 屬性以整數(0 或 1)被保存在咱們的數據庫中,把它轉換爲布爾值 16 ]; 17 }
序列化: Laravel模型及關聯可遞歸序列化成數組或JSON
1 //單個模型實例序列化成數組 2 $user = App\User::with('roles')->first(); 3 return $user->toArray(); 4 //集合序列化成數組 5 $users = App\User::all(); 6 return $users->toArray(); 7 8 //單個模型實例序列化成JSON 9 $user = App\User::find(1); 10 return $user->toJson(); 11 //直接進行string轉換會將模型或集合序列化成JSON 12 $user = App\User::find(1); 13 return (string) $user; 14 //所以你能夠直接從應用程序的路由或者控制器中返回 Eloquent 對象 15 Route::get('users', function () { 16 return App\User::all(); 17 });
1 $user->posts()->where('active', 1)->get();
Eloquent 模型支持多種類型的關聯:一對1、一對多、多對多、遠層一對多、多態關聯、多態多對多關聯
舉個例子,一個 User 模型會關聯一個 Phone 模型,一對一關聯(hasOne)1 <?php 2 3 namespace App; 4 5 use Illuminate\Database\Eloquent\Model; 6 7 class User extends Model 8 { 9 /** 10 * 獲取與用戶關聯的電話號碼 11 */ 12 public function phone() 13 { 14 return $this->hasOne('App\Phone'); 15 } 16 }
動態屬性容許你訪問關聯方法,使用 Eloquent 的動態屬性來獲取關聯記錄,如同他們是定義在模型中的屬性
1 $phone = User::find(1)->phone;
Eloquent 會假設對應關聯的外鍵名稱是基於模型名稱的。在這個例子裏,它會自動假設 Phone 模型擁有 user_id 外鍵。若是你想要重寫這個約定,則能夠傳入第二個參數到 hasOne 方法裏
1 return $this->hasOne('App\Phone', 'foreign_key');
若是你想讓關聯使用 id 之外的值,則能夠傳遞第三個參數至 hasOne 方法來指定你自定義的鍵
1 return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
若是咱們要在 Phone 模型上定義一個反向關聯,此關聯可以讓咱們訪問擁有此電話的 User 模型。咱們能夠定義與 hasOne 關聯相對應的 belongsTo 方法
1 <?php 2 3 namespace App; 4 5 use Illuminate\Database\Eloquent\Model; 6 7 class Phone extends Model 8 { 9 /** 10 * 獲取擁有該電話的用戶模型。 11 */ 12 public function user() 13 { 14 return $this->belongsTo('App\User'); 15 } 16 }
模型事件: Laravel爲模型定義的事件包括creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored。 模型上定義一個 $events
屬性
1 <?php 2 3 namespace App; 4 5 use App\Events\UserSaved; 6 use App\Events\UserDeleted; 7 use Illuminate\Notifications\Notifiable; 8 use Illuminate\Foundation\Auth\User as Authenticatable; 9 10 class User extends Authenticatable 11 { 12 use Notifiable; 13 14 /** 15 * 模型的時間映射。 16 * 17 * @var array 18 */ 19 protected $events = [ 20 'saved' => UserSaved::class, 21 'deleted' => UserDeleted::class, 22 ]; 23 }
若是你在一個給定的模型中監聽許多事件,也可以使用觀察者將全部監聽器變成一個類,類的一個方法就是一個事件監聽器
1 定義觀察者: 2 <?php 3 4 namespace App\Observers; 5 6 use App\User; 7 8 class UserObserver 9 { 10 /** 11 * 監聽用戶建立的事件。 12 * 13 * @param User $user 14 * @return void 15 */ 16 public function created(User $user) 17 { 18 // 19 } 20 21 /** 22 * 監聽用戶刪除事件。 23 * 24 * @param User $user 25 * @return void 26 */ 27 public function deleting(User $user) 28 { 29 // 30 } 31 } 32 33 註冊觀察者: 34 <?php 35 36 namespace App\Providers; 37 38 use App\User; 39 use App\Observers\UserObserver; 40 use Illuminate\Support\ServiceProvider; 41 42 class AppServiceProvider extends ServiceProvider 43 { 44 /** 45 * 運行全部應用. 46 * 47 * @return void 48 */ 49 public function boot() 50 { 51 User::observe(UserObserver::class); 52 } 53 54 /** 55 * 註冊服務提供. 56 * 57 * @return void 58 */ 59 public function register() 60 { 61 // 62 } 63 }
八. Laravel的Restful風格
通常認爲Restful風格的資源定義不包含操做,可是在Laravel中操做(動詞)也可做爲一種資源來定義。下圖是對Laravel中資源控制器操做原理的描述,能夠看到,create、edit就直接出如今了URI中,它們是一種合法的資源。對於create和edit這兩種資源的訪問都採用GET方法來實現,第一眼看到頓感奇怪,後來嘗試經過artisan console生成資源控制器,並注意到其對create、edit給出註釋「 Show the form for 」字樣,方知它們只是用來展示表單而非提交表單的。
關於POST與PUT方法的差別的討論,多認爲它們均可用於建立和修改數據,主要在於POST是非冪等操做而PUT是冪等操做。
九. 擴展開發
咱們知道,Laravel自己是基於Composer管理的一個包,遵循Composer的相關規範,能夠經過Composer來添加所依賴的其餘Composer包,所以在作應用的擴展開發時,能夠開發Composer包而後引入項目中便可;另外也可開發基於Laravel的專屬擴展包。下面所講的就是Laravel的專屬擴展開發,最好的方式是使用 contracts ,而不是 facades,由於你開發的包並不能訪問全部 Laravel 提供的測試輔助函數,模擬 contracts 要比模擬 facade 簡單不少。
1 /** 2 * 在註冊後進行服務的啓動。 3 * 4 * @return void 5 */ 6 public function boot() 7 { 8 $this->loadRoutesFrom(__DIR__.'/path/to/routes.php'); 9 }
1 /** 2 * 在註冊後進行服務的啓動。 3 * 4 * 用戶使用 vendor:publish 命令可將擴展包的文件將會被複制到指定的位置上。 5 * 6 * @return void 7 */ 8 public function boot() 9 { 10 $this->publishes([ 11 __DIR__.'/path/to/config/courier.php' => config_path('courier.php'), 12 ]); 13 } 14 15 $value = config('courier.option');//只要你的配置文件被髮布,就能夠如其它配置文件同樣被訪問 16 17 /** 18 * 或者選擇性在容器中註冊綁定。 19 * 20 * 此方法僅合併配置數組的第一級。若是您的用戶部分定義了多維配置數組,則不會合並缺失的選項 21 * 22 * @return void 23 */ 24 public function register() 25 { 26 $this->mergeConfigFrom( 27 __DIR__.'/path/to/config/courier.php', 'courier' 28 ); 29 }
1 /** 2 * 在註冊後進行服務的啓動。 3 * 4 * @return void 5 */ 6 public function boot() 7 { 8 $this->loadMigrationsFrom(__DIR__.'/path/to/migrations'); 9 }
1 /** 2 * 在註冊後進行服務的啓動。 3 * 4 * @return void 5 */ 6 public function boot() 7 { 8 $this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier'); 9 10 //若是不想發佈語言包至應用程序的 resources/lang/vendor 目錄,請註銷對$this->publishes()調用。運行 Laravel 的 vendor:publish Artisan 命令可將擴展包的語言包複製到指定的位置上 11 $this->publishes([ 12 __DIR__.'/path/to/translations' => resource_path('lang/vendor/courier'), 13 ]); 14 } 15 16 echo trans('courier::messages.welcome');//擴展包翻譯參照使用了雙分號 package::file.line 語法
視圖:若要在 Laravel 中註冊擴展包 視圖,則必須告訴 Laravel 你的視圖位置,loadViewsFrom 方法容許傳遞視圖模板路徑與擴展包名稱兩個參數。須要特別指出的是,當你使用 loadViewsFrom 方法時,Laravel 實際上爲你的視圖註冊了兩個位置:一個是應用程序的 resources/views/vendor 目錄,另外一個是你所指定的目錄。Laravel會先檢查 resources/views/vendor 目錄是否存在待加載視圖,若是不存在,纔會從指定的目錄去加載,這個方法可讓用戶很方便的自定義或重寫擴展包視圖。
1 /** 2 * 在註冊後進行服務的啓動。 3 * 4 * @return void 5 */ 6 public function boot() 7 { 8 $this->loadViewsFrom(__DIR__.'/path/to/views', 'courier'); 9 10 //若要發佈擴展包的視圖至 resources/views/vendor 目錄,則必須使用服務提供者的 publishes 方法。運行 Laravel 的 vendor:publish Artisan 命令時,擴展包的視圖將會被複制到指定的位置上 11 $this->publishes([ 12 __DIR__.'/path/to/views' => resource_path('views/vendor/courier'), 13 ]); 14 } 15 16 //擴展包視圖參照使用了雙分號 package::view 語法 17 Route::get('admin', function () { 18 return view('courier::admin'); 19 });
命令:使用 commands 方法給擴展包註冊 Artisan 命令,命令的定義要遵循Laravel Artisan 命令規範
1 /** 2 * 在註冊後進行服務的啓動。 3 * 4 * @return void 5 */ 6 public function boot() 7 { 8 if ($this->app->runningInConsole()) { 9 $this->commands([ 10 FooCommand::class, 11 BarCommand::class, 12 ]); 13 } 14 }
公用 Assets:你能夠發佈像 JavaScript、CSS 和圖片這些資源文件到應用程序的 public
目錄上。當用戶執行 vendor:publish
命令時,您的 Assets 將被複制到指定的發佈位置。因爲每次更新包時一般都須要覆蓋資源,所以您可使用 --force
標誌:php artisan vendor:publish --tag=public --force
1 /** 2 * 在註冊後進行服務的啓動。 3 * 4 * @return void 5 */ 6 public function boot() 7 { 8 $this->publishes([ 9 __DIR__.'/path/to/assets' => public_path('vendor/courier'), 10 ], 'public'); 11 }
發佈羣組文件:你可能想讓用戶不用發佈擴展包的全部資源文件,只須要單獨發佈擴展包的配置文件便可,經過在調用 publishes
方法時使用標籤來實現
1 /** 2 * 在註冊後進行服務的啓動。 3 * 4 * @return void 5 */ 6 public function boot() 7 { 8 $this->publishes([ 9 __DIR__.'/../config/package.php' => config_path('package.php') 10 ], 'config'); 11 12 $this->publishes([ 13 __DIR__.'/../database/migrations/' => database_path('migrations') 14 ], 'migrations'); 15 }
對於上例運行命令 php artisan vendor:publish --tag=config 時將忽略掉migrations部分