2010/02/28

噹噹~新皮夾


皮夾用了快五年,也迫得差不多了
最近沒事就在找新的皮夾
無意中逛街看到這個超中意的


我還是選了最亮的顏色= = 另外有灰色跟黑色


厚度,空的時候就很厚了


正面?? 後方還有零錢袋


這是我看上的原因,超多格可以放
看起來還好嗎?左側還可以翻開


置物空間多到一個好用


內層是用灰色,這樣搭我覺得很好看


這是原來在用的皮夾,購於公館,只要一張國父

2010/02/24

全家熊 VS 小七熊


託各位朋友的福,我換到小七的熊了,質感十分的不錯


跟等了三四個月的全家熊相比....全家還是不適合推公仔吧(茶)

2010/02/15

[CodeIgniter] Form Validation

春節還是寫一下程式XDD
在報名頁面有FROM讓使用者填寫資訊,你知道人總會手誤嘛!
所以一定得做檢查,讓使用者第一時間做修正,以往都是用JavaScript在前端檢查,後端則做簡易的檢查。
而CI提供的From Validation則是屬於後端的檢查。


引入Library
由於並不是每個頁面都需要,我選擇只在報名功能(signup/join)內引入,而非在建構子(Constructor) 內引入
$this->load->library('form_validation');
使用方法:$this->form_validation->函式();


加入驗證規則
如果沒有設規則,那就沒有意義了XDD
設定規則的方法:$this->form_validation->set_rules('名稱','給人看的描述','規則');
範例:$this->form_validation->set_rules('name','Your Name','required');
設定<input name='name'>是需要填寫的(required),如果有錯誤會提示'Your Name'沒有填寫


在報名頁面中,我設定以下的規則來驗證
$this->form_validation->set_rules('name','Your Name','required');
$this->form_validation->set_rules('sex','Sex','required');
$this->form_validation->set_rules('mobile','Mobile Phone','required|max_length[10]');
max_length[10] 限制最大長度為10

改寫處理程序
之前只有用$this->input->post('name')來檢查是不是有post資料,有則做新增,無則顯示form。簡易流程如下。
if( !$this->input->post('name') ){
  //無資料送過來,顯示form
}else{
  //有資料,做新增
}
改為依據驗證程序是否執行判斷(有跑過表示有送資料也通過驗證規則)
檢測是否有執行是使用$this->form_validation->run()
if( $this->form_validation->run() == FALSE ){
  //驗證程序未執行,可能為無資料或資料不正確,顯示form
}else{ 
  //驗證通過,做新增
}

在表單顯示錯誤訊息
為了讓使用者知道哪些欄位沒通過驗證規則,要在view中加入:
<?php echo validation_errors(); ?>
validation_errors()會顯示所有錯誤的訊息,預設是使用<p>包裝,可使用
$this->form_validation->set_error_delimiters('<div>','</div>');
來更改包圍物件,或可改為每個錯誤獨立顯示,如:
<input name='欄位名稱'><?php echo form_error('欄位名稱'); ?>


顯示自設的錯誤訊息
不喜歡預設的錯誤說明敘述可以使用set_message來更改
$this->form_validation->set_message('驗證條件','錯誤訊息');
可在說明中加入%s來顯示set_rule內的第二個參數文字
$this->form_validation->set_message('required','欄位%s未填寫');
$this->form_validation->set_message('max_length','欄位%s超過限定值%d');
我另外發現在限制字數可以增加%d來顯示限制的字元數


重新顯示填入的資料
Text使用set_value('欄位名稱');
<input type="text" name="username" value="<?php echo set_value('username'); ?>" size="50" />
Radio, Checkbox, Select 則使用set_radio(), set_checkbox(), set_select();
<input type="radio" name="myradio" value="1" <?php echo set_radio('myradio','1',TRUE); ?> />
<input type="radio" name="myradio" value="2" <?php echo set_radio('myradio','2'); ?> />


搭配Form Helper
TEXT: form_input('name',set_value('name') )
RADIO: form_radio('sex','m', (set_value('sex')=='m')?TRUE:FALSE )

修正驗證錯誤後的頁面錯誤

