顯示具有 php 標籤的文章。 顯示所有文章
顯示具有 php 標籤的文章。 顯示所有文章

2016年5月16日 星期一

Laravel 利用 orderByRaw 針對欄位順序排序

Laravel 的 orderBy 雖然可以多個下來 order, 但感覺這樣的 order 也只是以字串的開頭排序,而我要的是根據裡面是否有符合我要的字串,若有則依照欄位不同排序

如:
假設搜尋 class 跟 teacher 這兩個欄位有 "明" 這個字,但是所有的搜尋結果,要以 class 優先排序,再出現 teacher 的結果。

類似:


return $result

    ->orderByRaw('CASE WHEN `class ` LIKE \'%' . $query['query'] . '%\' THEN 0 ELSE 1 END ASC, id DESC')

    ->paginate($pageNumber);

參考:
http://stackoverflow.com/questions/6243678/specific-ordering-sql-command
Order by using multiple columns and manually array field in Laravel? By Itsolutionstuff
Laravel 5 orderByRaw 绑定参数问题
http://stackoverflow.com/questions/4798000/mysql-order-by-keyword-match

2015年11月19日 星期四

如何比較無雷地安裝 PHP7

前幾天同事 R 內部分享了如何正確地安裝 PHP7
據他本人分享,踩騙千千萬萬個雷,歸納出來的重點是:
『看 Laravel 的老爸怎麼裝,就怎麼裝!』

別說編編沒有讓大家看到這篇的重點,
https://github.com/laravel/settler/blob/php-7/scripts/provision.sh
這是實用的 script,
如果你是用 aws 起 ec2,在 user-data 的地方只要貼上這個 script,
如果你不想整包都靠這隻 script,那就看 script 把你不要的地方捨去掉,
主要是這包 script 把一些東西都裝好了,除了 php7 之外
composer, hhvm, nginx 什麼的都會裝。


2015年10月14日 星期三

PHP 進行斷字還是用 mb_ 開頭的好?! (substr v.s mb_substr)

我們常在什麼 XX 列表上,有時候因為一行文字過長,所以會有想把文字做截斷的動作,比方說 『i love you so much, do you know』,假設這段文字剛好擺進 div 時就是會過長,所以我想要把他截斷成 『i love you so much, do...』,最後還故意加了 ... 來表示。

假設這個問題只會發生在單行文字,那我會建議你用 css  的 ellipsis 解決掉,因為單行的段字做 ... 結尾用 css 蠻快的,但是發生在兩行或三行是就不能用 css 了。

這裏有一段很簡陋的程式碼,用意是每一筆 title 我都會判斷它的長度是否超過我認為的最常的可能性 (目前看起來是我希望他不要超過 39 個字),過長我就會用 substr 把它截斷,從第 0 個位子截到 39,最後補上 ... 字串:


不過很可惜這樣的做法有問題,我假設我存在資料庫的字串都是正確的字符編碼,可是經過這個 substr 截出來的字串,卻讓我得 � 奇怪的詭異符號,為什麼?!

經 google 發現原來 substr 算的是 bytes 而不是 characters,取而代之的是,如果你使用 mb_ 開頭的 function,可以額外處理 multi-byte encodings 的部分。

後來我用 mb_substr 將以上的程式碼改寫了一下: (要注意 function 的參數跟 substr 數量不太一樣)



每次在追這種怪問題都覺得心疲,但是得到解答後就覺得好多了

這個需求讓我想到以前工作的一些經驗,因為英文字跟中文字算出來的大概要擷取的數量是不一樣的,所以... 挺麻煩的。有時候 不要為了一些沒必要的堅持而堅持。

如果還有什麼不瞭解的地方可以 google substr vs mb_substr。

只是我還是不知道,到底在什麼情況下,我們會只用 substr ?


2015年9月30日 星期三

Laravel 在 Eloquent alias table 名稱

在 Laravel 撈資料的時候發現有時候 Eloquent 很不好組,尤其是有 sub query 的時候....,後來用了 DB 的方式去做 Query:
$query = DB::table('xxx_xxxx AS a')
     ->where(
         'a.GG',   '=',
         DB::raw(
            '(select max(GG) from xxx_xxxx as b where xxx_xxxx.`no` = b.no)'
         )
)

但我後來發現原來 alisa 在 Eloquent 有方法耶...
在 Eloquent 使用 Alias,只需要使用 from 的這 static method,比方說上面那一句可以改成下面這一句,後面的 where 幾乎是沿用就可以了。

