laravel 原理機制

Laravel框架一:原理機制篇

Laravel作為在國內(nèi)國外都頗為流行的PHP框架逢慌,風格優(yōu)雅,其擁有自己的一些特點袍患,且也發(fā)布長期支持版(LTS)坦康。


一. 請求周期

  Laravel 采用了單一入口模式,應用的所有請求入口都是 public/index.php 文件诡延。

1滞欠、注冊類文件自動加載器:Laravel通過composer進行依賴管理,并在bootstrap/autoload.php中注冊了Composer Auto Loader (PSR-4)孕暇,應用中類的命名空間將被映射到類文件實際路徑仑撞,不再需要開發(fā)者手動導入各種類文件,而由自動加載器自行導入妖滔。因此隧哮,Laravel允許你在應用中定義的類可以自由放置在Composer Auto Loader能自動加載的任何目錄下,但大多數(shù)時候還是建議放置在app目錄下或app的某個子目錄下座舍。

2沮翔、創(chuàng)建服務容器:從 bootstrap/app.php 文件中取得 Laravel 應用實例?$app(服務容器)。

3曲秉、創(chuàng)建 HTTP / Console 內(nèi)核:傳入的請求在HTTP / Console 內(nèi)核中進行預處理采蚀。HTTP 內(nèi)核繼承自 Illuminate\Foundation\Http\Kernel 類,其中注入了?$app?和?$router?兩個實例承二,內(nèi)核是完成應用引導榆鼠、請求處理(包括通過Router轉(zhuǎn)發(fā)請求等)的場所。HTTP Kernel 定義了一個 bootstrappers 數(shù)組亥鸠,配置了環(huán)境變量加載妆够、應用配置加載识啦、錯誤處理,以及其他在請求被處理前需要完成的工作神妹。

4颓哮、載入服務提供者至容器:在內(nèi)核引導啟動的過程中最重要的動作之一就是載入服務提供者到你的?$app?(即所有的服務提供者都要掛載到服務容器下去執(zhí)行),服務提供者負責引導啟動框架的全部各種組件鸵荠,例如數(shù)據(jù)庫冕茅、隊列、驗證器以及路由組件蛹找。因為這些組件引導和配置了框架的各種功能姨伤,所以服務提供者是整個 Laravel 啟動過程中最為重要的部分,所有的服務提供者都配置在 config/app.php 文件中的 providers 數(shù)組中熄赡。首先姜挺,所有提供者的 register 方法會被調(diào)用齿税;一旦所有提供者注冊完成彼硫,接下來,boot 方法將會被調(diào)用凌箕。/

5拧篮、分發(fā)請求:一旦應用完成引導和所有服務提供者都注冊完成,Request 將會移交給Router進行分發(fā)牵舱。在通過Router轉(zhuǎn)發(fā)請求時串绩,所有請求都必須先經(jīng)過全局HTTP中間件棧的處理,再調(diào)度到Router并獲得其回調(diào)芜壁,然后執(zhí)行該回調(diào)礁凡。

關(guān)于中間件

????(1)中間件好比一個過濾層,多個中間件就是多個過濾層慧妄,且它們有先后順序顷牌。

????(2)全局中間件可分發(fā)前執(zhí)行,也可分發(fā)后執(zhí)行塞淹;中間件組使用組key(如'web'窟蓝、'api')來調(diào)用middleware(key)執(zhí)行,中間件組僅僅是為了使一次將多個中間件指定給路由變得更加方便饱普;路由中間件在(自定義)路由分發(fā)中或分發(fā)后執(zhí)行运挫,也是通過key(如'auth')來調(diào)用middleware(key)執(zhí)行的。

????(3)你也可以自定義前置或后置中間件套耕,它們的差別在于在請求執(zhí)行前還是執(zhí)行后執(zhí)行自定義動作谁帕。

namespace App\Http\Middleware;

useClosure;

class AfterMiddleware

{

????public function handle($request, Closure$next)

????{

????????$response=$next($request);

????????//Perform action

????????return$response;

????}

}


(4)如果你想在內(nèi)核 handle 和 內(nèi)核 terminate 時使用同一個中間件實例,可使用容器的 singleton 方法向容器注冊中間件冯袍。

發(fā)送響應并結(jié)束:由Response發(fā)送響應匈挖,然后由內(nèi)核發(fā)出terminate,包括調(diào)用可終止的中間件(定義了terminate方法的全局HTTP中間件和路由中間件)、?$app?服務容器終止关划。