原因是因為當發生驗證錯誤時,返回的網址並沒有參加活動的sn
http://127.0.0.1/ci/signup/join
所以造成程式無法抓取活動的名稱,在新增資料時也會錯誤
只要將view內的form_open修正即可
從 <?php echo form_open('signup/join');?>
修正為<?php echo form_open('signup/join/'.$this->uri->segment(3));?>

原始碼 controller: signup.php
function join(){ 
    $this->load->library('form_validation');
    $this->form_validation->set_rules('name','Your Name','required');
    $this->form_validation->set_rules('sex','Sex','required');
    $this->form_validation->set_rules('mobile','Mobile Phone','required|max_length[10]');
    $this->form_validation->set_message('required','欄位%s未填寫');
    $this->form_validation->set_message('max_length','欄位%s超過限定值%d');  

    if( $this->form_validation->run() == FALSE ){
        $data['title'] = '報名';    

        $query = $this->mevent->get_one( $this->uri->segment(3) );
        $data['event_name'] = $query['topic'];  
        $data['content'] = 'join_form';
        $this->load->view('index',$data);
    }else{
        echo 'Success';  
        $insert_id = $this->mjoiner->add();   
        if( $insert_id ){   
            redirect('/signup/joininfo/'.$insert_id,'refresh');
        }else{
            echo "error";
            exit;
        }
    }
}


這篇寫的好爛= =,應該只有我自己看的懂

2010/02/09

[CodeIgniter] Table Library

以往在使用表格輸出資料的時候,都得打一堆<TR><TD>這種東西
不小心少打還會讓表格歪掉;
現在使用CI的Table Library,可藉由傳入一個陣列就可簡單的產生表格。

載入Library
$this->load->library('table');
要用之前要先載入,載入後以 $this->table->函式(); 形式使用

產生表格

Namesexage
Alexmale23
Brianmale30
ChangFemale28
Dannymale40

以上表為例,陣列如下(我懶得加引號XD)
$ary = array( array( name , sex , age ),
 array( Alex, male , 23 ),
 array( Brain, male , 30 ),
 array( Chang , Female , 28 ),
 array( Danny , Male , 40)
);

說穿了就是表格一行就是一個陣列,之後把它丟到generate
$this->table->generate($ary);
就會自己生HTML CODE了。

另外還有一個很變態的好用方法,他可以直接吃DB查詢後的結果
$this->load->library('table');
$query = $this->db->query("SELECT * FROM my_table");
echo $this->table->generate($query);
直接把QUERY的結果丟給他就可以產生表格了,非常的好用!

改變外觀
因為預設的樣子很醜,不能改外觀也沒人會想用,以下是官方說明文件的範例
$tmpl = array (
 'table_open' => '<table border="0" cellpadding="4" cellspacing="0" class='myTable' >',
 'heading_row_start' => '<tr>',
 'heading_row_end' => '</tr>',
 'heading_cell_start' => '<th>',
 'heading_cell_end' => '</th>',
 'row_start' => '<tr>',
 'row_end' => '</tr>',
 'cell_start' => '<td>',
 'cell_end' => '</td>',
 'row_alt_start' => '<tr>',
 'row_alt_end' => '</tr>',
 'cell_alt_start' => '<td>',
 'cell_alt_end' => '</td>',
 'table_close' => '</table>'
);

$this->table->set_template($tmpl); 

常用的應該只有第一行的 table_open,只要設定表格的ID或CSS
其餘使用CSS來設計即可。

一長串資料轉多行表格
如果要生表格的資料是屬於一維然後落落長的格式,可以使用這個讓它自己換行
$this->table->make_columns( 資料陣列 , 一行有幾個 );

範例:
$list = array('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve');
$new_list = $this->table->make_columns($list,3);
$this->table->generate($new_list);
onetwothree
fourfivesix
seveneightnine
teneleventwelve


可惜的是,目前沒看到怎麼讓他產生rowspan 或 colspan 的東東。

2010/02/08

[CodeIgniter] 報名功能與顯示資訊


在FORM使用那篇中,只完成到顯示表單而已(因為那時候我還沒時間寫 囧)
今天要來追一下目前的進度(我是編寫邊記錄我做了甚麼 所以要到一段落才會在PO文)

完成報名
流程大概是這樣
IF 沒收到資料
    顯示報名表單
ELSE
    //有收到資料
    新增資料到資料庫
    顯示報名成功業面
END