$query = XXXXXXX::from('xxx_xxxx as a')
$query-> where(
'xxx_xxxx.GG,  '=',
DB::raw(
'(select max(GG) from xxx_xxxx as b where xxx_xxxx.`no` = b.no)'
)
);
堅持要用 Eloquent 的原因是,之前改了一些 getter/setter (accessors and mutators),如果今天用 DB:: 的方式去撈資料,我改寫的 getter 好像就沒有作用了,哭...,還好後來還是有找到方法,硬是全用 Eloquent 的方法取資料。




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月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月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

2015年7月31日 星期五

Laravel 搜尋與分頁功能製作的一些心得

之前自學 Laravel 4 的時候,分頁跟搜尋做的真是...亂七八糟,知道亂七八糟是因為,知道了有更好的寫法,不然就是會一直停留在自己的已知。

後來這兩個月接觸 Laravel 5 之後,發現有些以前的寫法,可以寫得更好,比方說... 有些現成的 method,我沒發現以前可以那樣用之類的情況,還去網路上找了一些沒有必要的方法,所以打算先一些做紀錄 XD,描述一下大概的邏輯,不會把整份 code 把拿出來講,如果有其他做法也歡迎讓我知道。

我這邊所指的分頁跟搜尋,是以一般後台所需要的功能為主,文章可能會牽涉 controller, model 跟 view,畢竟一個搜尋,除了 ORM 要處理帶入的搜尋條件之外,也要影響分頁的顯示,還有排序等等情況要做。改天有空,我會再分享一篇前端用 ajax 撈 json 的分頁方式。


我們先從 ORM 說起吧,在沒有分頁之前,撈資料,建 Model 都是稀鬆平常的事,最常見的撈法就是整個全撈出來,像這樣:
$articles = Article::all();
這個全部撈出來的 $articles 變數,可能會放在 ArticleController 某個顯示列表的 method,我來假設那個 method 叫做 getIndex 好了,那麼 ArticleController 的 getIndex 會將 $news 的變數拋到某個 view 去:
// ArticleController.php

public function getIndex() {
    
    // ORM...
    // $articles = ....

    return view('article.index', ['articles' => $articles]);

}

接下來,來做一點複雜的情況,假設在 article.view 加了一個搜尋用的 Form 表單,讓 user 可以搜尋文章的作者(Author),文章的標題(Title),文章的流水號(Id)。假設 user 送出了那個表單,應該發生什麼事情?

(1) 如果是用 GET  method 送出表單,網址應該呈現 localhost/article?author=winwu&title=&id=&sort=id&order=desc

(2) view 得到的資料要是正確的

(3) 資料超過某個數量後,要顯示分頁

先來解決 (1),當搜尋的條件已經進到頁面時,ORM 就需要做一些改變,首先我會在 controller 決定我願意接受的搜尋條件參數: (簡單來說其實就是接 $_GET 參數啦! 只是我現在 focus 在 Laravel 上。)
$queryString =  Request::only([ 'id', 'author', 'title', 'order', 'sort' ]); 
接到 GET 參數後,接下來,需要把條件,帶到 ORM 的搜尋表達上,在這邊,我個人比較偏好 where 帶 $query 方式去做搜尋,因為比較彈性,也可以在 function 下一般的 if else 或是一些商業邏輯的判斷, 資料轉換:
// default desc
$order = (isset($queryString['order']) && ($queryString['order'] === 'asc')) ? 'asc' : 'desc';

// defaul sort
$sort = (isset($queryString['sort'])) ? $queryString['sort'] : 'id';

$articles = Article::where(function($query) use ($queryString) {


if (isset($queryString['id']) && $queryString['id'] !== "" ) {
$query->where('id', '=',  $queryString['id'] );
}

if (isset($queryString['author']) && $queryString['author'] !== "" ) {
$query->where('author', 'LIKE', '%' . $queryString['author'] . '%');
}
        // do other things 
        
}) // 如果你需要 join 其他表,可以接在這裡: 
   /*
      -> join('other_table', function ($join) {
          $join->on('article.id', '=', 'other_table.reference')
      })     
   */;

// 處理order, sort 以及分頁數量設定 10 筆為一頁
$articles =  $articles->orderBy($sort, $order)->paginate(10);
分頁筆數的部分,建議也可以拆出去用變數傳入。
(1) 的部分,算是到一個段落了。


接下來,處理 (2)。
view 就只有兩件事,兩件事都可以簡單,也可以複雜。
(2-1) 顯示分頁
(2-2) 列表的表格 tr th 上,要有排序的功能可以點按。

(2-1) 相對單純,pagination 只要看官方文件即可,顯示方式也跟 orm 那邊的設定有關,如果你是用 ->simplePaginate(10) 那就只會呈現只有上一頁, 下一頁的分頁顯示,如果是 ->paginate(10),就會顯示比較完整的分頁。