二. 服務容器和服務提供者

  服務容器是 Laravel?管理類依賴和運行依賴注入的有力工具小染,在類中可通過?$this->app?來訪問容器,在類之外通過?$app?來訪問容器贮折;服務提供者是 Laravel 應用程序引導啟動的中心裤翩,關(guān)系到服務提供者自身、事件監(jiān)聽器调榄、路由的啟動運行踊赠。因為應用程序中注冊的路由通過RouteServiceProvider實例來加載,而事件監(jiān)聽器在EventServiceProvider類中進行注冊每庆。在新創(chuàng)建的應用中筐带,AppServiceProvider 文件中方法實現(xiàn)都是空的,這個提供者是你添加應用專屬的引導和服務的最佳位置缤灵,當然伦籍,對于大型應用你可能希望創(chuàng)建幾個服務提供者,每個都具有粒度更精細的引導腮出。服務提供者在 config/app.php 配置文件中的providers數(shù)組中進行注冊


namespace App\Providers;

use????Riak\Connection;

use????Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider

{

/**

* 在容器中注冊綁定

*

* @return void

*/

publicfunctionregister()

{

$this->app->singleton(Connection::class,function($app) {

returnnewConnection(config('riak'));

});

}

}


三. 依賴注入

  Laravel 實現(xiàn)依賴注入方式有兩種:自動注入和主動注冊帖鸦。自動注入通過參數(shù)類型提示由服務容器自動注入實現(xiàn);主動注冊則需開發(fā)人員通過綁定機制來實現(xiàn)胚嘲,即綁定服務提供者或類(參考:?http://d.laravel-china.org/docs/5.4/container?)作儿。

綁定服務提供者或類:這種方式對依賴注入的實現(xiàn)可以非常靈活多樣

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');

});

參數(shù)類型聲明:通過對類的構(gòu)造器參數(shù)類型、類的方法參數(shù)類型馋劈、閉包的參數(shù)類型給出提示來實現(xiàn)

namespace App\Http\Controllers;

useApp\Users\RepositoryasUserRepository;

classUserControllerextendsController

{

/**

* user repository 實例攻锰。

*/

protected$users;

/**

* 控制器構(gòu)造方法。

*

* @param UserRepository $users

* @return void

*/

publicfunction__construct(UserRepository$users)

{

$this->users =$users;

}

/**

* 儲存一個新用戶妓雾。

*

* @param Request $request

* @return Response

*/

publicfunctionstore(Request$request)

{

$name=$request->input('name');

//

}

}

路由參數(shù)依賴:下邊的示例使用 Illuminate\Http\Request 類型提示的同時還獲取到路由參數(shù)id

你的路由可能是這樣定義的:

Route::put('user/{id}', 'UserController@update');

而控制器對路由參數(shù)id的依賴卻可能是這樣實現(xiàn)的:

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構(gòu)建了強大的Console工具artisan,artisan幾乎能夠創(chuàng)建任何你想要的模板類以及管理配置你的應用君珠,在開發(fā)和運維管理中扮演著極其重要的角色寝志,artisan是Laravel開發(fā)不可或缺的工具。在Laravel根目錄下運行:PHP artisan list可查看所有命令列表策添。用好artisan能極大地簡化開發(fā)工作材部,并減少錯誤發(fā)生;另外唯竹,還可以編寫自己的命令乐导。下面列舉部分比較常用的命令:

啟用維護模式:php artisan down?--message='Upgrading Database' --retry=60

關(guān)閉維護模式:php artisan up

生成路由緩存:php artisan route:cache

清除路由緩存:php artisan route:clear

數(shù)據(jù)庫遷移?Migrations:php artisan make:migration create_users_table --create=users

創(chuàng)建資源控制器:php artisan make:controller PhotoController --resource --model=Photo

創(chuàng)建模型及遷移:php artisan make:model User -m


五.?表單驗證機制

  表單驗證在web開發(fā)中是不可或缺的,其重要性也不言而喻浸颓,也算是每個web框架的標配部件了物臂。Laravel表單驗證擁有標準且龐大的規(guī)則集旺拉,通過規(guī)則調(diào)用來完成數(shù)據(jù)驗證,多個規(guī)則組合調(diào)用須以“|”符號連接棵磷,一旦驗證失敗將自動回退并可自動綁定視圖蛾狗。

  下例中,附加bail規(guī)則至title屬性仪媒,在第一次驗證required失敗后將立即停止驗證沉桌;“.”語法符號在Laravel中通常表示嵌套包含關(guān)系,這個在其他語言或框架語法中也比較常見

$this->validate($request,[

'title' => 'bail|required|unique:posts|max:255',

'author.name' => 'required',

'author.description' => 'required',

]);

Laravel驗證規(guī)則參考?http://d.laravel-china.org/docs/5.4/validation#可用的驗證規(guī)則?算吩;另外留凭,在Laravel開發(fā)中還可采用如下擴展規(guī)則:

自定義FormRequest (須繼承自 Illuminate\Foundation\Http\FormRequest )