顯示報名表單的部分與先前相同,所以直接KEY程式碼
function join(){
    if( !$this->input->post('name') ){
        $data['title'] = '報名';      

        $query = $this->mevent->get_one( $this->uri->segment(3) );
        $data['event_name'] = $query['topic'];      

        $data['content'] = 'join_form';
        $this->load->view('index',$data);
    }else{
        $insert_id = $this->mjoiner->add();      
        if( $insert_id ){
            $data['joiner'] = $this->mjoiner->get_one( $this->uri->segment(3) );
            $data['title'] = '報名完成';
            $data['content'] = 'join_info';
            $this->load->view('index',$data);
        }else{
            echo "error";
            exit;
        }
    }
}

紅字部分是新增的;在Model: mjoiner中的add() 會將POST過來的資料新增至DB
成功回傳該新增的流水號sn,失敗回傳false(但是DB有問題 CI會先跳錯誤畫面= = 我還不知道怎麼關掉)
再根據回傳的內容來判斷顯示何種內容。(可以自己設計錯誤畫面轉過去)


但是!重新整理會破功
為什麼要特別提這個呢!看起來都很正常不是?
當報名完成後,使用者重新整理的話,就會重複送出資料,要馬寫防止重複的方法,要馬不要給使用者重新整理的機會!
我想到兩種方法:
  1. 新增之後把POST資料清掉,讓使用者重新整理後看錯誤訊息
  2. 新增完成後轉到另個頁面,即使重新整理也不會重複送出
我選擇2比較省事(茶)



$data['joiner'] = $this->mjoiner->get_one( $this->uri->segment(3) );
$data['title'] = '報名完成';
$data['content'] = 'join_info';
$this->load->view('index',$data);

改為
redirect('/signup/joininfo/'.$insert_id, 'refresh');

所以我們需要另一個動作: function joininfo()

顯示報名資訊(改良版)
Controller Function: joininfo
function joininfo(){
    $data['joiner'] = $this->mjoiner->get_one( $this->uri->segment(3) );
    $data['title'] = '報名完成';
    $data['content'] = 'join_info';
    $this->load->view('index',$data);
}

對,只是把剛剛的東西搬過來而已= =
這樣即使重新整理獲回上一頁都不會重複送出


View: join_info.php
<h1><?php echo $title?></h1>

<hr/>
<?php foreach($joiner as $key => $value):?>
<?php     $data[] = array( $key, $value ); ?>
<?php endforeach;?>
<?php echo $this->table->generate($data);?>
<hr/>

<?php echo anchor('signup/','回活動列表');?>

看起來好像比想像中少!因為這裡用了好用的Table Library,之後再寫怎麼用= =

顯示活動所有報名資料
很簡單的小功能,在首頁點選活動標題會顯示所有報名資料。(但是一般只會顯示報名人數,所以只是寫經驗的 XD)
Controller: signup.php
function eventinfo(){
    $sn = $this->uri->segment(3);
    $data['event'] = $this->mevent->get_one($sn);
    $data['joiner'] = $this->mjoiner->get_byevent($sn);    

    $data['title'] = '報名資訊';
    $data['content'] = 'event_info';
    $this->load->view('index',$data);
}

有沒有覺得改用Model之後世界變很美好!
另外還得將原本的View: home.php
<div id='event-<?php echo
$row->sn; ?>' class='post'>
    <h2
class='title'><a href='#'><?php
echo $row->topic; ?></a></h2>
    ....
</div>
改為
<div id='event-<?php echo $row['sn']; ?>' class='post'>
    <h2 class='title'>
        <?php echo anchor('signup/eventinfo/'.$row['sn'], $row['topic']);?>
    </h2>
    ...
</div>
=====複習一下anchor的用法=====
anchor( 連結的位址 , 顯示的文字 ) => <a href='http://127.0.0.1/ci/連結網址'>顯示的文字</a>
======================


View: event_info.php
<h1><?php echo $title?></h1>
<hr/>
<div id='event' class='post'>
        <h2 class='title'>
            <a href='signup/eventinfo/<?php echo $event['sn']?>'><?php echo $event['topic']; ?></a>
        </h2>
        <p class='meta'>
            Time: <?php echo $event['start_time']; ?> ~ <?php echo $event['end_time']; ?>
            | 名額: <?php echo $event['joiner_no']; ?>
        </p>