顯示分頁,要在 article.view 加上 {!! $articles->render() !!} ,就會顯示出分頁了,這就是 (3) 。

不過這裡有個問題要注意,當我已經有搜尋某個條件之後,點到分頁的第二頁,原本的 query string 並不會帶到第二頁去,網址只會變成  localhost/article?page=2  而不會是  localhost/article?author=ssss&title=&id=&sort=id&order=desc&page=2。

這怎麼辦呢,有個好用的 method 叫做 appends(),可以把當下頁面上的搜尋條件參數,帶到分頁 link 的 url 上,你可以參考文件的 Appending To Pagination Links,或是像我一樣操作在 ORM 上。

我加上了 appends 到 articles 上。
$articles =  $articles->orderBy($sort, $order)
                                ->paginate(10)

                                ->appends($querystringArray);
因為我把 appends 這個項目加在 ORM 上,所以我的 view 的  {!! $articles->render() !!}  完全不用調整。你可以挑一個做法來用。

(2-2) 的話,排序 icon 大部份都是採用 bootstrap 的 icon 或是 font-awesome,麻煩就在這個排序的按鈕跟分頁其實是有些類似的,他一樣要帶入當下的搜尋條件,除了 order 跟 sort 要改變。

這個部分我至今也沒有很好的解法,但一直寫類似的 code,有些麻煩,後來我把這塊的 html 拆出去寫成兩個 function (這兩個 function 可以寫在 article.index 的 view 的最上方,好一點的做法就是拆出去一個 class 去包裝他 )

function getArticleSortLinks($sortField) {
  $sortLinks = route('article.index',
array(
     'sort'      => $sortField,
     'order'    => (isset($_GET['order']) && $_GET['order'] == 'desc') 
                                  ? 'asc' : 'desc',
     'id'          => isset($_GET['id']) ? $_GET['id'] : '',
     'author'  =>  isset($_GET['author']) ? $_GET['author'] : '',
     'title'       =>  isset($_GET['title']) ? $_GET['title'] : ''
     ));
     return $sortLinks;
}

function getSortIcons($sortField) {
  /*
     這個寫法的 else 會有一些問題,但就簡單參考一下。
   
     如果當下的排序是遞減,那個 icon 就要切換成遞增,反之。

     然後預設我是擺 asc。
   */

  if (isset($_GET['sort']) && ($_GET['sort'] == $sortField)) {
    $sortLinkIcons = ''
    . 'fa fa-sort-amount-'
    . ( (isset($_GET['order']) && ($_GET['order'] == 'desc')) ? 'asc' : 'desc');
  } else {
    $sortLinkIcons = ''
    . 'fa fa-sort-amount-asc';
  }
  
  return  $sortLinkIcons;
}

接著在排序的按鈕使用這個 function: (只舉某個欄位當作例子)
<th>文章標題
    <a href="{{ getArticleSortLinks('title')}}">
          <span class="{{ getSortIcons('title')}}"></span>
    </a>
</th>

大概是這樣,有想到什麼我會再補充。
另外根據經驗,如果 ORM 需要 join 到三張表以上,會變得蠻慢的... QQ。

下週還會跟公司的架構師做一些 code 的調整,有什麼我覺得一定會修正的部分,我會再補上來 :P

2015年7月30日 星期四

Laravel phpunit 報錯 Cannot redeclare .. previously declared ...

今天在 laravel 的專案下跑 phpunit 突然報了一堆 [Symfony\Component\Debug\Exception\FatalErrorException] 的錯誤。

可能的原因有很多。

以我的問題來說,一追才知道原來是因為我在 blade.php 的 view 下,寫了兩隻現成的 function,但是 phpunit 不能這樣做,測試被執行之後,重複執行相同的函數,就等於函數被重新定義,所以好方法就是,還是得乖乖的把這兩個 function 封裝到某個 class 下。

原本我的 xx.blade.php 是這樣:
function myViewHelper() {
      return ...;
}

<a href="{{ myViewHelper() }}">Go!</a>

最後我寫了一支類似 helper 或是 service 的 php,把 class use 進來,再 call 那個 function,得救 :P 。

類似這樣:
use XXX\XXX\ myViewHelper as myViewHelper;

<a href="{{ myViewHelper::getSortLinks()  }}">Go!</a>


應該還有更好的方式,可能 use 不應該放在 view(xx.blade.php) 的檔案裡,不過重點就是... 不能直接定義 function 在 view 裡。



2015年7月21日 星期二

homebrew 升級 PHP56

1. 先 tap hombrew-php
brew tap homebrew/homebrew-php