Validator::make()手動創(chuàng)建validator實例

創(chuàng)建validator實例驗證后鉤子

按條件增加規(guī)則

數(shù)組驗證

自定義驗證規(guī)則


六.?事件機制

  Laravel事件機制是一種很好的應用解耦方式,因為一個事件可以擁有多個互不依賴的監(jiān)聽器偎巢。事件類 (Event) 類通常保存在?app/Events?目錄下蔼夜,而它們的監(jiān)聽類 (Listener) 類被保存在?app/Listeners?目錄下,使用 Artisan 命令來生成事件和監(jiān)聽器時他們會被自動創(chuàng)建压昼。

注冊事件和監(jiān)聽器:EventServiceProvider的 listen 屬性數(shù)組用于事件(鍵)到對應的監(jiān)聽器(值)的注冊求冷,然后運行 php artisan event:generate將自動生成EventServiceProvider中所注冊的事件(類)模板和監(jiān)聽器模板,然后在此基礎(chǔ)之上進行修改來實現(xiàn)完整事件和監(jiān)聽器定義巢音;另外遵倦,你也可以在 EventServiceProvider 類的 boot 方法中通過注冊閉包事件來實現(xiàn)

定義事件(類):事件(類)就是一個包含與事件相關(guān)信息數(shù)據(jù)的容器,不包含其它邏輯

namespace App\Events;

use App\Order;

use Illuminate\Queue\SerializesModels;


class OrderShipped

{

use SerializesModels;

public $order;

/**

* 創(chuàng)建一個事件實例官撼。

*

* @param Order $order

* @return void

*/

public function__construct(Order$order)

{

$this->order =$order;

}

}

定義監(jiān)聽器:事件監(jiān)聽器在 handle 方法中接受了事件實例作為參數(shù)

namespace App\Listeners;

use App\Events\OrderShipped;


class SendShipmentNotification

{

/**

* 創(chuàng)建事件監(jiān)聽器。

*

* @return void

*/

public? function__construct()

{

//

}


/**

* 處理事件

*

* @param OrderShipped $event

* @return void

*/

public? function? handle(OrderShipped$event)

{

?//使用 $event->order 來訪問 order ...28}

?}

停止事件傳播:在監(jiān)聽器的?handle?方法中返回?false?來停止事件傳播到其他的監(jiān)聽器

觸發(fā)事件:調(diào)用 event 輔助函數(shù)可觸發(fā)事件似谁,事件將被分發(fā)到它所有已經(jīng)注冊的監(jiān)聽器上

namespace App\Http\Controllers;

useApp\Order;

useApp\Events\OrderShipped;

useApp\Http\Controllers\Controller;

classOrderControllerextendsController

{

/**

* 將傳遞過來的訂單發(fā)貨傲绣。

*

* @param int $orderId

* @return Response

*/

publicfunctionship($orderId)

{

$order= Order::findOrFail($orderId);


//訂單的發(fā)貨邏輯...

event(newOrderShipped($order));

}

}

隊列化事件監(jiān)聽器:如果監(jiān)聽器中需要實現(xiàn)一些耗時的任務,比如發(fā)送郵件或者進行 HTTP 請求巩踏,那把它放到隊列中處理是非常有用的秃诵。在使用隊列化監(jiān)聽器,須在服務器或者本地環(huán)境中配置隊列并開啟一個隊列監(jiān)聽器塞琼,還要增加 ShouldQueue 接口到你的監(jiān)聽器類菠净;如果你想要自定義隊列的連接和名稱,你可以在監(jiān)聽器類中定義?$connection?和?$queue?屬性彪杉;如果隊列監(jiān)聽器任務執(zhí)行次數(shù)超過在工作隊列中定義的最大嘗試次數(shù)毅往,監(jiān)聽器的 failed 方法將會被自動調(diào)用

namespace App\Listeners;

use????App\Events\OrderShipped;

use????Illuminate\Contracts\Queue\ShouldQueue;


class????SendShipmentNotification????implements????ShouldQueue

{

????/**

????* 隊列化任務使用的連接名稱。

????* @var string|null

????*/

????public????$connection= 'sqs';


????/**

????* 隊列化任務使用的隊列名稱派近。

????* @var string|null

????*/

????public????$queue= 'listeners';


????public????function????failed(OrderShipped????$event,$exception)

????{

????//

????}

}

事件訂閱者:事件訂閱者允許在單個類中定義多個事件處理器攀唯,還應該定義一個 subscribe 方法,這個方法接受一個事件分發(fā)器的實例渴丸,通過調(diào)用事件分發(fā)器的 listen 方法來注冊事件監(jiān)聽器侯嘀,然后在 EventServiceProvider 類的 $subscribe 屬性中注冊訂閱者

namespace App\Listeners;


class????UserEventSubscriber

{

/**

* 處理用戶登錄事件另凌。

*/

public????functionon????UserLogin($event) {}


/**

* 處理用戶注銷事件。

*/

public????functionon????UserLogout($event) {}

/**

* 為訂閱者注冊監(jiān)聽器戒幔。

*

* @param Illuminate\Events\Dispatcher $events

*/

public????function????subscribe($events)

{

$events->listen(

'Illuminate\Auth\Events\Login',

'App\Listeners\UserEventSubscriber@onUserLogin'

);


$events->listen(

'Illuminate\Auth\Events\Logout',

'App\Listeners\UserEventSubscriber@onUserLogout'

);

}

}


七. Eloquent 模型