</div>
<div id='joiner'>
    <?php if( count($joiner) > 0 ):?>
    <?php for($i=0;$i<count($joiner);$i++):?>
    <?php $row = $joiner[$i];?>
    <?php $data[] = array(
                        $i+1,
                        $row['name'],
                        ($row['sex']=='m')?('Male'):('Female'),
                        $row['mobile'],
                        $row['time']
                    );
    ?>
    <?php endfor;?>
    <?php //$this->table->set_template( array('table_open' => '<table border="1">') ); ?>
    <?php $this->table->set_heading('No','Name','Sex','Mobile','Time');?>
    <?php echo $this->table->generate($data);?>
    <?php else:?>
        <h2>無報名資料</h2>
    <?php endif;?>
</div>
紅字都是Table Library的功能,所以看不懂很正常XDD,以下是畫面。(註:我有改CSS)


可以看得出來我在測試剛剛說的重複報名修正


我自己的感覺,使用CI之後,寫程式比較有條理,也不會再看到PHP混著HTML然後不時有SQL與法來攪局。
接下來還要有後台可以管理活動資料,讓使用者登入進而修改報名資料
之後更可以擴展功能,表單驗證、檔案上傳、圖片處理

[CodeIgniter] 使用Model

先說明一下:我也是第一次使用Model的概念來寫,所以有錯或不正確請多包涵!


Model
在上一篇表單的製作中,發現Controller可能會夾雜大量的SQL語法來讀取資料
所以使用Model來讓流程控制與資料庫溝通部分切開

此例中有兩個主要表格:活動跟報名資料
於是我針對這兩個表格分為幾個操作
活動資料表會有:
  1. 取得單筆活動資料  <= 像是報名頁面要顯示該活動資訊
  2. 取得所有紅動資料  <= 像是首頁顯示所有的活動列表
  3. 新增/修改/刪除 活動     <= 管理者用

報名資料表會有:

  1. 取得一個人的報名資料
  2. 取得一活動的所有報名人資料
  3. 取得一活動的報名人數   <= 這個與第二項很類似
  4. 新增/修改/刪除 報名資料
目前想到有大概這樣的功能需求,以下是寫個很陽春的程式碼
mevent.php
Class Mevent extends Model{
 function mevent(){
  parent::Model();
 }
  
 function get_all(){
  $query = $this->db->get('event');
  foreach($query->result_array() as $row){
   $data[] = $row;
  }
  return $data;
 }
  
 function get_one($sn){
  $query = $this->db->get_where('event',array('sn'=>$sn));
  return $query->row_array();
 }
  
 function add(){
  $sql_data = array(
      'topic'      => $this->input->post('topic'),
      'desc'          => $this->input->post('desc'),
      'start_time' => $this->input->post('start_time'),
      'end_time'      => $this->input->post('end_time'),
      'joiner_no'  => intval($this->input->post('joiner_no'))
     );
  if( $this->db->insert('event',$sql_data) ){
   return $this->db->insert_id();
  }else{
   return flase;
  }          
 }
  
 function update($sn){
  $sql_data = array(
      'topic'      => $this->input->post('topic'),
      'desc'          => $this->input->post('desc'),
      'start_time' => $this->input->post('start_time'),
      'end_time'      => $this->input->post('end_time'),
      'joiner_no'  => intval($this->input->post('joiner_no'))
     );
  $this->db->where('sn',$sn);
  return $this->db->update('event',$sql_data);
 }
  
 function delete($sn){
  return $this->db->delete('event',array('sn',$sn));
 }
}

