2015年8月30日 星期日

Laravel PHP 5.6 mail stream_socket_enable_crypto

今天在用 Laravel 使用寄信時一直遇到這個問題: stream_socket_enable_crypto(): Peer certificate CN

查了一下才知道自己的 php 版本是 php 5.6,好像是改變了一些機制,看起來很複雜,不過快速的方式就是,如果你跟我一樣不是用 SSL,那可以把 'encryption' 的設定改為 '' 空值就好。

預設為 //'encryption' => 'tls',,改為 'encryption' => '', 或是 'encryption' => env('MAIL_ENCRYPTION', ''),

有空再來了解,蠻煩的,因為最近專案全面使用 php 5.6 以上,沒遇過這些問題,本來 SMTP 是一件這麼簡單的事情,搞的我心煩。

參考文章 https://laracasts.com/discuss/channels/general-discussion/smtp-mail

2015年8月21日 星期五

SCP 下載遠端 server 上的檔案

複製 AWS  ubuntu 上的檔案到自己的本機,可以使用 SCP 指令。

要看完整的 scp (secure copy) 指令就 man 一下吧 (man scp)。


如果你是要用 pem 去登入的話:
公式:
scp -r -i [pem file path]  user@host:[server folder path] [local destination path]

範例:
scp -r -i your_server.pem  ubuntu@ip_or_domain:/var/www/XXXX/  /tmp/目的地資料夾


-r 是遞迴的意思,因為複製的檔案可能是好幾層的資料夾。
-i 是 identity_file 的意思,所以後面的參數就是接 pem 檔的路徑。


因為每次都忘記,所以特別紀錄一下。

2015年8月6日 星期四

關於 Laravel 5.1 的 Middleware

接觸 Laravel 5.1 的時候看到 middleware 這個資料夾的時候真的有驚訝到,是說,正常人第一眼看到 middleware 的時候會有以下症狀:

* middleware 是什麼?
* 我該怎麼用 middleware?
* 不會寫會怎麼樣嗎? (<- 一定不會怎樣,看你對自己的要求到哪而已 XD)
* 乾眼症

本魯心中也是這兩個問號,不過還好,在剛進敝公司的時候,跟隨同事用 Koa (一個 node.js 的 web framework) 開發 Web Application 的時候,就已經接觸過 Middleware 這個玩意兒了,他說起來確實有點難解釋,抽象,但漸漸抓到它的用處之後,會覺得他真的很好用,使用它最著名的範例,像是 csrf, 權限管理, auth...,下面我會貼上一個 link,大家可以稍微去參考一下那個作者怎麼使用 middleware 做權限管理 (年紀越大越懶得把整份 code 都貼出來講...)。

我先拉回來繼續講 middleware 本身。middleware 有的書會翻中介層,有的會翻中介軟體。它本身的行為可以說是 filter (過濾器),他本身的功能,用意是來隔離網站基礎設施與業務邏輯,至於他的工作範圍,可以說是收到 http request 之後一直到業務邏輯這一段都算,你可以想像你在切一顆洋蔥,那一刀要切下去的時候,就是開始發送 http request,切到中心點時,就是 app 本身的核心功能 (MVC),中間這一段過程,都是 middleware 可以處理事情的地方。

所以那個過程,可以攔截很多資訊,像是在程式還沒跑到 view 時,就先判斷誰有資格看到那個 view,所以這樣的權限功能,就可以寫在 middleware,而不是已經進到 controller 甚至是 view 了,才開始做這些判斷,你一個 app 可能 50 個 controller,難道你要一隻一隻程式各別去加那個判斷嗎? 所以,試著把這些功能抽出來,寫到越前面(底)的地方,提早執行, 不符合條件繼續往下一步就早點 response 回去給 user。

假如今天我寫了一個 middleware,在每個 route 去設定 middleware 以及要 filter 的規格,這樣就算是有用到 middleware 的功能了,不過這樣的設計,或許還可以再更好一點,換言之,我覺得其實 middleware 就是在寫業務邏輯跟流程控制,只是讓他早點發生 XD。(有些文章認為,為每個 route 去設定 middleware 不是很好的設計。)