2. 安裝 php56 (我沒有刻意移除過去的存在的 php55版本)(安裝需要一點時間)
brew install php56

3. 修改你的檔案 .profile, .zshrc, .bashrc 或者 .bash_profile,看你用哪一種,加入以下的 export $PATH:
export PATH="$(brew --prefix homebrew/php/php56)/bin:$PATH”

4. 重新 source  .profile, .zshrc, .bashrc 或者 .bash_profile:
像我是用 .zshrc:
source ~/.zshrc

2015年6月5日 星期五

Laravel stdClass Object 轉 Array

(我想我最近已經寫 code 寫到不知道在幹嘛了...)

在使用 Laravel 的時候,從 DB (Mysql) 拿資料出來時,你可以透過 PDO Fetch style 去改變拿出來時的型態(取 ASSOC 或是 CLASS),這時候只要改變 database 的 config 就可以了(參考),又或者你只有某一部份的 sql 需要改變 PDO Fetch style,那你也可以單獨寫設定在程式裡,記得在結束後把設定改回來:

// 參考自 http://stackoverflow.com/questions/26174267/convert-laravel-object-to-array

// 改變 PDO 的 style
DB::setFetchMode(PDO::FETCH_ASSOC);

// array of arrays instead of objects
DB::table(..)->get();

// 改回原本的 PDO style
DB::setFetchMode(PDO::FETCH_CLASS);

另外一個情境是,我的資料是先預先存在 Redis 裡面,如果 Redis 取不到資料,就會跟 Mysql 要,拿到資料後再 json_encode 塞到 Redis,但是這兩者取出的資料格式會變成 stdClass v.s Array,麻煩的是我希望 Redis 取出的格式是跟著 Mysql 的,所以是 Reids 的 stdClass 要轉為 Array()。


要讓 redis 取出的格式跟 mysql 一樣都是 array 的話,
在 json_decode 時要記得加第二個參數 true 就可以讓他以 Array 的方式回傳了:
json_decode($redis->get('key'), true);
老實說要不是因為發現 redis 取出之後型態不同,我還真的沒發現 json_decode 的第二個參事就是 assoc 的意思,只是預設是 false :P。原來一個簡單的參數就可以解決我的問題,差點還以為要上網找一些奇怪的 method 來轉換 :P 感謝天
end.

2015年4月7日 星期二

Passing the same data over different views in Laravel

如何在 Laravel 讓每一頁都有同一包資料?!


在用 Laravel 做網站的時候,我有一張 table 用來儲存網站的基本資料,也就是像是 title, description, keywords, google analytics 追蹤碼, 公司等資訊,可是這些資料需要每一頁都 load 進來,但我不想要每個 controller 都寫這一句 SQL,也可以說我的需求只是想要共享這些資料到每個 view。

我找的這個 Stackoverflow 的解答是把這段 SQL 執行在 BaseController 的 __construct,然後用 View::share 的方式把資料送出去每個繼承 BaseController 的 controller,這樣只要寫一次就好。

目前這樣解決我的問題,不知道有沒有人可以提供更好的方法,感謝!~。

2015年3月26日 星期四

ubuntu 14.04 service nginx restart 出現 Fail 狀況

service nginx restart  執行 Fail 
今天在本來是一台 apache server 上的 Laravel,想要多架個 nginx,結果在 sudo service nginx restart 時一直出現 Fail:


(因為之前有耳聞如果 nginx 的設定檔沒有寫好,好像也會 fail,所以還有特地檢查我的 nginx 設定檔的寫法... Orz)

2015年3月17日 星期二

Laravel 多國語系網站 (使用 Laravel 4.2)

我在家偶爾有空的時候有試了一下多國語系,不知道是我沒認真學還是怎樣,但是前前後後也花了兩週啊... QQ,總之,自己做多國語系要串 routes,但是其實不會很難,可是畢竟很多事情要控制,自己寫很容易漏掉,像是語系是否要預設語系 (舉例: 不論 user 身在何地都顯示英文) 還是以瀏覽器的預設語系為主 (舉例: 在台灣就幫你預設顯示繁體中文),當下我是覺得我對多國語系需要的架構太不熟了...,所以我裝了 package 解決,文章後面會提到用哪些 package。

2014年12月29日 星期一

[ PHP 也有 Day ] Laravel 開發小型專案經驗分享


12/18,我在 PHP 也有 Day 講了這場 Talk。大概的方向是使用 Laravel 的 Blade,跟過去開發小型專案的分享。一直很忙,本來想要好好記錄一下自己說了什麼,不過後來放棄了... 我的熱情已被工作磨的差不多了 T_T

