為什麼我不太想用Laravel ?
本文首發在我的個人公眾號,歡迎關注。
不是不用,是不太想用。
N年前Laravel 剛面世時,的確讓很多人眼前一亮,眾人驚呼原來PHP 代碼還可以寫得這麼簡潔優雅。
現在公司有兩個項目基於Laravel5,使用下來總感覺彆扭。其實到目前為止,還沒有一款真正讓我喜歡的框架。曾經年少輕狂,寫過框架並用在某教育門戶網站,現在看來,還是too young too simple。從工作到現在,使用、研究過各類流行框架,如Yaf、Yii、CodeIgniter、ThinkPHP、Symfony、Laravel、Slim 、ZendFramework2 等等。
- CI 就不說了,上世紀產品。
- TP 理念陳舊,懶得吐槽。
- ZF2 用過一個實際項目,從此再也不碰了。
- Yii 還行,只是還行。 。
- Slim 輕量級,實際用起來略繁瑣。
- Symfony 重量級框架,複雜,同時強大。
- Laravel 挺像Rails,簡潔優雅,同時強大。
不得不說,從設計哲學、功能組件、擴展性、社區等各方面來看,Laravel 當之無愧是最火熱的PHP框架。
我個人現在主要用Symfony3,主要是因為熟悉,也有挺多我很喜歡的特性,其它也有很多令人不爽的地方,這個後面單獨細講。
現在主要講講Laravel 讓我不爽的地方。
1.對IDE不友好#
這一點之所以放第一位,是因為我覺得框架對IDE 友好直接跟工作效率相關。
說來慚愧,我現在脫離了IDE(PhpStorm) 基本沒法工作了,只能手寫很簡單的入門級PHP 代碼。反過來說,我個人認為,對IDE 友好是框架的基本節操,再深挖下去,如果一個框架對IDE 不友好,能說明什麼問題? PhpStorm 堪稱宇宙最強PHP IDE,為何就連Laravel 項目的代碼提示都搞不定?加上插件也撇腳?
讀過Laravel 代碼後你就會逐步發現,裡面用到了很多非常magic 、tricky、hacky … (詞窮了)的代碼(或機制)。
下面列出一部分
1.1首先是容器機制#
Laravel 的容器機制的確設計得很靈活,比Symfony 的service container 機制靈活強大,但弊端在於對IDE 不友好,如:
$xxx = App::make('xxx');
這裡你能直接知道$xxx 是什麼對象嗎?你想調用其下的方法$xxx->someMethod()時, IDE 不會自動提示,你得翻代碼,看文檔,找到其provider 是誰,具體提供了哪個對象,再去看該對象代碼。
如默認helper 裡的代碼:app('url')->route($name, $parameters, $absolute),如果想按住Command 點擊route 方法,是不能跳轉的,IDE 無法識別。
1.2 再來說Facade ,同樣的問題。 #
Redis::get這裡的方法名不會自動提示,這類常用的簡單方法還好,可以直接打出來,對於一些不常用的呢?比如Redis::ZREMRANGEBYRANK 用之前是不是得查一下redis 文檔看看拼寫錯了沒?雖然運行時大小寫不敏感,但建議還是規範寫成Redis::zRemRangeByRank($key, $start, $end); 此時你又得查文檔,代碼裡還找不到,因為這裡Redis 的provider 提供的實際instance 是vendor/laravel/framework/src/Illuminate/Redis/Database.php,裡面用__call魔術方法調用Predis Client。
再如,我在調用某個方法時,通常會先查看其參數有哪些,按住Command,鼠標移到方法名上,就可以顯示了,如圖:
但是這些Facade 的靜態方法IDE 通通無法識別,只能繼續查文檔,翻代碼。 。
以上部分也就是其“簡潔” “優雅” 的一部分,但IDE 碰到這些代碼就一臉懵逼了,臣妾做不到啊。 。 。
1.3 Eloquent #
通過Model 查詢數據時,我每次都按下面的方式寫
AppModelsUser::query()->find(123); AppModelsUser::query()->where('type', $type)->get();
而不是官方推薦的直接用where 或find 等靜態方法形式
AppModelsUser::find(123); AppModelsUser::where('type', $type)->get();
你應該猜到為什麼了,因為調用->query()時會返回EloquentBuilder對象,這讓IDE 能夠識別,後面的鍊式調用才能一氣呵成。
而靜態方法,又是通過魔術方法實現的,IDE 又跪了。
1.4 補丁? #
別告訴我要用laravel-ide-helper ,這個雖然能解決大部分IDE兼容問題,但對代碼有侵入性,需要加到composer.json裡,需要啟用provider,需要執行命令生成meta文件,這些跟你的項目本身沒有半毛錢關係,這麼做不覺得跟Laravel 本身的簡潔優雅背道而馳嗎? (雖然我現在也加了。。)
總之,對IDE 不友好,會影響開發者效率,或增加心理負擔。
2. Laravel做得太多#
你們別扔雞蛋。 。 “特麼功能多了也能吐槽?傻x ”
2.1魔法太多#
準確的說這個應該算是優點,只不過。 。不知道你們見過下面的姿勢沒有。 。
$user_id = $this->request->get('user_id') $user_id = $this->request->user_id $user_id = $this->request['user_id']
我接手的某項目裡此類情況挺多,實在是。 。
從語義來看,只有第一個是正常的,也是SymfonyComponentHttpFoundationRequest裡提供的默認用法,是調用request 裡get 方法獲取某個參數,而不是讀request 對象的user_id屬性,更不是從request這個”數組(字典)” 裡取user_id這個key。無力吐槽。
Eloquent Model 對像也是類似,比如
$user = AppModelsUser::find(123); $user->nickname $user['nickname']
此類用魔術方法實現的機制有很多很多,可以試試搜索laravel framework 代碼,會發現非常多的__call__callStatic __get __set __isset
以此實現的所謂優雅,帶來的是語義缺失,寫法五花八門。
2.2自帶的helpers #
例如array_add array_last array_sort …
這些輔助函數的確很有用,不過我自己從來不用,也不推薦。
因為曾有人問我:“這些不是PHP 默認函數嗎?”
好吧。 。
以上只是很少一部分,這些嚴格來說不算是缺點,只是會讓程序員更懶、更容易誤導、更容易放棄追求本質。特別是對剛入門的程序員。
3.功能相關#
3.1 Facade機制#
前面已經提到一部分,調用某facade 方法時,你得找到其provider ,再找到真實instance 才能看到提供了哪些方法,都有什麼參數等等。
繞了一圈,才能找到真實出處,這點有些反直覺。
另外,如果你想自定義facade ,你得寫一個facade 模板文件,在getFacadeAccessor 裡返回一個唯一標識(取名),然後在某provider 裡註冊一下,然後再加到配置文件的aliases 中。
你會發現其實facade 模板文件非常雞肋,除了定義標識以外基本沒卵用。
不過, Laravel5.4版裡已經優化成real-time facade,本質是說“噢,facade模板文件的確很雞肋,現在我可以幫你自動生成~標識就是類名~ ”
在找不到某個facade 模板文件時幫你自動生成一個緩存文件,命名空間是Facades<your namespace>,此處仍然很magic
如,以AppMagicFacade為例
namespace App; class MagicFacade { public function hello() { return 'hello laravel'; } }
然後
此時PhpStorm 先懵逼一會。 。
magic 之處在於,將某個類當做facade 時,在其命名空間前加上Facades就可以了,現在你告訴我FacadesAppMagicFacade 對應的方法文件是AppMagicFacade ,其實生成的cache 文件才是親兒子。 。
這難道不也是反直覺嗎?語義呢?
3.2 Blade模板#
足夠簡單,但不夠純粹,感覺就是在寫PHP 代碼,除了關鍵詞替換了以外。如$users as $user $loop->first 等等
@foreach($users as $user) @if ($loop->first) This is the first iteration. @endif @endforeach
最不可思議的是,居然可以在模板裡寫PHP 代碼?
@php //wtf ? @endphp
“權限”太大,以至於,還可以有這種操作?
@foreach(User::where('type', $type)->get() as $user) {{ $user->name }} @endforeach
當然,這也是開發規範問題。
不過我更喜歡Twig 模板引擎,足夠靈活,且職責明確。
4.其它#
以下這些問題其它框架也都存在,不是吐槽,只是個人喜好
4.1路由定義#
Laravel 的路由定義跟Rails 很相似,不過我更喜歡Symfony 裡的寫法:
用註釋形式定義好路由訊息就可以了,還順帶能自動生成API文檔:
Laravel 從5.0 開始去掉了類似功能,現在你可以通過第三方包LaravelCollective/annotations實現此功能。
4.2數據表維護#
項目快速迭代期間,數據表變動是很常見的,難道每次變動都寫一個migration 文件?雖然Laravel 的migration 很好用,堪比Rails ,但我更喜歡Symfony + Doctrine 的用法,在Entity 文件(類似Laravel 裡的Model 文件)里以註釋形式定義各個字段屬性:
通過doctrine:schema:update 命令可以自動更新數據庫到最新狀態,其會比對當前Entity 屬性與數據庫的差異,生成對應的SQL語句並執行更新。
這個跟Laravel 的Schema change 方法類似,底層都用到了doctrine/dbal組件。 Laravel 裡也可以通過laraveldoctrine項目實現類似功能。
5.最後#
整體覺得Laravel 雖然強大,但很多特性是犧牲了語義化,或是反直覺的。當然,這些並不阻礙其成為PHP 社區最火框架。
Laravel 跟Rails 很像,Rails 社區哲學是約定優於配置, Laravel 也是類似,以上問題本質上不是什麼大問題,按照社區約定做法還是挺不錯的。
我現在常用的Symfony3 ,本身也有很多槽點,下期再講。
好了,以上是我的個人喜好,大家可以扔雞蛋了。
求關注#
最近才開始寫公眾號,求關注~
公眾號:亨特
也可以加我的個人微信號:henter