範例
這裏有篇 Laravel 5 Simple ACL manager 請參考 (這只是個有用到 middleware 的 sample,可以大概看一下,你不要照 copy,除非你要的功能跟邏輯跟他一樣。)。如果你不喜歡這個範例,下面參考也有其他 link。


範例邏輯說明
* 要寫 middleware,首先你要有隻 middleware (你也可以用 php artisan make:middleware 來產生空的檔案,預設 middleware 會放在 app/Http/Middleware ),以上面那個範例來說,作者建立了一隻 CheckRole 的 middleware。
* 在 kernel.php 去註冊你的 middleware (不然誰知道你有 middleware 呢?),註冊的 key 就是使用 middleware 的名稱,如:  'roles' => 'App\Http\Middleware\CheckRole', 使用 middleware 時就是用 roles 那個名字。

放在 App\Http\Kernel.php 的 middleware,算是 global 或是 application 層級的 middleware。 -  翻自 『To add a global or application level middleware, we use the $middleware property of the App\Http\Kernel class.』- Laravel 5 Middleware Stack Decoded 
Route level middlewares can be registered using the $middleware property of the App\Providers\RouteServiceProvider class. - 參考自 Laravel 5 Middleware Stack Decoded 
* 在 middleware 把規則寫出來,做流程控制,哪時候可以通過這個 middleware (return $next($request)),哪時候不行通過,吐個 401 或是 402 跟使用者說掰掰之類的...。
* 在路由控制 (route) 做 middleware,哪幾個 route 需要經過你的 middleware 都要列出來,或者,你也可以把 route 做群組 (group) 之後,再設定 middleware。


小結論
* 合理的使用 middleware。(<- 純嘴砲,我也不知道怎樣算合理?!)
* middleware 也要注意程式有 Exception 的情況。
* 本篇只是小心得,希望大神路過指教 :) 有錯就鞭
* 其實我私心的覺得 L5 把 middleware 變得更好理解一點了,可是 code 比較多,在學 koa 的時候 code 少少的,只要 app.use() 一下就好,可是當時在學的時候,比較難理解...。

參考
https://docs.djangoproject.com/en/1.6/topics/http/middleware/
http://koa.rednode.cn/ 請參考 middleware 那邊有張圖。
http://www.richardbagshaw.co.uk/laravel-5-middleware/
http://www.codeheaps.com/php-programming/laravel-5-middleware-stack-decoded/
http://blog.elliothesp.co.uk/coding/passing-parameters-middleware-laravel-5/

2015年8月5日 星期三

Laravel 5.1 測試時模擬 user 登入

最近這幾天都在寫測試,花蠻多時間的,不熟之外,資源也不夠齊(不好找,有時 L4, L5 參雜,自己都搞混了)。

如果你是用 Laravel 4,那麼請參考 4 的說明 (有一段:『 You may set the currently authenticated user using the be method』從這邊開始 )。

在 Laravel 5.1 測試 user 登入狀態,要用 Model Factories 的方式,在這裡也要感謝小鐵大大,幫了我很多忙 :P。


大概的重點如下
* 在 phpunit 測試 code 裡面,使用 factory 去 fake 一個 $user,如官方文件說明,看你想要用 make() 還是 create()。另外我也意外地發現 $faker 蠻方便的,很開心的在 database/factories/ModelFactory.php 盡量學習用 $faker XD

我的 sample code,不是很完美,大概描述一下作法類似這樣子而已:
因為之後有很多模組或是功能都會需要這個假裝有 user 登入的測試情況,所以把它寫在外面 (TestCase去),繼承 TestCase 的測試 php 就都可以使用。
// TestCase.php

protected function demoUserLoginIn()
{
    $user = factory(App\User::class)->make();
    // Use model in tests...
    // 登入 user
    $this->be($user);
}

繼承 TestCase 的測試程式:
//  某隻 extends 自 TestCase 的測試.php

public function testLogout()
{  
    // 使用 TestCase 的 demoUserLoginIn
    $this->demoUserLoginIn();

    // 檢查登入狀態
    $this->assertTrue(Auth::check());
    // do other things...
}