  Eloquent?ORM 以ActiveRecord形式來和數(shù)據(jù)庫進行交互吠谢,擁有全部的數(shù)據(jù)表操作定義,單個模型實例對應數(shù)據(jù)表中的一行

$flights= App\Flight::where('active', 1)

->orderBy('name', 'desc')

->take(10)

->get();

  config/database.php中包含了模型的相關(guān)配置項诗茎。Eloquent 模型約定:

數(shù)據(jù)表名:模型以單數(shù)形式命名(CamelCase)囊卜,對應的數(shù)據(jù)表為蛇形復數(shù)名(snake_cases),模型的$table屬性也可用來指定自定義的數(shù)據(jù)表名稱

主鍵:模型默認以id為主鍵且假定id是一個遞增的整數(shù)值错沃,也可以通過primaryKey來自定義栅组;如果主鍵非遞增數(shù)字值,應設置primaryKey來自定義枢析;如果主鍵非遞增數(shù)字值玉掸,應設置incrementing = false

時間戳:模型會默認在你的數(shù)據(jù)庫表有 created_at 和 updated_at 字段,設置timestamps=false可關(guān)閉模型自動維護這兩個字段醒叁;timestamps=false可關(guān)閉模型自動維護這兩個字段司浪;dateFormat 屬性用于在模型中設置自己的時間戳格式

數(shù)據(jù)庫連接:模型默認會使用應用程序中配置的數(shù)據(jù)庫連接啊易,如果你想為模型指定不同的連接饮睬,可以使用 $connection 屬性自定義

批量賦值:當用戶通過 HTTP 請求傳入了非預期的參數(shù)租谈,并借助這些參數(shù) create 方法更改了數(shù)據(jù)庫中你并不打算要更改的字段捆愁,這時就會出現(xiàn)批量賦值(Mass-Assignment)漏洞,所以你需要先在模型上定義一個?fillable(白名單昼丑,允許批量賦值字段名數(shù)組)或fillable(白名單呻逆,允許批量賦值字段名數(shù)組)或guarded(黑名單菩帝,禁止批量賦值字段名數(shù)組)

//用屬性取回航班呼奢,當結(jié)果不存在時創(chuàng)建它...

$flight= App\Flight::firstOrCreate(['name' => 'Flight 10']);


//用屬性取回航班控妻,當結(jié)果不存在時實例化一個新實例...

$flight= App\Flight::firstOrNew(['name' => 'Flight 10']);

模型軟刪除:如果模型有一個非空值 deleted_at,代表模型已經(jīng)被軟刪除了郎哭。要在模型上啟動軟刪除夸研,則必須在模型上使用Illuminate\Database\Eloquent\SoftDeletes trait 并添加 deleted_at 字段到你的模型 $dates 屬性上和數(shù)據(jù)表中亥至,通過調(diào)用trashed方法可查詢模型是否被軟刪除

namespace App;


use????Illuminate\Database\Eloquent\Model;

use????Illuminate\Database\Eloquent\SoftDeletes;

class????FlightextendsModel

{

use? SoftDeletes;


/**

* 需要被轉(zhuǎn)換成日期的屬性姐扮。

* @var array

*/17protected$dates= ['deleted_at'];

}

查詢作用域:Laravel允許對模型設定全局作用域和本地作用域(包括動態(tài)范圍)茶敏,全局作用域允許我們?yōu)槟P偷乃胁樵兲砑訔l件約束(定義一個實現(xiàn) Illuminate\Database\Eloquent\Scope 接口的類)驯嘱,而本地作用域允許我們在模型中定義通用的約束集合(模型方法前加上一個?scope?前綴)丽蝎。作用域總是返回查詢構(gòu)建器

全局作用域定義:

namespace App\Scopes;


use????Illuminate\Database\Eloquent\Scope;

use????Illuminate\Database\Eloquent\Model;

use????Illuminate\Database\Eloquent\Builder;

class????AgeScopeimplementsScope

