Laravel框架一:原理机制篇
Laravel作為在國內國外都頗為流行的PHP框架,風格優雅,其擁有自己的一些特點。
?
一. 請求周期
Laravel 采用了單一入口模式,應用的所有請求入口都是 public/index.php 文件。
? ??
二. 服務容器和服務提供者
服務容器是 Laravel?管理類依賴和運行依賴注入的有力工具,在類中可通過?$this->app?來訪問容器,在類之外通過?$app?來訪問容器;服務提供者是 Laravel 應用程序引導啟動的中心,關系到服務提供者自身、事件監聽器、路由以及中間件的啟動運行。應用程序中注冊的路由通過RouteServiceProvider實例來加載;事件監聽器在EventServiceProvider類中進行注冊;中間件又稱路由中間件,在app/Http/Kernel.php類文件中注冊,調用時與路由進行綁定。在新創建的應用中,AppServiceProvider 文件中方法實現都是空的,這個提供者是你添加應用專屬的引導和服務的最佳位置,當然,對于大型應用你可能希望創建幾個服務提供者,每個都具有粒度更精細的引導。服務提供者在 config/app.php 配置文件中的providers數組中進行注冊
<?phpnamespace 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?)。
?
四.?Artisan Console?
Laravel利用PHP的CLI構建了強大的Console工具artisan,artisan幾乎能夠創建任何你想要的模板類以及管理配置你的應用,在開發和運維管理中扮演著極其重要的角色,artisan是Laravel開發不可或缺的工具。在Laravel根目錄下運行:PHP artisan list可查看所有命令列表。用好artisan能極大地簡化開發工作,并減少錯誤發生的可能;另外,還可以編寫自己的命令。下面列舉部分比較常用的命令:
- 啟用維護模式:php artisan down?--message='Upgrading Database' --retry=60
- 關閉維護模式:php artisan up
- 生成路由緩存:php artisan route:cache
- 清除路由緩存:php artisan route:clear
- 數據庫遷移?Migrations:php artisan make:migration create_users_table --create=users
- 創建資源控制器:php artisan make:controller PhotoController --resource --model=Photo
- 創建模型及遷移:php artisan make:model User -m
?
五.?表單驗證機制
表單驗證在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 命令來生成事件和監聽器時他們會被自動創建。
事件訂閱者:事件訂閱者允許在單個類中定義多個事件處理器,還應該定義一個 subscribe 方法,這個方法接受一個事件分發器的實例,通過調用事件分發器的 listen 方法來注冊事件監聽器,然后在 EventServiceProvider 類的 $subscribe 屬性中注冊訂閱者
1 <?php2 3 namespace App\Listeners;4 5 class UserEventSubscriber6 {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 模型約定:
訪問器和修改器:訪問器(getFooAttribute)和修改器(setFooAttribute)可以讓你修改 Eloquent 模型中的屬性或者設置它們的值,比如你想要使用 Laravel 加密器來加密一個被保存在數據庫中的值,當你從 Eloquent 模型訪問該屬性時該值將被自動解密。訪問器和修改器要遵循cameCase命名規范,修改器會設置值到 Eloquent 模型內部的?$attributes?屬性上
1 <?php2 3 namespace App;4 5 use Illuminate\Database\Eloquent\Model;6 7 class User extends Model8 {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;//將自動調用相應的訪問器?序列化: 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 //單個模型實例序列化成JSON9 $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 });Eloquent 模型支持多種類型的關聯:一對一、一對多、多對多、遠層一對多、多態關聯、多態多對多關聯
舉個例子,一個 User 模型會關聯一個 Phone 模型,一對一關聯(hasOne) 1 <?php2 3 namespace App;4 5 use Illuminate\Database\Eloquent\Model;6 7 class User extends Model8 {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 <?php2 3 namespace App;4 5 use Illuminate\Database\Eloquent\Model;6 7 class Phone extends Model8 {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 <?php2 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 <?php3 4 namespace App\Observers;5 6 use App\User;7 8 class UserObserver9 { 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 ”字樣,方知它們只是用來展現表單而非提交表單的。
?
九. 擴展開發
我們知道,Laravel本身是基于Composer管理的一個包,遵循Composer的相關規范,可以通過Composer來添加所依賴的其他Composer包,因此在做應用的擴展開發時,可以開發Composer包然后引入項目中即可;另外也可開發基于Laravel的專屬擴展包。下面所講的就是Laravel的專屬擴展開發,最好的方式是使用 contracts ,而不是 facades,因為你開發的包并不能訪問所有 Laravel 提供的測試輔助函數,模擬 contracts 要比模擬 facade 簡單很多。
- 服務提供者:服務提供者是你的擴展包與 Laravel 連接的重點,須定義自己的服務提供者并繼承自?Illuminate\Support\ServiceProvider 基類
- 路由:若要為你的擴展包定義路由,只需在包的服務提供者的 boot 方法中傳遞 routes 文件路徑到 loadRoutesFrom 方法即可 1 /** 2 * 在注冊后進行服務的啟動。 3 * 4 * @return void 5 */ 6 public function boot() 7 { 8 $this->loadRoutesFrom(__DIR__.'/path/to/routes.php'); 9 }
- 配置文件:你可以選擇性地將擴展包的配置文件發布(publishes)到應用程序本身的config目錄上或者合并(mergeConfigFrom)到應用程序里的副本配置文件中,但不應在配置文件中定義閉包函數,當執行 config:cache Artisan命令時,它們將不能正確地序列化 1 /**2 * 在注冊后進行服務的啟動。3 *4 * 用戶使用 vendor:publish 命令可將擴展包的文件將會被復制到指定的位置上。5 *6 * @return void7 */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 }
- 數據庫遷移:如果你的擴展包包含數據庫遷移,需要使用 loadMigrationsFrom 方法告知 Laravel 如何去加載它們。在運行 php artisan migrate 命令時,它們就會自動被執行,不需要把它們導出到應用程序的 database/migrations 目錄 1 /** 2 * 在注冊后進行服務的啟動。 3 * 4 * @return void 5 */ 6 public function boot() 7 { 8 $this->loadMigrationsFrom(__DIR__.'/path/to/migrations'); 9 }
- 語言包:如果你的擴展包里面包含了本地化,則可以使用 loadTranslationsFrom 方法來告知 Laravel 該如何加載它們。下例假設你的包名稱為courier 1 /**2 * 在注冊后進行服務的啟動。3 *4 * @return void5 */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 void5 */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 void5 */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 void5 */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 void5 */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部
轉載于:https://www.cnblogs.com/xuqp/p/7285161.html
總結
以上是生活随笔為你收集整理的Laravel框架一:原理机制篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: halcon直线标定板对相机标定的效果评
- 下一篇: CCF 2022:DPU评测技术白皮书发