* 另外一種測法是,只是測試資料帶入 form 然後登入的情況,一樣也可以模擬出登入的情況,只是每次都要繞到登入頁,這種測法就比較適合單純測登入表單的情況。

測試 form 登入,資料帶入 POST 的情況,要小心 csrf 的問題,還要在測試加上 Session::start();。
public function testLogin()
{
    Log::info('testLoginSuccess');
      Session::start();
    
    $this->call('POST', '/login', [
        'username' => 'winwuloveyou',
        'password' => '123456',
        '_token' => csrf_token(),
    ]);

    $this->assertRedirectedTo('dashboard');
}


參考
書目:「Basic TDD in Laravel 5」作者: Jace Ju.
Testing Laravel 5 Routes with CSRF Protection Using PHPUnit
https://laracasts.com/series/whats-new-in-laravel-5-1/episodes/5


2015年8月3日 星期一

Laravel 設定 foreign 要注意的事情

設定 FOREIGN_KEY_CHECKS
(1) 如果你在有設定 foreign 的相關 table 做 seeder 的時候,假設出現以下的 error: SQLSTATE[42000]: Syntax error or access violation: 1701 Cannot truncate a table referenced in a foreign key constraint ....

會有這樣的問題是因為 seed 的時候,就會立刻去檢查某個 schema 的外鍵,可是外鍵當時可能還沒建立。

那表示你需要在 seeder 在 inser 資料前後,做 FOREIGN_KEY_CHECKS 的切換 (畫底線那兩行),inser資料前先設為 0,insert 資料後設為 1 :

// 假設這是某隻 seeder 
// 這隻 seeder 的某個欄位是別張表的外鍵 
// XXXXTableSeeder.php

<?php

use Illuminate\Database\Seeder;
class UserTableSeeder extends Seeder
{
    public function run()
    {
        // disable foreign key constraints
        DB::statement('SET FOREIGN_KEY_CHECKS = 0');
        DB::table('user')->truncate();
        $users = [
               // data....
        ];
         DB::table('user')->insert($users);
        // disable foreign key constraints
        DB::statement('SET FOREIGN_KEY_CHECKS = 1');
    }
}


小心,unsigned()!
(2) 『當外鍵有參照到自動增量時,記得設定外鍵為 unsigned 型態。』- from http://laravel.tw/docs/4.2/schema
這句話的意思是,如果某張表的 key 是參考到另一張表的鍵而且是 auto_increment 時,要記得將那個外鍵設定為 unsigned()

範例,假這個 id 會在別的 table 某欄位參考到,那麼就要記得 unsigned:
$table->increments('id')->unsigned();

這是比較多新手會忘記的地方。

phpunit 跑 SQLite
(3) 補充一下關於 (1) FOREIGN_KEY_CHECKS 這件事情,因為在 phpunit 測試 ORM 時,有時會用 sqlite 而不是 MySQL,如果照樣使用 (1) 的 FOREIGN_KEY_CHECKS 的方式會報錯,另外也不建議使用 DB::enableForeignKeyCheck() 或是 DB::disableForeignKeyCheck() (這是我從 issue 找到的方法....),試一下發現 sqlite 一樣看不懂,而且還會噴出這樣的錯誤:  rrorException: call_user_func_array() expects parameter 1 to be a valid callback, class 'Illuminate\Database\SQLiteConnection' does not have a method 'disableForeignKeyCheck'

所以我目前找到比較好的方式(可以 mysql 跟 sqlite 兩全其美) 就是把 (1) 改成這個樣子...我其實說不上來這樣的修改好不好,如果有快速,乾淨解法,拜託讓我知道 QQ
if (DB::connection() instanceof Illuminate\Database\SQLiteConnection) {
    DB::statement(DB::raw('PRAGMA foreign_keys=0'));
} else {
    DB::statement('SET FOREIGN_KEY_CHECKS = 0');
}

// insert data


if (DB::connection() instanceof Illuminate\Database\SQLiteConnection) {
    DB::statement(DB::raw('PRAGMA foreign_keys=1'));
} else {
    DB::statement('SET FOREIGN_KEY_CHECKS = 1');
}