{

/**

* 應用作用域

* @param \Illuminate\Database\Eloquent\Builder $builder

* @param \Illuminate\Database\Eloquent\Model $model

* @return void

*/

public????function????apply(Builder$builder, Model$model)

{

return$builder->where('age', '>', 200);

}

}

本地作用域:

namespace App;

use????Illuminate\Database\Eloquent\Model;

class????UserextendsModel

{

/**

* 限制查詢只包括受歡迎的用戶烤咧。

*

* @return \Illuminate\Database\Eloquent\Builder

*/

public????function????scopePopular($query)

{

return????$query->where('votes', '>', 100);

}


/**

* 限制查詢只包括活躍的用戶浓恳。

*

* @return \Illuminate\Database\Eloquent\Builder

*/

public????function????scopeActive($query)

{

return????$query->where('active', 1);

}

}


動態(tài)范圍:

namespace App;


use????Illuminate\Database\Eloquent\Model;


class????UserextendsModel

{

/**

* 限制查詢只包括指定類型的用戶奖蔓。

*

* @return \Illuminate\Database\Eloquent\Builder

*/

public????function????scopeOfType($query,$type)

{

return????$query->where('type',$type);

}

}

隱藏和顯示屬性:模型?hidden屬性用于隱藏屬性和關(guān)聯(lián)的輸出,hidden屬性用于隱藏屬性和關(guān)聯(lián)的輸出洲守,visible 屬性用于顯示屬性和關(guān)聯(lián)的輸出梗醇,另外makeVisible()還可用來臨時修改可見性叙谨。當你要對關(guān)聯(lián)進行隱藏時手负,需使用關(guān)聯(lián)的方法名稱竟终,而不是它的動態(tài)屬性名稱


namespace App;

use????Illuminate\Database\Eloquent\Model;

class????UserextendsModel

{

/**

* 在數(shù)組中可見的屬性统捶。

*

* @var array

*/

protected????$visible= ['first_name', 'last_name'];

}

?>


//makeVisible()用來臨時修改可見性19return$user->makeVisible('attribute')->toArray();

訪問器和修改器:訪問器(getFooAttribute)和修改器(setFooAttribute)可以讓你修改 Eloquent 模型中的屬性或者設置它們的值喘鸟,比如你想要使用 Laravel 加密器來加密一個被保存在數(shù)據(jù)庫中的值什黑,當你從 Eloquent 模型訪問該屬性時該值將被自動解密。訪問器和修改器要遵循cameCase命名規(guī)范凯力,修改器會設置值到 Eloquent 模型內(nèi)部的?$attributes?屬性上

namespace App;


use????Illuminate\Database\Eloquent\Model;

class????UserextendsModel

{

/**

* 獲取用戶的名字。

*

* @param string $value

* @return string

*/

public????function????getFirstNameAttribute($value)

{

return????ucfirst($value);

}


/**

* 設定用戶的名字祈惶。

*

* @param string $value

* @return void

*/

public????function????setFirstNameAttribute($value)

{

$this->attributes['first_name'] =strtolower($value);

}

}

而對于訪問器與修改器的調(diào)用將是模型對象自動進行的

1$user= App\User::find(1);

2$user->first_name = 'Sally';//將自動調(diào)用相應的修改器3$firstName=$user->first_name;//將自動調(diào)用相應的訪問器

追加屬性:在轉(zhuǎn)換模型到數(shù)組或JSON時捧请,你希望添加一個在數(shù)據(jù)庫中沒有對應字段的屬性疹蛉,首先你需要為這個值定義一個 訪問器可款,然后添加該屬性到改模型的 appends 屬性中

namespace App;


use????Illuminate\Database\Eloquent\Model;

class????UserextendsModel

{

/**

* 訪問器被附加到模型數(shù)組的形式闺鲸。

*

* @var array

*/

protected????$appends= ['is_admin'];

/**

* 為用戶獲取管理者的標記摸恍。

*

* @return bool

*/

public????function????getIsAdminAttribute()

{

return$this->attributes['admin'] == 'yes';

}

}

屬性類型轉(zhuǎn)換:$casts 屬性數(shù)組在模型中提供了將屬性轉(zhuǎn)換為常見的數(shù)據(jù)類型的方法立镶,且鍵是那些需要被轉(zhuǎn)換的屬性名稱谜慌,值則是代表字段要轉(zhuǎn)換的類型欣范。支持的轉(zhuǎn)換的類型有:integer恼琼、real、float蛙卤、double颤难、string行嗤、boolean栅屏、object栈雳、array哥纫、collection磺箕、date、datetime建椰、timestamp

namespace App;

use????Illuminate\Database\Eloquent\Model;

class????UserextendsModel

