Laravel框架學(xué)習(xí)筆記之代碼開發(fā)規(guī)范

單一職責(zé)原則

一個(gè)類和方法只負(fù)責(zé)一項(xiàng)職責(zé)。

壞代碼:

public function getFullNameAttribute()
{
    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {
        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' $this->last_name;
    } else {
        return $this->first_name[0] . '. ' . $this->last_name;
    }
}

好代碼:

public function getFullNameAttribute()
{
    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();
}

public function isVerfiedClient()
{
    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();
}

public function getFullNameLong()
{
    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
}

public function getFullNameShort()
{
    return $this->first_name[0] . '. ' . $this->last_name;
}

胖模型援奢、瘦控制器

如果你使用的是查詢構(gòu)建器或原生 SQL 查詢的話將所有 DB 相關(guān)邏輯都放到 Eloquent 模型或 Repository 類。

壞代碼:

public function index()
{
    $clients = Client::verified()
        ->with(['orders' => function ($q) {
            $q->where('created_at', '>', Carbon::today()->subWeek());
        }])
        ->get();

    return view('index', ['clients' => $clients]);
}

好代碼:

public function index()
{
    return view('index', ['clients' => $this->client->getWithNewOrders()]);
}

Class Client extends Model
{
    public function getWithNewOrders()
    {
        return $this->verified()
            ->with(['orders' => function ($q) {
                $q->where('created_at', '>', Carbon::today()->subWeek());
            }])
            ->get();
    }
}

驗(yàn)證

將驗(yàn)證邏輯從控制器轉(zhuǎn)移到請求類肝劲。

壞代碼:

public function store(Request $request)
{
    $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
        'publish_at' => 'nullable|date',
    ]);

    ....
}

好代碼:

public function store(PostRequest $request)
{    
    ....
}

class PostRequest extends Request
{
    public function rules()
    {
        return [
            'title' => 'required|unique:posts|max:255',
            'body' => 'required',
            'publish_at' => 'nullable|date',
        ];
    }
}

業(yè)務(wù)邏輯需要放到服務(wù)類

一個(gè)控制器只負(fù)責(zé)一項(xiàng)職責(zé)突雪,所以需要把業(yè)務(wù)邏輯都轉(zhuǎn)移到服務(wù)類中闪唆。

壞代碼:

public function store(Request $request)
{
    if ($request->hasFile('image')) {
        $request->file('image')->move(public_path('images') . 'temp');
    }

    ....
}

好代碼:

public function store(Request $request)
{
    $this->articleService->handleUploadedImage($request->file('image'));

    ....
}

class ArticleService
{
    public function handleUploadedImage($image)
    {
        if (!is_null($image)) {
            $image->move(public_path('images') . 'temp');
        }
    }
}

DRY

盡可能復(fù)用代碼碧库,單一職責(zé)原則可以幫助你避免重復(fù)柜与,此外巧勤,盡可能復(fù)用 Blade 模板,使用 Eloquent 作用域弄匕。

壞代碼:

public function getActive()
{
    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();
}

public function getArticles()
{
    return $this->whereHas('user', function ($q) {
            $q->where('verified', 1)->whereNotNull('deleted_at');
        })->get();
}

好代碼:

public function scopeActive($q)
{
    return $q->where('verified', 1)->whereNotNull('deleted_at');
}

public function getActive()
{
    return $this->active()->get();
}

public function getArticles()
{
    return $this->whereHas('user', function ($q) {
            $q->active();
        })->get();
}

優(yōu)先使用 Eloquent 和 集合

通過 Eloquent 可以編寫出可讀性和可維護(hù)性更好的代碼颅悉,此外,Eloquent 還提供了強(qiáng)大的內(nèi)置工具如軟刪除迁匠、事件签舞、作用域等。

壞代碼:

SELECT *
FROM `articles`
WHERE EXISTS (SELECT *
              FROM `users`
              WHERE `articles`.`user_id` = `users`.`id`
              AND EXISTS (SELECT *
                          FROM `profiles`
                          WHERE `profiles`.`user_id` = `users`.`id`) 
              AND `users`.`deleted_at` IS NULL)