Blade 比較沒有什麼特別的技巧,就是熟,把他弄熟...習慣它,不過因為我平常都是用 Jade Template,用法跟 Blade 很像,所以就是也挺上手的,那天忘記說,如果不習慣 Blade,可以改用 twig 也是不錯的選擇。

至於專案經驗,或許有點過於 case by case,但就是給大家參考參考摟...,我是盡量濃縮到大部分的人在開發上會做的功能有哪些,可能遇到的問題,大概點一下方向。

Model 跟 Controller 在這次分享比較少提及,也許是我也不是很熟拉,不知道能怎麼講,所以就著重在 View 的分享。

不過這次比較抱歉的是沒有提及前端的測試,不過前端的測試是另一門學問了,如果真的想要深入,也能要另闢新的講題來演講。


希望大家不嫌棄~
小的我 2015 會繼續努力的。

2014年12月18日 星期四

Laravel-Excel 匯出 Excel (xls) 心得

最近在工作上一直處理匯出報表這件事情,整體來說, Laravel-Excel 已經能符合大部分的需求,安裝與使用上,也不是很複雜 (難的是撈資料啊....)。


不過,有個很懊惱的問題是,如同這位先生問的一樣:
exporting large files in separate sheets

我的問題不完全跟他一樣,但重點很像。如果資料量太大,要用 excel 匯出,會非常吃 memory,(PHPExcel是在 memory 中處理所有資料),一等就是遙遙無期 ( 我改用 fputcsv 超級快... 但是在mac會有亂碼問題 ),可能檔案還沒下載下來,畫面就先掛掉了,除此之外,你還可能會經歷兩種 fatal error:

(1) PHP Fatal error:  Allowed memory size of 16777216 bytes exhausted (tried to allocate 32 bytes)

(2) PHP Fatal error:  Maximum execution time of 60 seconds exceeded


第一個是,memory 不夠了,怎麼辦?! 你願意拿 memory 來換 xls 檔嗎? 可以的話,就不用吝嗇付出更多的 memory,設定一下 memory_limit (example: ini_set('memory_limit', '1G')) 。

第二個,時間不夠用。你可以等嗎? 那你可以把 max_execution_time 調高 ( ini_set('max_execution_time', 600);)。


以上兩種調整,可以讓 xls 成功匯出,但可能一等就是十分鐘,經測試,絕對不是 Query 慢,再慢也不會是這樣,而是 PHP 會等資料都 ready 好之後,再放到 xls 檔,所以佔用記憶體,你必須等...。

還有一種方法,就是把這樣的任務放到 background 去跑,礙於時程壓力,經驗不夠,我沒有走這種方式,我也不曉得測出來的結果如何,簡直悲劇一場。

結論: 我不想讓客戶久等,我暫時用 fputcsv 來擋一下當時專案匯出報表的需求...。至於 xls 我目前只會用在小量資料匯出。

(PS: fputcsv 要處理 select query 回來的 2 維陣列,又有一點麻煩之處,有空再紀錄 xdd)

2014年11月29日 星期六

[聽演講筆記] PHP 也有 Day - 那些金流的二三事



非常幸運的有報到 11 月的 php 也有 Day,Ricky 大大要講金流處理的分享,我最這塊也是超有興趣的,還好有來聽,學習到很多經驗。

以下的筆記,是我個人在聽的一些紀錄,建議還是看影片比較真實哦。

相關網址:
Youtube:






以下是我的筆記,其實跟 slideshow 差不多拉,因為我去就是瘋狂紀錄這樣 XDD :

PHP 也有 Day - 那些金流的二三事


金流類型
  • 線上付款
    • 付款即時確認
    • credit card
    • WebATM
    • Paypal
    • 支付寶
  • 離現金流
    • 訂單成立後有一段緩衝時間讓 user 付款,付款完成後才算成立。
    • 超商繳費 (ibon, 條碼繳費)
    • ATM 轉帳 (一次性) (虛擬帳號)
      • 某些不讓你臨櫃繳款 lol
      • 超商繳費: 金額不可以超過 20000 元 :8ball:
      • 虛擬帳號 ATM: 使用繳費方式,不受三萬塊方式 :+1:   (請記得選繳費而非轉帳)
    • 轉帳:: 非約定戶單日上限 3 萬


金流 API 
廠商要你簽 NDA,簽完才給你 API,再決定簽哪家之前只能問朋友了 xdd