{

/**

* 應該被轉(zhuǎn)換成原生類型的屬性棉姐。

*

* @var array

*/

protected????$casts=[

'is_admin' => 'boolean',//is_admin 屬性以整數(shù)(0 或 1)被保存在我們的數(shù)據(jù)庫中伞矩,把它轉(zhuǎn)換為布爾值16];

}

序列化: Laravel模型及關(guān)聯(lián)可遞歸序列化成數(shù)組或JSON

//單個模型實例序列化成數(shù)組

$user= App\User::with('roles')->first();

return? $user->toArray();

//集合序列化成數(shù)組5$users= App\User::all();

return? $users->toArray();

//單個模型實例序列化成JSON9$user= App\User::find(1);

return? $user->toJson();

//直接進行string轉(zhuǎn)換會將模型或集合序列化成JSON12$user= App\User::find(1);

return? (string)$user;

//因此你可以直接從應用程序的路由或者控制器中返回 Eloquent 對象15Route::get('users',function() {

return? App\User::all();

});

關(guān)聯(lián)(方法)與動態(tài)屬性:在 Eloquent 模型中苛让,關(guān)聯(lián)被定義成方法(methods)狱杰,也可以作為強大的查詢語句構(gòu)造器

$user->posts()->where('active', 1)->get();

Eloquent 模型支持多種類型的關(guān)聯(lián):一對一仿畸、一對多错沽、多對多千埃、遠層一對多镰禾、多態(tài)關(guān)聯(lián)吴侦、多態(tài)多對多關(guān)聯(lián)

舉個例子备韧,一個 User 模型會關(guān)聯(lián)一個 Phone 模型织堂,一對一關(guān)聯(lián)(hasOne)


namespace App;

useIlluminate\Database\Eloquent\Model;

classUserextendsModel

{

/**

* 獲取與用戶關(guān)聯(lián)的電話號碼

*/

public????function????phone()

{

return$this->hasOne('App\Phone');

}

}

動態(tài)屬性允許你訪問關(guān)聯(lián)方法易阳,使用 Eloquent 的動態(tài)屬性來獲取關(guān)聯(lián)記錄潦俺,如同他們是定義在模型中的屬性

$phone= User::find(1)->phone;

Eloquent 會假設對應關(guān)聯(lián)的外鍵名稱是基于模型名稱的事示。在這個例子里肖爵,它會自動假設 Phone 模型擁有 user_id 外鍵劝堪。如果你想要重寫這個約定幅聘,則可以傳入第二個參數(shù)到 hasOne 方法里

return? $this->hasOne('App\Phone', 'foreign_key');

如果你想讓關(guān)聯(lián)使用 id 以外的值荐糜,則可以傳遞第三個參數(shù)至 hasOne 方法來指定你自定義的鍵

return? $this->hasOne('App\Phone', 'foreign_key', 'local_key');

如果我們要在 Phone 模型上定義一個反向關(guān)聯(lián)暴氏,此關(guān)聯(lián)能夠讓我們訪問擁有此電話的 User 模型答渔。我們可以定義與 hasOne 關(guān)聯(lián)相對應的 belongsTo 方法

namespace App;


useIlluminate\Database\Eloquent\Model;

classPhoneextendsModel

{

/**

* 獲取擁有該電話的用戶模型沼撕。

*/

public????function????user()

{

return? $this->belongsTo('App\User');

}

}

模型事件:?Laravel為模型定義的事件包括creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored务豺。?模型上定義一個?$events?屬性


namespace App;


use? App\Events\UserSaved;

use? App\Events\UserDeleted;

use? Illuminate\Notifications\Notifiable;

use? Illuminate\Foundation\Auth\UserasAuthenticatable;


class? UserextendsAuthenticatable

{

use? Notifiable;


/**

* 模型的時間映射。

*

* @var array

*/

protected? $events=[

'saved' => UserSaved::class,

'deleted' => UserDeleted::class,

];

}

?如果你在一個給定的模型中監(jiān)聽許多事件奔浅,也可使用觀察者將所有監(jiān)聽器變成一個類汹桦,類的一個方法就是一個事件監(jiān)聽器

定義觀察者:

namespace App\Observers;

use? App\User;

class? UserObserver

{

/**

* 監(jiān)聽用戶創(chuàng)建的事件营勤。

*

* @param User $user

* @return void

*/public? function? created(User$user)

{

}


/**

* 監(jiān)聽用戶刪除事件葛作。

*

* @param User $user

* @return void

*/

public function deleting(User$user)

{

//

}

}

注冊觀察者:

namespace App\Providers;


use? App\User;

use? App\Observers\UserObserver;

use? Illuminate\Support\ServiceProvider;


class? AppServiceProvider? extends? ServiceProvider

{

/**

* 運行所有應用.

*

* @return void

*/

public? function? boot()

{

User::observe(UserObserver::class);

}

/**

* 注冊服務提供.

*

* @return void

*/

public? function? register()

{

//

}

}