2015 08-31 補充:

有時候在跑測試的時候,會出現 Illuminate\Database\QueryException: SQLSTATE[HY000]: General error: 1 near "SET": syntax error (SQL: SET FOREIGN_KEY_CHECKS=0),這也是因為測試時的 db 環境是 Sqlite,所以 check foreign 的方式也不太一樣。

https://laracasts.com/discuss/channels/general-discussion/sql-hy000-error-when-seeding-phpunit-tests
這邊有個參考作法,直接從 DatabaseSeeder 動手腳,感覺比較乾淨,用 DB::getDriverName() 的方法感覺也比 instanceof Illuminate\Database\SQLiteConnection 更適合。

如果再跑測試的時候遇到 SQLSTATE[HY000]: General error: 1 too many SQL variables
找了一下只知道是因為: this is a limitation of sqlite: 999 parameters.。解法還在找 :P

Laravel ORM 兩個 schema(cloumns) 一起 unique

在製作某個表時,發現有個 schema 是需要一起 unique 起來的。

比方說有張記錄使用者權限的表格,user winwu  同時有 article-write, 以及 article-read 兩個權限,就會有兩筆記錄:

id  user_account     user_auth
1   winwu                 article-write
1   winwu                 article-read

但是又不希望  winwu   article-write 有一天又被重複紀錄,就是 winwu 這個人,對應到 article-write 是 unique 的。

在 schema builder  (migration 檔案) 可以這樣表示:

$table->unique(['user_account', 'user_auth']);

記得,重跑 migrate:rollback,然後再重新 migrate,你可以在 seeder 試著故意重複兩次一樣的紀錄,應該就會有 error 告訴你這些 schema 是 unique 的了。

error的訊息會像這樣:
  [PDOException] SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'winwu-article-read' for key ...

參考:
http://stackoverflow.com/questions/16990723/laravel-4-making-a-combination-of-values-columns-unique

對了,如果有比較適合的方式可以達到一樣的功能,就麻煩留言一下讓更多人知道 :) 謝謝!

2015年8月2日 星期日

O'REILLY『可測試的 JavaScript』 讀書心得

(我好幾次呆坐在螢幕前,不知道該分享些什麼...)

第一次在書店看到這本書時還蠻興奮的,又是 JavaScript,又講測試,感覺就應該讀一下,立刻帶回家 XD (敗家)

幾個心得:
*測試是有方法可以遵循,有經驗可以參考,但是寫測試是要練出來的,只有自己練出來才是最真實的...。

 *  TDD (Test-Driven Development) 測試驅動開發,是敏捷開發的推薦做法,就是先寫測試,再寫相對應的程式。 

 * BDD (Behavior-Driven Development) 行為驅動開發,建立在 TDD 之上,測試描述一個程式或是模組的行為。 

*要測試你的程式碼,首先程式碼要先可以被測試才是重點,畢竟程式可以很複雜,也可以很簡單,可以測試的程式碼要盡量是低耦合, 獨立的。(多做程式的拆解練習,把複雜的程式慢慢拆解為一隻隻比較單純, 單一任務的程式)。

*第三章講的事件導向架構,我沒有看得很懂就是,但是重點跟上一點接近。

*第四章講單元測試,單元測試算是我第一次接觸測試的方式,他需要工程師去思考每個單元測試需要哪些測試案例(腦補一些可能出現的弱點,就好像某隻程式明明需要帶入一個 值為 email 的參數,但是只有測到這個參數帶進去,卻沒有測試 email 的格式是否正確之類的...),因此,單元測試算是不能百分之百找到 bug 的測試,但是單元測試的重點是在於測試每隻程式的功能是否有符合需求(功能確認導向)。
   另外單元測試的 code 我覺得蠻好讀的,他的重點就是斷言(assert),直接去驗證某個值是否符合預期。