AND `verified` = '1'
AND `active` = '1'
ORDER BY `created_at` DESC

好代碼:

 Article::has('user.profile')->verified()->latest()->get();

批量賦值

關(guān)于批量賦值細(xì)節(jié)可查看對應(yīng)文檔柒瓣。

壞代碼:

$article = new Article;
$article->title = $request->title;
$article->content = $request->content;
$article->verified = $request->verified;
// Add category to article
$article->category_id = $category->id;
$article->save();

好代碼:

$category->article()->create($request->all());

不要在 Blade 執(zhí)行查詢 & 使用渴求式加載

壞代碼:

@foreach (User::all() as $user)
    {{ $user->profile->name }}
@endforeach

好代碼:

$users = User::with('profile')->get();

...

@foreach ($users as $user)
    {{ $user->profile->name }}
@endforeach

注釋你的代碼

壞代碼:

if (count((array) $builder->getQuery()->joins) > 0)

好代碼:

// Determine if there are any joins.
if (count((array) $builder->getQuery()->joins) > 0)

最佳:

if ($this->hasJoins())

將前端代碼和 PHP 代碼分離:

不要把 JS 和 CSS 代碼寫到 Blade 模板里,也不要在 PHP 類中編寫 HTML 代碼吠架。

壞代碼:

let article = `{{ json_encode($article) }}`;

好代碼:

<input id="article" type="hidden" value="{{ json_encode($article) }}">

或者

<button class="js-fav-article" data-article="{{ json_encode($article) }}">{{ $article->name }}<button>

在 JavaScript 文件里:

let article = $('#article').val();

使用配置芙贫、語言文件和常量取代硬編碼

壞代碼:

public function isNormal()
{
    return $article->type === 'normal';
}

return back()->with('message', 'Your article has been added!');

好代碼:

public function isNormal()
{
    return $article->type === Article::TYPE_NORMAL;
}

return back()->with('message', __('app.article_added'));

使用被社區(qū)接受的標(biāo)準(zhǔn) Laravel 工具

優(yōu)先使用 Laravel 內(nèi)置功能和社區(qū)版擴(kuò)展包,其次才是第三方擴(kuò)展包和工具傍药。這樣做的好處是降低以后的學(xué)習(xí)和維護(hù)成本磺平。

任務(wù) 標(biāo)準(zhǔn)工具 第三方工具
授權(quán) 策略類 Entrust、Sentinel等
編譯資源 Laravel Mix Grunt拐辽、Gulp等
開發(fā)環(huán)境 Homestead Docker
部署 Laravel Forge Deployer等
單元測試 PHPUnit拣挪、Mockery Phpspec
瀏覽器測試 Laravel Dusk Codeception
DB Eloquent SQL、Doctrine
模板 Blade Twig
處理數(shù)據(jù) Laravel集合 數(shù)組
表單驗(yàn)證 請求類 第三方擴(kuò)展包俱诸、控制器中驗(yàn)證
認(rèn)證 內(nèi)置功能 第三方擴(kuò)展包菠劝、你自己的解決方案
API認(rèn)證 Laravel Passport 第三方 JWT 和 OAuth 擴(kuò)展包
創(chuàng)建API 內(nèi)置功能 Dingo API和類似擴(kuò)展包
處理DB結(jié)構(gòu) 遷移 直接操作DB
本地化 內(nèi)置功能 第三方工具
實(shí)時(shí)用戶接口 Laravel Echo、Pusher 第三方直接處理 WebSocket的擴(kuò)展包
生成測試數(shù)據(jù) 填充類睁搭、模型工廠赶诊、Faker 手動(dòng)創(chuàng)建測試數(shù)據(jù)
任務(wù)調(diào)度 Laravel Task Scheduler 腳本或第三方擴(kuò)展包
DB MySQL、PostgreSQL园骆、SQLite舔痪、SQL Server MongoDB

遵循 Laravel 命名約定

遵循 PSR 標(biāo)準(zhǔn)。此外锌唾,還要遵循 Laravel 社區(qū)版的命名約定:

What How Good Bad
控制器 單數(shù) ArticleController ArticlesController
路由 復(fù)數(shù) articles/1 article/1
命名路由 下劃線+'.'號分隔 users.show_active users.show-active,show-active-users
模型 單數(shù) User Users
一對一關(guān)聯(lián) 單數(shù) articleComment articleComments,article_comment
其他關(guān)聯(lián)關(guān)系 復(fù)數(shù) articleComments articleComment,article_comments
數(shù)據(jù)表 復(fù)數(shù) article_comments article_comment,articleComments
中間表 按字母表排序的單數(shù)格式 article_user user_article,article_users
表字段 下劃線锄码,不帶模型名 meta_title MetaTitle; article_meta_title
外鍵 單數(shù)、帶_id后綴 article_id ArticleId, id_article, articles_id
主鍵 - id custom_id
遷移 - 2017_01_01_000000_create_articles_table 2017_01_01_000000_articles
方法 駝峰 getAll get_all
資源類方法 文檔 store saveArticle
測試類方法 駝峰 testGuestCannotSeeArticle test_guest_cannot_see_article
變量 駝峰 $articlesWithAuthor $articles_with_author
集合 復(fù)數(shù) $activeUsers = User::active()->get() active,data
對象 單數(shù) $activeUser = User::active()->first() users,obj
配置和語言文件索引 下劃線 articles_enabled ArticlesEnabled; articles-enabled
視圖 下劃線 show_filtered.blade.php showFiltered.blade.php, show-filtered.blade.php
配置 下劃線 google_calendar.php googleCalendar.php, google-calendar.php
契約(接口) 形容詞或名詞 Authenticatable AuthenticationInterface, IAuthentication
Trait 形容詞 Notifiable NotificationTrait

使用縮寫或可讀性更好的語法

壞代碼:

$request->session()->get('cart');
$request->input('name');

好代碼:

session('cart');
$request->name;

更多示例:

通用語法 可讀性更好的
Session::get('cart') session('cart')
$request->session()->get('cart') session('cart')
Session::put('cart', $data) session(['cart' => $data])
$request->input('name'), Request::get('name') $request->name, request('name')
return Redirect::back() return back()
is_null($object->relation) ? $object->relation->id : null } optional($object->relation)->id
return view('index')->with('title', $title)->with('client', $client) return view('index', compact('title', 'client'))
$request->has('value') ? $request->value : 'default'; $request->get('value', 'default')
Carbon::now(), Carbon::today() now(), today()
App::make('Class') app('Class')
->where('column', '=', 1) ->where('column', 1)
->orderBy('created_at', 'desc') ->latest()
->orderBy('age', 'desc') ->latest('age')
->orderBy('created_at', 'asc') ->oldest()
->select('id', 'name')->get() ->get(['id', 'name'])
->first()->name ->value('name')

使用 IoC 容器或門面

自己創(chuàng)建新的類會(huì)導(dǎo)致代碼耦合度高晌涕,且難于測試滋捶,取而代之地,我們可以使用 IoC 容器或門面渐排。

壞代碼:

$user = new User;
$user->create($request->all());

好代碼:

public function __construct(User $user)
{
    $this->user = $user;
}

....

$this->user->create($request->all());   

不要從直接從 .env 獲取數(shù)據(jù)

代碼示例

.env 文件中設(shè)置:

CDN_DOMAIN=cdndomain.com

config/app.php 文件中設(shè)置:

'cdn_domain' => env('CDN_DOMAIN', null),

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

  1. env('CDN_DOMAIN')
  2. config('app.cdn_domain')

在此統(tǒng)一規(guī)定:所有程序配置信息 必須 通過 config() 來讀取炬太,所有的 .env 配置信息 必須 通過 config() 來讀取,絕不 在配置文件以外的范圍使用 env()驯耻。

傳遞數(shù)據(jù)到配置文件然后使用 config 輔助函數(shù)獲取數(shù)據(jù)亲族。

壞代碼:

$apiKey = env('API_KEY');

好代碼:

// config/api.php
'key' => env('API_KEY'),

// Use the data
$apiKey = config('api.key');

有何優(yōu)勢

這樣做主要有以下幾個(gè)優(yōu)勢:

  1. 定義分明炒考,config() 是配置信息,env() 只是用來區(qū)分不同環(huán)境霎迫;
  2. 統(tǒng)一放置于 config 中還可以利用框架的 配置信息緩存功能 來提高運(yùn)行效率斋枢;
  3. 代碼健壯性, config()env() 之上多出來一個(gè)抽象層知给,會(huì)使代碼更加健壯瓤帚,更加靈活。

以標(biāo)準(zhǔn)格式存儲(chǔ)日期

使用訪問器和修改器來編輯日期格式涩赢。

壞代碼:

{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}

好代碼:

// Model
protected $dates = ['ordered_at', 'created_at', 'updated_at']
public function getMonthDayAttribute($date)
{
    return $date->format('m-d');
}

// View
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->monthDay }}

其他好的實(shí)踐

不要把任何業(yè)務(wù)邏輯寫到路由文件中戈次。

在 Blade 模板中盡量不要編寫原生 PHP。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末筒扒,一起剝皮案震驚了整個(gè)濱河市怯邪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌花墩,老刑警劉巖悬秉,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異冰蘑,居然都是意外死亡和泌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門祠肥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來武氓,“玉大人,你說我怎么就攤上這事仇箱×浚” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵工碾,是天一觀的道長弱睦。 經(jīng)常有香客問我,道長渊额,這世上最難降的妖魔是什么况木? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮旬迹,結(jié)果婚禮上火惊,老公的妹妹穿的比我還像新娘。我一直安慰自己奔垦,他們只是感情好屹耐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著椿猎,像睡著了一般惶岭。 火紅的嫁衣襯著肌膚如雪寿弱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天按灶,我揣著相機(jī)與錄音症革,去河邊找鬼。 笑死鸯旁,一個(gè)胖子當(dāng)著我的面吹牛噪矛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铺罢,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼艇挨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了韭赘?” 一聲冷哼從身側(cè)響起雷袋,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辞居,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛋勺,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓦灶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抱完。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贼陶。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖巧娱,靈堂內(nèi)的尸體忽然破棺而出碉怔,到底是詐尸還是另有隱情,我是刑警寧澤禁添,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布撮胧,位于F島的核電站,受9級特大地震影響老翘,放射性物質(zhì)發(fā)生泄漏芹啥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一铺峭、第九天 我趴在偏房一處隱蔽的房頂上張望墓怀。 院中可真熱鬧,春花似錦卫键、人聲如沸傀履。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钓账。三九已至碴犬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間官扣,已是汗流浹背翅敌。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惕蹄,地道東北人蚯涮。 一個(gè)月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像卖陵,于是被迫代替她去往敵國和親遭顶。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

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

  • 一. 說明 以下內(nèi)容大部分引用Laravel China社區(qū)的文章 - 分享下團(tuán)隊(duì)的開發(fā)規(guī)范 ——《Laravel...
    knghlp508閱讀 7,884評論 0 28
  • Laravel 學(xué)習(xí)交流 QQ 群:375462817 本文檔前言Laravel 文檔寫的很好泪蔫,只是新手看起來會(huì)有...
    Leonzai閱讀 7,913評論 2 12
  • 原文鏈接 必備品 文檔:Documentation API:API Reference 視頻:Laracasts ...
    layjoy閱讀 8,607評論 0 121
  • 首先特別感謝今天體檢來采血的老師們棒旗,真的都超級棒!在一個(gè)技術(shù)還有待提高的實(shí)習(xí)生面前你們給了我無限動(dòng)力撩荣,我會(huì)成為一...
    Bella小設(shè)影家閱讀 360評論 0 1
  • 感恩父母給于生命铣揉,感恩父母把我養(yǎng)大,感恩父母公婆身體健康餐曹,祝愿天下老人身體健康長壽 感恩金錢寶寶讓我種下財(cái)富的種子...
    馬忠娟閱讀 98評論 0 0