八. Laravel的Restful風格

  一般認為Restful風格的資源定義不包含操作,但是在Laravel中操作(動詞)也可作為一種資源來定義虱岂。下圖是對Laravel中資源控制器操作原理的描述第岖,可以看到蔑滓,create键袱、edit就直接出現(xiàn)在了URI中蹄咖,它們是一種合法的資源。對于create和edit這兩種資源的訪問都采用GET方法來實現(xiàn)俊抵,第一眼看到頓感奇怪务蝠,后來嘗試通過artisan console生成資源控制器馏段,并注意到其對create院喜、edit給出注釋“ Show the form for ”字樣喷舀,方知它們只是用來展現(xiàn)表單而非提交表單的爸邢。

  關(guān)于POST與PUT方法的差異的討論杠河,多認為它們都可用于創(chuàng)建和修改數(shù)據(jù)券敌,主要在于POST是非冪等操作而PUT是冪等操作待诅。


九. 擴展開發(fā)

  我們知道,Laravel本身是基于Composer管理的一個包轧钓,遵循Composer的相關(guān)規(guī)范弛房,可以通過Composer來添加所依賴的其他Composer包文捶,因此在做應用的擴展開發(fā)時粹排,可以開發(fā)Composer包然后引入項目中即可顽耳;另外也可開發(fā)基于Laravel的專屬擴展包。下面所講的就是Laravel的專屬擴展開發(fā)胰耗,最好的方式是使用 contracts 柴灯,而不是 facades赠群,因為你開發(fā)的包并不能訪問所有 Laravel 提供的測試輔助函數(shù)乎串,模擬 contracts 要比模擬 facade 簡單很多。

服務提供者:服務提供者是你的擴展包與 Laravel 連接的重點长豁,須定義自己的服務提供者并繼承自?Illuminate\Support\ServiceProvider 基類

路由:若要為你的擴展包定義路由匠襟,只需在包的服務提供者的 boot 方法中傳遞 routes 文件路徑到 loadRoutesFrom 方法即可

/**

* 在注冊后進行服務的啟動酸舍。

*

* @return void

*/

public? functionboot()

{

$this->loadRoutesFrom(__DIR__.'/path/to/routes.php');

}

配置文件:你可以選擇性地將擴展包的配置文件發(fā)布(publishes)到應用程序本身的config目錄上或者合并(mergeConfigFrom)到應用程序里的副本配置文件中,但不應在配置文件中定義閉包函數(shù)淮阐,當執(zhí)行 config:cache Artisan命令時泣特,它們將不能正確地序列化

/**

* 在注冊后進行服務的啟動。

*

* 用戶使用 vendor:publish 命令可將擴展包的文件將會被復制到指定的位置上膏孟。

*

* @return void

*/

public????function? boot()

{

$this->publishes([__DIR__.'/path/to/config/courier.php' => config_path('courier.php'),]);

}


$value= config('courier.option');//只要你的配置文件被發(fā)布暑劝,就可以如其它配置文件一樣被訪問16

/**

* 或者選擇性在容器中注冊綁定骆莹。

*

* 此方法僅合并配置數(shù)組的第一級。如果您的用戶部分定義了多維配置數(shù)組担猛,則不會合并缺失的選項

*

* @return void

*/

public? function? register()

{

$this->mergeConfigFrom(__DIR__.'/path/to/config/courier.php', 'courier');

}

數(shù)據(jù)庫遷移:如果你的擴展包包含數(shù)據(jù)庫遷移幕垦,需要使用 loadMigrationsFrom 方法告知 Laravel 如何去加載它們丢氢。在運行 php artisan migrate 命令時,它們就會自動被執(zhí)行先改,不需要把它們導出到應用程序的 database/migrations 目錄

/**

* 在注冊后進行服務的啟動。

*

* @return void

*/

public? function? boot()

{

$this->loadMigrationsFrom(__DIR__.'/path/to/migrations');

}

語言包:如果你的擴展包里面包含了本地化仇奶,則可以使用 loadTranslationsFrom 方法來告知 Laravel 該如何加載它們貌嫡。下例假設你的包名稱為courier

/**

* 在注冊后進行服務的啟動。

*

* @return void

*/publicfunctionboot()

{

$this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');

//如果不想發(fā)布語言包至應用程序的 resources/lang/vendor 目錄该溯,請注銷對$this->publishes()調(diào)用岛抄。運行 Laravel 的 vendor:publish Artisan 命令可將擴展包的語言包復制到指定的位置上

$this->publishes([

__DIR__.'/path/to/translations' => resource_path('lang/vendor/courier'),

]);

}