* 程式碼涵蓋率(Code Coverage),用來測量已經執行的測試程式碼之於未執行過的程式碼的涵蓋數,結果會用百分比來表示。 通常跟單元測試有關。書中 P.95 有個涵蓋率的參考:
    - 低於 50% 的 code coverage,是個低標區 (red flag)
    - 介於 60%~80% 的 code coverage,是甜蜜區 (sweet spot)
    - 大於 80% 的 code coverage,單元測試的效能會符合收益遞減率(<- 基本上我看不懂這個意思是什麼) ( code coverage 過高可能有誤導之嫌)。
   另外,code coverage 最好是自動化產生。

* 測試環境的模擬,也是一個很重要的測試技能。也許你在測試某隻程式時,需要重新設定資料庫的狀態,或是某隻程式可能相依於另一隻程式(相依性,是單元測試不想要測試的部分,可以利用 mocks 或是 stubs)。

*  mocks 跟 stubs 的不同在於 mocks 用於命令, stubs 用於查詢(參考 p.95)
  
*  mocks 用來被驗證 function 正確呼叫外部的 api。

*  stubs  用來假造回傳值到需要被測試的 function。

*  Doubles (測試替身),用來模擬相依性所需要用的物件,他也可同時扮演 mock 或是 stub。

*  前端 JavaScript,可參考 PhantomJS, Selenium。因為這兩個,方向都很大,我也沒摸熟過,不方便提...

*  第六章講效能測試,瀏覽器效能測試可以參考 HAR,書中有說明如何產生 HAR 檔案,如何查看。負載測試(Load Testing)  最常聽到的就是 ab (Apache Bench)。

*  效能測試可以用來辨別會阻礙效能的瓶頸,負載測試用來測試程式可以處理多少事情。

最後,我是覺得剛接觸測試的話,可以翻翻這本書,但是不用帶著墨在書本範例的程式碼,概念上先瞭解一下,這本書用蠻多 YUI 的 code 來解說,大概看一下就好,只是像我這種沒用過 YUI 的人,會覺得有點失落 XDD,因為書本有時候會提到一些 solution 是針對 YUI 的解法,所以有時候也會蠻疑惑的 XD。

前陣子在工作上是使用 Mocha, Chai 寫測試,有空再來分享這一塊。

以前看到別人講測試會覺得有點複雜,工具太多,要測試的方法也很多,嘗試過 chai, mocha 之後我才漸漸了解,原來這些測試工具都是環環相扣的,而且很有可能會上癮,也許你單元測試寫完之後,你就會想了解 code coverage,然後你也會想要用一下 phantom 或是 selenium,然後測試的項目,就會越來越齊全...,也會越有成就感。



參考:


2015年8月1日 星期六

Laravel5 file upload 上傳檔案範例

我把 controller 的範例分兩個來看,一個是多檔上傳,一個是單檔上傳,也不是說寫得很好,但至少還可以 work。

多檔上傳的範例:
另外因為是 json 回傳,所以 ajax 的部分我就不說明了。


單檔上傳的範例:

另外我也描述一下 view 的 form:

範例裡面有兩個 Form::file,請選擇是用哪一種,一個是 photo,另一個是 photo[]。

你還可以多注意的事情
* 檔名的建立方式我通常偏好 uuid,這部分就是依個人喜好。
* 圖片的 crop, resize 在這個範例並沒有做,你也可以自己串。
* 如果你不是用 Form:: 的方式在 view 建立你的 <form> 等等標籤,那麼務必要記得 form 的這個 enctype="multipart/form-data" (在這個範例是透過 'files'=>'true' 屬性產生)屬性一定要有,否則你永遠沒有東西可以上傳
* 這邊沒有做 javascript ajax 的範例,所以如果你是沿用這個 sample,當然送出之後會接看到 json 格式的頁面,所以也是要自己把這段串起來,這邊我是為了方便,才先用 form submit。

參考
How To Create File Upload With Laravel By clivern On April 10, 2014
webpatser/laravel-uuid

Vue multiselect set autofocus and tinymce set autofocus

要在畫面一進來 focus multiselect 的方式: 參考: https://jsfiddle.net/shentao/mnphdt2g/ 主要就是在 multiselect 的 tag 加上 ref (例如: my_multiselect), 另外在 mounted...