API 串接流程
線上付款:
  • 消費者-->請求付款--> 做一個 forward request (怕詐騙)
    • 有些時候刷卡動作會把你導向一些金流平台,進行刷卡動作。
    • 提供 redirect url 給金流平台。
    • 驗證完成後會 callback 讓你知道有刷過還是沒有刷過。
  • 有些金流廠商的 callback,是把訊息封裝完之後,由 user 端帶回來給你 (類似加密過的資訊直接帶給消費者的瀏覽器)
  • 付款請求資訊欄位
    • 簽約完之後會給你一組商家代碼
    • 訂單編號
    • 付款方式
    • 金額
    • 繳費截止時間 (虛擬帳號或是ibon,有些平台允許你加入繳費截止時間,時間都是以『天』來算)
    • ibon 會要求你提供訂單明細(繳費單會列項目是什麼)



離線金流:
  • user 請求付款,電商回傳繳費代碼。
  • 銷帳: user 至超商,一繳錢/匯款後,就會通知金流平台-->繳費成功-->繳費成功通知,進入結賬程序-->通知 user 已收到錢。




付款結果通知欄位
  • 商家代碼
  • 訂單編號
  • 付款方式
  • 金額
  • 交易狀態(成功或失敗)
  • 交易時間
    •  (有些金流商不讓你設定繳款時間,因為有 user 就是未超過繳費時間才繳費,最後我們會用人工的方式去確認繳費時間)



如何驗證欄位
金流商最後要給我們的 callback。
 Note: 你不會知道這是不是真的金流商送的,相對的金流商也不知道是不是真的你。通常他們首先都會先產生一組 key(共同持有),將欄位加密後傳後,能解密 callback 的資料就是真的資料。


  • 某綠色金流商採用的方式為
    • key/IV 由金流商以及商家共同持有。
      • 簽完約金流商就會給你了。
    • 資料已 XML 封裝。
      • 要簽湊齊 16 bytes 的整數倍。
      • 要補 padding(PKCS7 padding)
      •   PHP:
      •     加密: function AESEncrypt($xml, $key, $iv)...
      •     解密: function AESDecrypt(...)
      •       要把padding 解掉。
    • AES 128 CBC Mode..... 加密。
      • CBC 是以一塊一塊的方式來加密,所以是有固定的長度的。
      • 將欄位與 key 依照特定格式 加密為 MD5...??@#@$@% (沒跟到 T_T )
      • 接收端驗證 MD5



訂單資料結構
  • 訂單狀態 (有效/作廢)
  • 交易方式 (credit  card/ ATM / ibon)
  • 交易狀態 (succes / ing / fail)
  • 繳款代碼
  • 交易有效期限
  • 付款時間
  • 訂單建立時間


程式架構
  • 跟金流商串接
  • callback 接
  • 一條龍的程式架構
    • 金流方式一直增加,以上流程無限迴圈 ?
    • 最可怕的事,在串接新的金流時,有些廠商沒有測試平台 (OS: 真是驚人..... )


分為商業邏輯跟金流處理

商業邏輯
  • 處理 user 付款流程
    • 專注在跟訂單相關的事件
    • 變動訂單的狀態
    • 處理動作是由事件驅動
  • 一堆 callback 等著商業邏輯來 call
    • Example :
    • 更新資料庫繳款代碼
    • 發送簡訊
    • 寄送email



金流處理
  1. 加密
  2. hash
  3. 送請求到金流廠商
  4. 不處理資料庫寫入的動作


  • 離線金流的話要先建立事件通知 (event) 商業邏輯。
  • 當消費者繳費了,金流商會給我們 callback 處理,一樣是處理事件通知。


返回 (Redirect Url)
  • 返回到商業邏輯,帶著訂單編號等資料,從資料庫 match data,然後做訂單狀態顯示。
    • 訂單狀態 response http status code 為依據


實作 Event
  • Symfony (Laravel) / Pear Event Dispatcher
  • 自己刻一個 XD


如何測試
  • 金流處理如何送出 Event?
  • 我發出訊息給金流廠商,廠商回傳繳費代碼
    • 實作邏輯兩件事:
      • 收到繳費代碼事件的 callback
      • 負責更新狀態
      • 送 email
      • 送簡訊...


Event callback works?
  • by 事件 event 去 call 他。
  • 一開始程式就要註冊好,收到 event 之後要做什麼事情。


Note
注意 : 
ATM 繳款的話,要看你的金流廠商怎麼做,比如說多繳錢或是少繳錢,有些廠商會給錯誤訊息,但有些廠商會給成功訊息。



Q: 產生訂單編號都用哪些方法?
  產生訂單編號,最忌諱流水號,因為容易被猜到。
  講者的慣例是 :
  第一位按照訂單類型編號
  第二位為年 (ex: 2014 只取 14),哪一年的哪一天(太陽日,一年之類的第幾天)
  幾點幾分幾秒產生 (同一秒會有人同時交易,所以後面會有 randon 三位數 rand (100, 999) )