echotrans('courier::messages.welcome');//擴展包翻譯參照使用了雙分號 package::file.line 語法


視圖:若要在 Laravel 中注冊擴展包 視圖,則必須告訴 Laravel 你的視圖位置狈茉,loadViewsFrom 方法允許傳遞視圖模板路徑與擴展包名稱兩個參數(shù)夫椭。需要特別指出的是,當你使用 loadViewsFrom 方法時氯庆,Laravel 實際上為你的視圖注冊了兩個位置:一個是應用程序的 resources/views/vendor 目錄蹭秋,另一個是你所指定的目錄。Laravel會先檢查 resources/views/vendor 目錄是否存在待加載視圖堤撵,如果不存在仁讨,才會從指定的目錄去加載,這個方法可以讓用戶很方便的自定義或重寫擴展包視圖实昨。

/**

* 在注冊后進行服務的啟動洞豁。

*

* @return void

*/

public? function? boot()

{

$this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');


//若要發(fā)布擴展包的視圖至 resources/views/vendor 目錄,則必須使用服務提供者的 publishes 方法屠橄。運行 Laravel 的 vendor:publish Artisan 命令時族跛,擴展包的視圖將會被復制到指定的位置上11$this->publishes([

__DIR__.'/path/to/views' => resource_path('views/vendor/courier'),

]);

}


//擴展包視圖參照使用了雙分號 package::view 語法17Route::get('admin',function() {

return? view('courier::admin');

});

命令:使用 commands 方法給擴展包注冊 Artisan 命令,命令的定義要遵循Laravel Artisan 命令規(guī)范

/**

* 在注冊后進行服務的啟動锐墙。

*

* @return void

*/

public? function? boot()

{

if($this->app->runningInConsole()) {

$this->commands([

FooCommand::class,

BarCommand::class,

]);

}

}

公用 Assets:你可以發(fā)布像 JavaScript礁哄、CSS 和圖片這些資源文件到應用程序的?public?目錄上。當用戶執(zhí)行?vendor:publish?命令時溪北,您的 Assets 將被復制到指定的發(fā)布位置桐绒。由于每次更新包時通常都需要覆蓋資源,因此您可以使用?--force?標志:php artisan vendor:publish --tag=public --force

/**

* 在注冊后進行服務的啟動之拨。

*

* @return void

*/

public? function? boot()

{

$this->publishes([__DIR__.'/path/to/assets' => public_path('vendor/courier'),], 'public');

}


發(fā)布群組文件:你可能想讓用戶不用發(fā)布擴展包的所有資源文件茉继,只需要單獨發(fā)布擴展包的配置文件即可,通過在調(diào)用?publishes?方法時使用標簽來實現(xiàn)

/**

* 在注冊后進行服務的啟動蚀乔。

*

* @return void

*/

publicfunctionboot()

{

$this->publishes([__DIR__.'/../config/package.php' => config_path('package.php')], 'config');


$this->publishes([__DIR__.'/../database/migrations/' => database_path('migrations')], 'migrations');

}

對于上例運行命令 php artisan vendor:publish --tag=config 時將忽略掉migrations部分

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末烁竭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子吉挣,更是在濱河造成了極大的恐慌派撕,老刑警劉巖婉弹,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異终吼,居然都是意外死亡镀赌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門际跪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來商佛,“玉大人,你說我怎么就攤上這事姆打×寄罚” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵幔戏,是天一觀的道長歇盼。 經(jīng)常有香客問我,道長评抚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任伯复,我火速辦了婚禮慨代,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘啸如。我一直安慰自己侍匙,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布叮雳。 她就那樣靜靜地躺著想暗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪帘不。 梳的紋絲不亂的頭發(fā)上说莫,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音寞焙,去河邊找鬼储狭。 笑死,一個胖子當著我的面吹牛捣郊,可吹牛的內(nèi)容都是我干的辽狈。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼呛牲,長吁一口氣:“原來是場噩夢啊……” “哼刮萌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起娘扩,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤着茸,失蹤者是張志新(化名)和其女友劉穎壮锻,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體元扔,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡躯保,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了澎语。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片途事。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖擅羞,靈堂內(nèi)的尸體忽然破棺而出尸变,到底是詐尸還是另有隱情,我是刑警寧澤减俏,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布召烂,位于F島的核電站,受9級特大地震影響娃承,放射性物質(zhì)發(fā)生泄漏奏夫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一历筝、第九天 我趴在偏房一處隱蔽的房頂上張望酗昼。 院中可真熱鬧,春花似錦梳猪、人聲如沸麻削。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呛哟。三九已至,卻和暖如春匿沛,著一層夾襖步出監(jiān)牢的瞬間扫责,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工俺祠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留公给,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓蜘渣,卻偏偏與公主長得像淌铐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蔫缸,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容