讓你的程式更美好 – Service後可以加入很多功能

今天我們繼續拆分程式碼!把商業邏輯的內容寫在 Service 檔案中,這過程中我們都沒有加入新功能,主要是把程式放適當的位置。

Service

Service檔案 必須手動新增,建立一個 AnimalService 專門來處理動物資源大大小小的商業邏輯。(這裡不包含外部API喔!只用來寫操作動物資源的邏輯!)

假設我今天想把 行政院農委會收容所認養公開資料 的API,接到我的送養平台,要另外寫一個 Service檔案喔!

我的另外一篇鐵人賽 後端前進PostgreSQL (鐵人賽連結) 系列,是在嘗試學習用 PostgreSQL 資料庫!整理、拆解這些農委會的認養公開資料以及PostgreSQL的介紹,有興趣的也可以看一下喔!

閱讀更多

讓你的程式更美好 – 重構現有的程式碼

開始重構程式碼,前幾天有提到 Controller 越來越肥大,程式碼越來越多,根本就已經超出人類想要看的範圍了!就像一篇好的文章,字數太多也是一件壞事,要有足夠的耐性才會想看。

這系列鐵人賽目前的 Controller 程式碼還不算太多,還算簡單拆開來相對容易,講解也比較清楚。

預計這樣拆開目前的 Controller

  • 驗證會員等級權限 – 已由 Policy 負責
  • 驗證使用者輸入資料 – 需再加入 Request
  • 商業邏輯或外部資源 – 需再加入 Service
  • 轉換資料結構 – 已由 Resource 負責

為了達到接近單一職責原則預計把Controller 拆成這個樣子! 降低程式之間的耦合性!對目前的程式來說比較好!檔案不會拆太多太細,又可以讓程式可讀性增加, PolicyResource 已經完成了!現在來拆解 Service驗證資料

閱讀更多

讓你的程式更美好 – 寫一點點測試

昨天簡單介紹一下 Service 怎麼那麼突然要寫測試!小弟我對於測試這東西也是超級初心者~但是因為維護上遇到了一些困擾,所以對於這方面開始暸解一下~

發現對於CI/CD 還蠻有興趣的,打算有時間的話可以嘗試看看有沒有機會導入公司的開發維護流程

因為遇到的困難

  • 因為需求變更修改輸出結果!發現一些必要值移除了,有些畫面顯示不出來~發生錯誤。
  • 改了這個功能沒事正常運作,但是改A壞B,另外一個功能因為有用到相關的值導致錯誤

因此來寫一點點測試吧!確保資料是我們要的樣子,測試輸出結果。

讓程式幫我們測試我們的功能,我們是開發API想必會給其他人介接API,跑測試若是減少到必要資料時就會提醒有錯誤,可以避免如果做了一些變動,不知道介接API的人用到哪些參數值,因為有測試確保錯誤的發生!

另外一個關鍵適當的用版本號控管,這必須看開發時的時間點,有不同的解決辦法,正式開發中的API就不用太在意URI的版本號了!正式上線後有人開始使用API,有改到資料的結構,建議直接用版本號去更新,就別動舊的API不然很多人用時,你可以會被很多人關心。