mjoiner.php
Class Mjoiner extends Model{
 function Mjoiner(){
  parent::Model();
 }
 function get_one($sn){
  $this->db->select('joiner.* , event.topic');
  $this->db->join('event','joiner.event_sn = event.sn');
  $query = $this->db->get_where('joiner',array('joiner.sn' => $sn) );
  return $query->row_array();
 }
 function get_byevent($event_sn){
  $this->db->order_by('sn','asc');
  $query = $this->db->get_where('joiner', array('event_sn' => $event_sn));
  if( $query->num_rows() ){
   foreach($query->result_array() as $row){
    $data[] = $row;
   }
   return $data;
  }else{
   return null;
  }
 }
 function getpno_byevent($event_sn){
  $query = $this->db->get_where('joiner', array('event_sn' => $event_sn));
  return $query->num_rows();
 }
 function add(){
  $sql_data = array(
     'name'          => $this->input->post('name'),
     'sex'         => $this->input->post('sex'),
     'mobile'     => $this->input->post('mobile'),
     'event_sn'     => $this->input->post('event_sn'),
     'time'         => date('Y-m-d H:i:s')
     );
  if( $this->db->insert('joiner',$sql_data) ){
   return $this->db->insert_id();
  }else{
   return false;
  }          
 }
 function update($sn){
  $sql_data = array(
     'name'         => $this->input->post('name'),
     'sex'         => $this->input->post('sex'),
     'mobile'     => $this->input->post('mobile'),
     'event_sn'     => $this->input->post('event_sn')
     );
  $this->db->where('sn',$sn);
  return $this->db->update('joiner',$sql_data);          
 }
 function delete($sn){
  return $this->db->delete('joiner',array('sn',$sn));
 }
}

為了區別這是Model,我在首字上都加上'm'表示

修改Controller
寫好後請回到Controller
在function signup() 內加入
$this->load->model( 'mevent' );
如果想一次讀取多的Model,可在參數內傳入Array
$this->load->model( array('mevent','mjoiner') );
載入後,就可使用$this->mevent->函數(); 取得資料

完成Model後,就可以把先前在Controller內存取DB的部份取代
function index() :
$data['event'] = $this->db->get('event');  =>  $data['event'] = $this->mevent->get_all();   
function join() :
$this->db->select('topic');
$query = $this->db->get_where('event', array('sn' => $this->uri->segment(3) ) );
$row = $query->row();
$data['event_name'] = $row->topic;
$query = $this->mevent->get_one( $this->uri->segment(3) );
$data['event_name'] = $query['topic'];

這樣是不是可以把操作DB的部分從Controller轉到Model了

修改View
照理說應該跟VIEW沒關係,但是開始寫的時候是把查詢後的物件直接丟給VIEW做處理
但是轉給MODEL後是回傳多維陣列,需要做些修改
View: home.php
<?php if( $event->num_rows() > 0 ):?>
<?php foreach($event->result() as $row):?>
<?php if( count($event) > 0 ):?>
<?php foreach($event as $row):?>

其中的變數需要從物件存取改為陣列存取
<?php echo $row->sn; ?> => <?php echo $row['sn']; ?>

2010/02/05

[CodeIgniter] 報名功能使用Form Helper

首頁已經可以顯示所有報名活動了
本偏要來新增讓使用者報名的功能與介紹Form Helper、Url Helper


加入"我要報名"連結
這個步驟非常簡單,只需要在View: home.php加個連結就好;
但是這次換個方式來加連結
首先請在Class: signup.php signup() 內增加兩行
$this->load->helper('url');          Load Url Helper
$this->load->helper('form');       Load Form Helper

View: home.php中增加連結
...
<h2 class='title'><a href='#'><?php echo $row->topic; ?></a></h2>
    <p class='meta'>
        Time: <?php echo $row->start_time; ?> ~ <?php echo $row->end_time; ?>
         | 人數: <?php echo $row->joiner_no; ?> | <?php echo anchor('signup/join/'.$row->sn,'我要報名');?>
     </p>
...


其中:
anchor( '連結的位址', '連結的文字', '屬性')
範例:
anchor('signup/join/','我要報名')  會生成 <a href='http://127.0.0.1/ci/signup/join/'>我要報名</a>
說明:http://ci.wuboy.twbbs.org/user_guide/helpers/url_helper.html


存檔後,就可以看到有報名的連結了,請先移到連結上是否最後有該活動的SN於後
EX: http://127.0.0.1/ci/signup/join/1  => 沒有的話等下是不知道要報名哪個活動低

報名表單
我把這個表單叫做 join_form.php
<h2><?php echo $title?></h2>
<h3>報名場次: <?php echo $event_name;?></h3>
<hr/>
<?php echo form_open('signup/join');?>
<?php echo form_hidden('event_sn',$this->uri->segment(3));?>
<table>
    <tr>
        <td>Name</td>
        <td><?php echo form_input('name');?></td>
    </tr>
    <tr>
        <td>Sex</td>
        <td><?php echo form_radio('sex','m',true);?> Male
            <?php echo form_radio('sex','f');?>Female
        </td>
    </tr>
    <tr>
        <td>Mobile</td>
        <td><?php echo form_input('mobile');?></td>
    </tr>
    <tr>
        <td colspan='2'><?php echo form_submit('submit','送出');?></td>
    </tr>