國外金流手續費太高 paypal lol
(光抽的 % 數就差很多)

銀聯卡
會有固定匯率的問題,每半年程式就要調整。 (可能是國外的金流才有這個問題)


Q: 有無串國外金流的部分?
國外金流手續費太高 paypal  (光抽的 % 數就差很多)
銀聯卡
會有固定匯率的問題,每半年程式就要調整。 (可能是國外的金流才有這個問題)

Q: 多久掃一次繳費截止時間?
常常去掃的話會太頻繁。
有人下訂單才會去掃,有效跟無效的,在計算已經超過繳費時間的一次作廢掉。

Q: 離線金流要怎麼知道 user 已經繳費
如果你是透過這些金流廠商,有些廠商會主動 callback。
有些虛擬帳號的部分不會主動 callback,你要定期去要,回傳恐怖的 CSV XDD

Q: 什麼時間點去扣庫存 ? 先扣還是後扣?
這通常是無解的問題... 
只要有訂單下訂,我們就會保留名額,會有時間限制 (ex: 過了一天沒繳費就把庫存加回來,然後此訂單作廢)
來賓發言: 只要有人把東西放到購物車,就會先扣,這個問題就會比較少。

Q: 分期處理是否有相關的經驗 ?
有些銀行期數太多,手續費是不一樣的
基本上只是剛開始帶入的參數不一樣,即使分期,實際上收錢是銀行跟 user 慢慢收,而你的訂單的金額,銀行會把錢一次性的給你。 


完。

2014年10月24日 星期五

2014 PHPConf Taiwan 參加記錄







又是一篇流水賬,事隔好幾天了,我都詞窮了 :P

今年的 PHPConf 辦在輔仁大學,是 10/18 (六),活動網站點這個,然後一如往常,
如果沒有參加的朋友,通常都是看 PPT 比較多,不過我個人非常推薦了解一下今年的 phpconf 議題,很妙的是,我覺得大部份的議題跟 PHP 本身沒有太多的關係,架構面跟效能處理上的議題比較多 (ex: mongoDB, MySQL...),而且都蠻實務的,一整天聽下來有物超所值的感覺哦 !


以下就由本 blog 特派員小 win 做個簡單的 conf 記錄!~

場地來說,雖然也不是第一次到輔大了,不過那個會場真的是有些遠,好熱啊... (不知道為什麼今天特別的熱)。但是學校場地能這樣子也算不錯了,雖然下午會議室有點悶,不過是可接受範圍,感謝辛苦的工作人員還搬電扇來 :)。

一去就是滿滿的人潮在簽到。


這袋子跟去年的很像內,大象就是可愛!


個人識別證。



微博 LAMP 優化之路

第一場,微博 LAMP 演變,講者是中國最有影響力的 PHP 技術專家惠新宸,Laruence,採用 live 的方式進行議程,挺炫的啊,第一次體驗這種 live 議程,不過訊號斷斷續續有點可惜,有興趣可以先看這個網址。

大部份在講網站性能優化/結構優化,前端 php 後端 C, 從 LAMP 到 LNMP, O+ 替換 APC 等分享。很多專有名詞我沒有聽過 (ex: yaf, apc...)...,唯一的心得是覺得在台灣做網站,流量也很少有機會需要這樣優化我的網站,機會很少,有點可惜...。

然後聽完這場之後學到一個很有趣的名詞:  首屏時間 (頁面整個 load 完的時間 (數據填滿用戶瀏覽器的時間))。



創意與專案管理的冰與火之爭
這場我沒有太多的想法耶 XDD 抱歉。
只是很少再跟程式相關的場合聽到跟 PM 有關的議程,這也是一種開創呀~



whoscall & MongoDB
gogolook CTO 分享 mongoDB 的應用,雖然我沒有用過 MongoDB,不過是很實戰的分享經驗,講者說明了從 GAE 轉換到 AWS 使用 MongoDB 的過程之中遇到什麼樣的問題以及以何種方式去解決等等。

聽完之後我才知道原來 Time Sync (時間同步) 這麼重要,index size fit in memory,有多少資料配多少的記憶體,採用高水準 DISK等等。

希望之後會有 ppt 可以看,我很想收藏啊~






PHP Extension  開發實務

我的 PHP 程度很普通,所以這場是有點半登出的狀態,講者問: 『有誰看過 php 的原始碼?』 我想會場大概不超過 10 個吧,其實我也沒看過... Orz



運用 Docker 部署PHP專案 (Fntsrlike)
Docker 這陣子真的是很火紅。