測試檔案命名規則

  1. 檔案一定要由Test結尾才會讀取得到!(例如下方產生的AnimalTest
  2. 檔案內的方法必須由test開頭才可以!

產生測試檔案

產生一個用來測試動物資源的測試檔案

php artisan make:test AnimalTest

準備來寫測試囉!打開 tests/Feature/AnimalTest.php

寫測試時間

這個檔案中,請自行對於想測試的動作做命名,要想想各種可能,例如我想要測試查詢動物的API,因為查看動物不需要任何權限,因此我們的程式碼相對比較簡單,我們繼續看下去吧!

測試查詢 animal 結構

/**
* 測試查看animal 列表的 json 格式
*
* @return void
*/
public function testViewAllAnimal()
{
    //請求 api/animal 結果存入 $response
    $response = $this->get('api/animal');

    //assertJsonStructure 判斷 Json 結構是否與我們下方的結構相同
    $response->assertJsonStructure([
        "current_page",
        "data" => [
            [
                "id",
                "type_id",
                "name",
                "birthday",
                "area",
                "fix",
                "description",
                "personality",
                "created_at",
                "updated_at",
                "user_id"
            ]
        ],
        "first_page_url",
        "from",
        "last_page",
        "last_page_url",
        "next_page_url",
        "path",
        "per_page",
        "prev_page_url",
        "to",
        "total",
    ]);
}

這時候你會發現左側測試套件的工具列會出現tests/Feature/AnimalTest.php 並且可以點開 有testViewAllAnimal 方法! 如下圖灰色圖示的兩個項目

套件畫面

點選 三角形執行測試,如果程式寫對,輸出結果跟剛剛撰寫的測試結構一樣,就會顯示綠色勾勾!

套件畫面

再來想想有什麼需要測試

測試創建 animal 資源

在新建前因為確保每次測試完都可以成功,不是跑第一次後第二次就不成功,或是這台機器可以跑並且全數通過,但其他機器不能跑的情況!

Laravel 有一個方便的重置資料庫的trait,不過這是會直接執行 php artisan migrate:refresh 初始化資料庫「請勿正式環境使用不然你會後悔

use Illuminate\Foundation\Testing\RefreshDatabase;

class AnimalTest extends TestCase
{
    use RefreshDatabase;

    //...
}

另外一種是 DatabaseTransactions 他是使用資料庫 Transactions 執行完後並不會提交,會rollback ,所以只會還原這是的變更!我另外一系列的鐵人賽有講到這個在附上連結!

所以我們不用上面的RefreshDatabase用DatabaseTransactions 請自行依照你的測試流程斟酌使用

use Illuminate\Foundation\Testing\DatabaseTransactions;

class AnimalTest extends TestCase
{
    use DatabaseTransactions;

    //...
}

測試有權限有效 token創建資源

因為創建這部分,管理員跟一般會員都可以操作所以寫一個就好,Passport 套件有一個方法讓我們可以模擬權限

先在tests/Feature/AnimalTest.php新建一個 testCanCreateAnimal() 我先把官方網站的範例複製過來,修改成新建動物的請求POST /api/animal其他先不改,儲存並點選執行測試的三角形按鈕,除了工具列的顯示,還會發現錯誤標示於錯誤的方法上。

測試錯誤

錯誤訊息的意思是說測試結果請求POST /api/animal 回傳 401 HTTP 狀態碼,不是 201 新建成功的狀態碼。

因為我們還沒加上模擬權限的部分,請如下修改


    /**
    * 測試建立animal
    *
    * @return void
    */
    public function testCanCreateAnimal()
    {
        Passport::actingAs(
            User::first(),
            ['*']
        );

        // 請求時並傳入資料
        $response = $this->json(
            'POST', 'api/animal', 
            [
                'type_id' => '1',
                'name' => '大黑',
                'birthday' => '2019-10-05', //今天要補班
                'area' => '台北市',
                'fix' => '1'
            ]
        );
        
        //檢查返回資料
        $response->assertStatus(201)  //狀態碼應屬於201
            ->assertJson(
                [
                    "type_id"=> "1",
                    "name"=>"大黑",
                    "birthday"=>"2019-10-05",
                    "area"=>"台北市",
                    "fix"=>"1"
                ]
            );
    }

順利通過測試,這邊用 assertJson 來檢查回傳資料是否完全符合,裡面沒有包含到的就不會去檢查,例如他會回傳 createdat、updatedat、id 沒有寫在這個陣列裡,就不會去檢查它的值,這點必須留意一下。

測試沒有有效 token 操作 測試

多增加一個 testCanNotCreateAnimal 方法,程式碼如下所示

    /**
     * 測試不能建立animal
     *
     * @return void
     */
    public function testCanNotCreateAnimal()
    {
        // 沒有模擬會員權限的程式

        // 請求時並傳入資料
        $response = $this->json(
            'POST', 'api/animal', 
            [
                'type_id' => '1',
                'name' => '大黑',
                'birthday' => '2019-10-05', //今天要補班
                'area' => '台北市',
                'fix' => '1'
            ]
        );
        
        //檢查返回資料
        $response->assertStatus(401)  //沒有token,狀態碼應屬於401
            ->assertJson(
                [
                    "message" => "Unauthenticated."
                ]
            );
    }

其他想到可以測試的功能

大部分都是這樣寫,以此類推,主要是要想辦法讓每一台伺服器跑測試的結果都一樣,並不是這台測試正確就好。以下列出還可以寫的測試,有興趣可以多多嘗試,這裡不一一列出程式碼。

  • 更新 animal 測試
    • 測試 管理員 更新
    • 測試 一般會員 更新
    • 測試 沒有有效 token 操作 更新
  • 刪除 animal 測試
    • 測試管理員 刪除
    • 一般會員 刪除
    • 沒有有效 token 操作 測試

存檔自動測試

如果你是自己撰寫程式碼多多少少會有錯誤,一直按三角形執行測試是不是覺得很煩呀~ 工程師就是最不喜歡重複的動作了!這個套件可以監測檔案儲存,自動執行測試喔!

套件畫面

現在只要儲存就會自動跑測試~ 爽~~

結論

寫測試有涵蓋率這種東西,沒有寫到測試的地方還是測不到,但我覺得核心有顧好!錯誤自然就會減少!大家一起加油吧~我也在學習,再次強調,小弟我這方面非常淺,歡迎給我任何回應!可以讓我作為改善的參考!或有什麼推薦的教學也歡迎跟我分享喔! 感謝

參考資料

Passport 模擬權限

Laravel官方測試文件

讓你的程式更美好 – Service 概念

我自己的經驗呢!是把原生的PHP轉換成Laravel框架,那個時候最主要希望可以好維護,但是,把權限啦~商業邏輯啦~全部都會寫在Controller 最後你就會發現

Controller 越來越大

Controller 越來越大

Controller 越來越大

如果又不是前後端分離的方式拆開的話,Controller 會非常非常亂。

之前就開始想辦法前後端拆開,所以開始研究RESTful API 的設計方式

閱讀更多

進階 RESTful API 討論

複習一下!並加入比較深入進階的部分,利用鐵人賽這個機會讓我再去認真查詢學習 RESTful API 的相關設計!雖然不是強制一定要這麼設計API,但是可以讓程式碼的可讀性更好!我想這是各位大大會想努力的地方。

確認一個資源

如之前的範例資源就是動物資源,設計RESTful API要先有一個客戶端資源,可以做查詢動物的資訊、建立、編輯、刪除,分別用HTTP不同的動詞,以客戶端(介接我們API的人)的方向去思考,不需要跟資料表一模一樣。

HTTP 動詞

  • GET: 讀取資源
  • POST: 新增資源
  • PUT: 替換資源
  • DELETE: 刪除資源
  • PATCH: 更新資源部份內容

GET 的動作相較安全,它不會變動更改到伺服器的資訊,主要用來查詢資料。

POST、DELETE、PATCH、PUT 則會依照對應的動詞做需要的動作,寫入資料庫,或一些商業邏輯。

一定會是這種模式 動詞+資源

POST http://127.0.0.1/animal

PUT、PATCH 差別

PUT 通常做替換一個資源功能。

PATCH 修改資源的部分內容。

我自己是這麼定義的,假設資料表內已經有一筆資料內容如下

{
  "id" : 1,
  "name" : "小白" ,
  "type" : "小型犬" ,
  "birthday" : "2019/1/29"
}

PUT : 動物資料整個替換掉 PUT 方式請求網址 /animals/1  修改編號1的資料,回傳資料內容都是ID 1但裡面的內容整個不同。

PUT下面的內容,那麼ID 1的資料就會全部被修改成小黑的資訊,所以請求的時候必須填寫所有欄位不然資料會變成預設值。

{
  "name":"小黑" ,
  "type":"大型犬",
}

--結果--
{
  "id" : 1,
  "name" : "小黑" ,
  "type" : "大型犬" ,
  "birthday" : null
}
閱讀更多

我的最愛追蹤功能製作

定義資源

我的最愛功能,製作一個可以讓 user 追蹤動物的操作,是一個連結的關係,綁定動物與用戶的關聯。

依照以前的經驗,我會幫這樣的動作取一個名字 like 之類的資料表來儲存內容。

但經過幾次的打造API經驗後,在規劃資料表的命名上,如果系統規模很大只有 like 當表名不是很明確。

我們這個系列打造的送養系統如果想要新增一個追蹤某位愛心媽媽的功能,就會覺得like不是很明確。

這樣在資料庫中看到 like 資料表,無法明確的知道內容。所以這邊我不另外給它一個名字。

會製作一張表  animal_user  這是我目前的原則!可以清楚知道這張資料表紀錄著 animal 與 user 的關係,並且依照字母排列A->Z命名這張表。 所以不命名為 user_animal 這是開始用 Laravel 後才有的習慣,在某個官方文章有寫到預設是 開頭A->Z來建立資料表。

閱讀更多

資料表設計規則-命名規則

之前有提到過小弟我是在開發WEB的工程師,經過幾次的案子會發現,注意一些小細節,建立好一個原則,對於大型的專案有很大的幫助。一開始可能覺得很麻煩,就跟後端程式碼的Coding Style一樣,縮排要空幾格、逗號之後空一格、名稱格式…感覺沒什麼用,資料庫也是一樣,如果隨便命名資料表,依照當下心情去決定,或是不想設定條件約束,設定後礙手礙腳,不好操作。

不過只要知道原理,以及命名有一個原則,等到有非常多資料表在資料庫中時,就會感受到這些小細節帶給你的好處!

閱讀更多

會員權限設計(管理員、一般會員)

昨天設定修改資料表以符合需求,接下來要設定權限部分,打算分兩種會員 管理員與一般會員。

權限說明
管理員可以做所有的新增刪除修改
一般會員可以新增動物而已,並且只能修改自己新增的動物。無法新增刪除修改分類

新建一個原則 policy

php artisan make:policy AnimalPolicy -m Animal
產生policy檔案
閱讀更多

JOIN 介紹

今天來介紹JOIN,有以下幾個模式!

  1. JOIN
  2. LEFT JOIN
  3. RIGHT JOIN
  4. FULL OUTER JOIN
  5. CROSS JOIN
SELECT aga.area_pkid, a.name, aga.subid, aga.place, aga.kind 
FROM adoption_gov_animals AS aga LEFT JOIN areas AS a 
ON aga.area_pkid = a.id;
閱讀更多

修改資料表新建 migration

明天我們要來建立權限的部分,之前設定的是驗證使用者使用 token 身份,但一班的網站至少會有管理員或一般會員的區分,因此今天先來修改基本需要設定的東西。

變更資料表

資料表原本規劃時沒注意到的地方,或是規劃好的東西難免都會被要求修改需求,所以修改資料表也是一門學問,用 migration 檔案修改檔案可以方便整個團隊更新到最新的版本。

跟大家說一個經驗,如果目前的系統是在線上用運行中的,並且公司沒有 DBA 在負責管理資料庫的話,要變更資料庫欄位時一定要三思而後行。 至少要確認有沒有備份。

閱讀更多