</table>
<?php echo form_close();?>
<hr/>

$this->uri->segment( n );
可取得網址的第幾個參數,舉例: signup (1) / join (2) / 1 (3)
所以要取得報名活動的序號使用segment(3)即可
手冊:http://ci.wuboy.twbbs.org/user_guide/libraries/uri.html
 form_open( '目的網址' );
目的網址等同於<form>中的action內容
舉例: form_open('signup/join') 生成 <form action='http://127.0.0.1/ci/signup/join' method='post'>
form_close();
有頭也要有尾,但也可直接使用</form>
form_hidden('name','value');
生成隱藏欄位。
範例:form_hidden('event_sn', $this->uri->segment(3) ); => <input type='hidden' name='event_sn' value='1'/>
form_input( 'name' , '預設值' );
生成input。
範例:form_input('name'); => <input type='text' name='name' />
form_radio( name, value, 是否被選取 );
生成radio。
範例:form_radio('sex','m',true); => <input type='radio' name='sex' value='m' checked='checked' />
form_dropdown( name, 選項 , 預設值 );
這次沒有用,但是這個用起來超方便,只要在選項丟一個array就會產生OPTION
$options = array(
    'small' => 'Small Shirt',
    'med' => 'Medium Shirt',
    'large' => 'Large Shirt',
    'xlarge' => 'Extra Large Shirt'
)
echo form_dropdown('shirts',$options,'large');

我都得這樣生option
foreach($option_ary as $key=>$value){
    $option.= "<option value='$key'>$value</option>";
}
echo "<select name='shirts'> $option </select>";
用這函數就可以自動處理了

form_submit( name , 按鈕的字 );
這個不用說了吧....

View的部分就完成了,但是還欠缺一個變數需要從Controller送過來;就是報名活動的名稱

新增報名表單的功能
Controller: signup.php
        function join(){
            $data['title'] = '報名';
          
            $data['content'] = 'join_form';
            $this->load->view('index',$data);
        }
先把基本的雛形寫好,這樣已經可以點選連結看到報名表單了,但是會有一個錯誤
原因是我想顯示報名場次的名稱,但是controller沒有。

取得報名活動的名稱
這裡就可以用到複雜的Active Record語法
要根據活動的序號 ( Where sn = ? ) 從event table ( From event ) 取得該活動名稱 ( Select topic ),中文剛好反過來對吧!

SQL語法:Select `topic` from event where sn = '$event_sn';

Active Record:
$this->db->select('topic');
$this->db->where('sn', $this->uri->segment(3) );
$query = $this->db->get('event');
其中 where 與 get 可以用 get_where 替代,所以也可寫成這樣
$this->db->select('topic');
$query = $this->db->get_where('event', array('sn' => $this->uri->segment(3) ) );
只是where條件需要以陣列傳入第二個參數
還沒完呢!得把資料抓出來
$row = $query->row();     => row();

適用只抓取一行的情況, 也可用row_array() 取得陣列型態
$data['event_name'] = $row->topic;
打完!收工

總結
介紹了很常用的表單函數,要不要使用當然見人見智。
全部使用form函數可能會讓View中全部都是PHP變數,如果view 是交給沒學過PHP的設計師就不能用了= =

到現在這支程式還沒用到Model,發現了嗎?在抓取活動名稱的時候,用了一堆操作DB的敘述在Controller
這樣還是會讓SQL與法跟流程控制混在一起,日子一久就一整個混亂了。
那有更好的方法嗎?我的想法是讓controller呼叫個函數就可以取得資料了
$data['event_name'] = get_event( $this->uri->segment(3) ); 之類的
這樣子的做法很像Model的概念,所以就來寫個model吧!

2010/02/02

[友情贊助] Mr.Pig 3 - 我很笨因為我在上班 新書!!

mrpig3.jpg
友情贊助幫忙打個廣告,這次的畫風有越來越精緻化
故事也有越來越幽默的趨向~應該是好事吧(茶)
請各位能給予支持^^  博客來購書