這場真是大爆滿啊,不知道是不是因為最近 Docker 越來越多人用了。
這場我很難做些什麼筆記,講者的整個介紹算是很完整了,剩下的就是需要靠自己去學習 Docker 的部分了。

順便推薦一下臺北有個 Docker-Taipei。




HTTP accelerator - Varnish 應用 (Ninja)
今天聽下來最喜歡的一場議程,在講述 Varnish 的應用,如何用 Varnish 做 cache,為什麼選擇 Varnish? 使用之後的問題是什麼?

抱歉,以下這段是我記錄給自己的簡短的 note,請大家還是看 PPT 吧,當時要邊聽議程邊打字,可能難免有誤。

Web Server 安裝 Varnish
  • 優點: 線上服務快速部署
  • 但是線上服務已經在 hosting 了,當時的狀況是把一台下下來,另一台前端檔一個 Varnish。
  • Varnish 是檔在 apache 之前,varnish 會判斷 request 有沒有 cache 的資料,沒有才會跟 apache 溝通。

新的問題

  • Hit Rate 遞減
  • Cache 空間浪費
  • 後來發現機器越開越多,網站反而變慢,因為 hit rate 降低了 (被 cache 在 varnish 的次數變少了)。
架構調整

  • 幾台 varnish 擋在 apache 前
  • 風險: 單點失效影響服務。如果某檯機器失效的話,導致後端服務的 web server 可能會承受到 2,3 倍以上的量。
  • Warm up 存在 gap。
     cache miss storm
避免 warm up

  • 依舊保留 varnish 擋在 apache 之前
  • (單點失效的時候)短時間有問題而導致 varnish 掛掉的話,使用 switch 調配 ( 但是這是一個理想的情況)

VCL (Varnish config language) - 聽講者的介紹,是個方便的東西呀

VarnishStat 即時狀態
VarnishHist 線上網頁來源

議程後面有一段在講 cache-control,我覺得好像是蠻重要的部分,雖然我是有做筆記,不過很亂,不好意思放上來。
這場議程會後,我有點想知道 Nginx 跟 Varnish 的差別是什麼...



結語

其實還有幾場是我去聽但是沒有做記錄的,因為電腦快沒電了...,然後也沒有特別的想法,就沒再多描述了,Sorry。

隨著工作的時間跟累積的經驗,越來越能聽得懂某些議程的議題是什麼,感覺還不錯。
去年聽 PHP Conf,沒有像今年這麼的有心得,不完全跟今年的議程安排有關係,一方面也是隨著工作上遇到的問題,越來越能聽得懂某些議程想要分享的內容。

一年的工作累積,有差。進步總是很漫長,短時間內看不出結果,但累積到一定的量,還是會有收獲的 :D


我想今年台灣的 Conference 也算夠多的了,不知不覺也參加了好幾場....
偶爾要放下電腦,停停走走,走走... 停停...



2014年9月28日 星期日

Laravel 如何接住沒有連上 Redis 的 Error

如果沒有連上 Redis 理當的確會顯示 error message,我還為了測出這個 Error 刻意把 Redis 停掉,不過這是必要的,因為網站放到 Server 之後,也是必須考慮會有 Redis 未開的情況。

這是 Error Message,當在 database 有設定連線 Redis 時,因為連不上 6379 Port:



我的解法其實也沒什麼特別的,雖然有用 try,但是並不打算把 catch 到的 expection 接起來,畢竟把 error 丟給 user 看,沒什麼太大的意義,撈不到 redis 資料就看不到畫面,也不是很合理 :P 那就讓撈不到 Redis 時,直接從 Database Query 出資料吧 :)

我的寫法並不高深,也不會很炫,但至少不會給 User 難看的錯誤訊息畫面...


try {
    // 先嘗試 Redis 連線,連到的話測試使否拿的到要的 Key
   $app = app();
   $redis = $app['redis']->connection();
   $your_data = json_decode($redis->get('YOUR_KEY'));
   //echo '從 Redis 取得';
   
   if ($your_data == "") {
      // 如果 Redis 要不到資料,那就先跟資料庫拿,拿完之後打入 Redis
      //echo '跟資料庫要';
      $data_from_db = Model::orderBy('createdAt','desc')->get();
      $redis->set('YOUR_KEY', json_encode($data_from_db));
      $your_data = json_decode($redis->get('YOUR_KEY'));
   }else{
      $your_data = Model::orderBy('createdAt','desc')->get();
   }
} catch(Exception $e) {
   // 連不到 Redis 那就直接跟資料庫拿
   //echo '跟資料庫要';
   $your_data = Model::orderBy('createdAt','desc')->get();
}


Vue multiselect set autofocus and tinymce set autofocus

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