2008年6月11日 星期三

瀏覽器網頁空白問題

有人的IE可以正常看見網頁,但有些人就是不行。這時候,請先檢查一下網頁HTML的<title></title>,<title>內又是multi-byte的字元,而編碼的meta是不是在<head>的第一行。通常對方的瀏覽器沒有設定自動偵測時,這時候網頁可能會變成亂碼,或者是一片空白。
修正的做法如下:
<head>

<meta http-equiv="Content-Type" content="text/html; charset=big5">
<title>網頁標題title>

2008年4月2日 星期三

Common Dialog and Controls 簡介

Common Dialog and Controls Introduction

Contents:

1. Common Controls

(i) Button

(ii) Edit / Static Text

(iii) Check Box

(iv) Combo Box

2. Common Dialog

3. Modal / Modeless

4. Child Window

Preface

Key concept: All Controls Are Window.

Common Controls

Remark: GetDlgItem, DDX/DDV, Message Map

1. Button:

(i) ON_BN_CLIECKED(Control ID, Function)



in your header file, may find something like following:

BEGIN_MESSAGE_MAP(CClass3Dlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(IDC_BUTTON1, OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON2, OnBnClickedButton2)
ON_BN_CLICKED(IDC_BUTTON3, OnBnClickedButton3)
ON_BN_CLICKED(IDC_BUTTON4, OnBnClickedButton4)
ON_BN_CLICKED(IDC_BUTTON5, OnBnClickedButton5)
ON_BN_CLICKED(IDC_BUTTON6, OnBnClickedButton6)
ON_BN_CLICKED(IDC_BUTTON7, OnBnClickedButton7)
ON_WM_DESTROY()
ON_BN_CLICKED(IDC_BUTTON8, OnBnClickedButton8)
END_MESSAGE_MAP()



(ii) See WM_COMMAND and BN_CLICKED in MSDN.

(iii) When you click a button, the child window control (button) sends WM_COMMAND message to its parent window (Main window). And BN_CLICKED (notification code) is in HIWORD of WParam.

- LOWORD(wParam) Child window ID

- HIWORD(wParam) Notification code

- lParam Child window Handle

2. Static Text:

(i) For Showing text

3. Edit Control:

(i) For inputting text

(ii) Multi-Line / Want Return

(iii) Controlling it like window

4. Check Box:

(i) For Boolean

Common Dialogs

1. CFileDialog: Open/Save file

2. CFontDialog: Choose font type and style

3. CColorDialog: Choose color

4. CPrintDialog: Set printer

Modal / Modeless

1. Modal (DoModal)

(i) Loading the dialog resource

(ii) Disabling (freezing) parent and stopping it from getting any messages.

(iii) Creating and displaying - RunModalLoop

(iv) Destroy window after user hit “OK” or “Cancel”.


code like following:

CDemo demo; // CDemo inherts CDialog

if(IDOK == demo.DoModal())
{
CString tmp;

tmp.Format(TEXT(" edit1 = %sn edit2 = %dn check = %dn combo = %sn"),
demo.m_sEdit1, demo.m_nEdit2, demo.m_bCheck, demo.m_sCombo);

MessageBox(tmp);
}// if(IDOK == demo.DoModal())


2. Modeless

(i) Using new operator and then call CDialog::Create() to initialize and display with ShowWindow(SW_SHOW).

(ii) Using EndDialog() to terminate (not destroy or delete) modeless dialog.

(iii) Destroying and deleting it finally.

code like following:

show dialog:

if(about == 0)
{
about = new CAboutDlg();
about->Create(IDD_ABOUTBOX);
}

about->ShowWindow(SW_SHOW);



destroy dialog:

if(about != 0)
{
about->EndDialog(0);
BOOL B = about->DestroyWindow();
delete about;
about = 0;
}




ChildWindow

1. Remember it: All Controls Are Child Windows.

2. If you want to create a window to embed it into a window (parent window).

3. How to embed a window in the other.

(i) Add a FORMVIEW into resource and create a class for it.

(ii) Generate instance with new operator and call Create to initial it.

(iii) Move the window to specific location.

(iv) Show it!!!!


code like following:
child = new CTestChild(this);
child->Create(IDD_FORMVIEW, this);

RECT rect = {0}, rect1 = {0};

this->GetClientRect(&rect);
child->GetWindowRect(&rect1);

int w = rect1.right - rect1.left;
int h = rect1.bottom - rect1.top;

rect1.top = rect.bottom - h - 5;
rect1.bottom = rect1.top + h;

child->MoveWindow(&rect1);

child->ShowWindow(SW_SHOW);

MFC 簡介

MFC Introduction

Contents:

1. Some Background on Object-Oriented Programming

2. A Little History

3. A Grand Tour of MFC

(i) CObject

(ii) CWnd

(iii) CWinApp / CWinThread

(iv) CString

(v) Remaining

4. A Simple Code about Dialog Style Application


Preface

MFC (Microsoft Foundation Classes) is a framework for Windows programming, and there are other ones like ATL (Active Template Library) and WTL (Windows Template Library) from Microsoft.

Some Background on Object-Oriented Programming

OOP Terminology

1. Abstraction

2. Encapsulation

3. Inheritance

4. Polymorphism

5. Modularity

A Little History

1989 – Microsoft establish the application framework technology development group (AFX group, and you will see many functions with prefix “AFX”) (Legend has it that “AF” didn’t sound so great by itself, so the threw in the letter X to complete the acronym, The X doesn’t really mean anything)

1992 – Microsoft released MFC 1.0

1993/04 – Microsoft released VC++ 1.0 with MFC 2.0

1993 late – VC++ 1.5 with MFC 2.5

1994 late – MFC 3.0

1995 – VC++ 4.0 with MFC 4.0 (the last version is MFC 4.2)

1995 ~ 200x – MFC 5.0, 6.0 Unknow

2002 - Visual Studio .Net 2003 (VC++ 7.0) with MFC 7.0

Future - Visual Studio .Net 2005 (VC++ 8.0) with MFC 8.0

A Grand Tour of MFC

CObject: The Mother of (Almost) All Classes

It is MFC’s strategy, like JAVA Object. Classes derived from CObject inherit some useful capabilities, including run-time type identification for derived class, serialization (that is, persistence), diagnostic functions, and support for dynamic object creation.

CWnd: The Mother of All Windows

CWnd serves as a breeding ground for all of Windows’ visual interface objects like dialog box and controls (button, list box, edit and so on).

Dialogs:

CFileDialog – Selects a file from a directory.

CColorDialog – Selects a specific color.

CFontDialog – Selects a font.

CPrintDialog – Handles printer setup and printing.

CFindReplaceDialog – Selects text to search and replace.

Most important skill for Dialog: (In MFC, I only like this)

Dialog Data Exchange and Validation

CWinApp / CWinThread

CwinThread represents a thread of execution within an MFC program. Though earlier versions of MFC weren’t thread safe, version 3.0 and later are.

CWinApp, which is derived from CWinThread, represents the standard Windows application. CWinApp has overrideable functions you can use to initialize your app and perform termination cleanup to suit your own needs.

CString

Creating strings with the CString class is a snap. MFC’s CString class provides basic operations such as concatenation, comparison, and assignments.

Remaining:

1. GDI Support and Drawing Object – See GDI topic in MSDN

2. Document/View Architecture – like MVC concept.

3. OLE Support: OLE Control

4. ODBC Support / DAO Support

A Simple Code about Dialog Style Application

See HelloWorld project

The Key point for Dialog

1. The disagreeable buttons: OK and Cancel

They are very convenient for dialog box, but not for application. Because of terminating application when user clicks OK or Cancel (presses Enter or ESC), we have to modify these undesired codes.

Solution 1: Modify the message mapping (I don’t like it).

Solution 2: Modify the virtual function and add new message mapping.

2. The Message MAP: the macro instructions for message map.

(i) DECLARE_MESSAGE_MAP(), BEGIN_MESSAGE_MAP / END_MESSAGE_MAP()

(ii) Combine the Windows message with your routine

(iii) Don’t add any extra codes in the Message Map declaration

3. DDX / DDV (Dialog Data Exchange / Validation)

(i) DoDataExchange(CDataExchange* pDX)

(ii) The DDX_ and DDV_ macro instructions

(iii) UpdateData(true) and UpdateData(false)

4. WM_INITDIALOG / OnInitDialog()

When a dialog is created, Windows sends a WM_INITDIALOG message just before the dialog is displayed so that the application can initialize any controls in the dialog before they become visible to the end user.

5. GetDlgItem: To get handle of child window

The HWND is the key to control any windows (including button and etc). If you want access unknown windows, you just find their handle (HWND) and call some windows function (like SetWindowText) with it.

Windows Programming 簡介

Windows Programming Introduction

Contents:

1. Windows History

2. Aspect of Windows

3. Windows Programming with Pure API

(i) A simple code with MessageBox

(ii) A simple code with CreateWindow

Preface

The word “Windows” means that Microsoft Windows OS in the text.

Windows History

1983/11 – Microsoft announce Windows

1985/11 – Microsoft release Windows 1.0

1987/11 – Windows 2.0

1990/05/02 – Windows 3.0 (16-bit protected-mode)

1992/04 – Windows 3.1 (True Type Font, OLE)

1993/07 - Window NT

1995/08 – Windows 95

1998/06 – Window 98

Aspect of Windows

1. Both Window 98 and NT are 32-bit preemptive multitasking and multithreading graphical operation system.

* Earlier versions of Windows used a system of multitasking called “non-preemptive”. This meant that Windows did not use the system timer to slice processing time between the various programs running under the system.

2. Programs running in Windows can share routines that are located in other files called “dynamic-link libraries”. Windows includes a mechanism to link the program with the routines in the dynamic-link libraries.

Kernel: krnl386.exe for 16-bit, and kernel32.dll for 32-bit.

Kernel handles OS kernel traditional routines like memory management, I/O, and tasking.

User: user.exe for 16-bit, and user32.dll for 32-bit

User refers to the user interface, and implements all the windowing logic.



GDI: gdi.exe for 16-bit, and gdi32.dll for 32-bit.

GDI is the Graphics Device Interface, which allows a program to display text and graphics on the screen and printer.

Windows Programming with Pure API


A very simple code with MessageBox

#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, PSTR szCmdLine, int nCmdShow)

{

MessageBox(NULL, TEXT("Hello World!"), TEXT("First Example"), MB_OK | MB_ICONINFORMATION);

return 0;

}

1. The Header Files

The “windows.h” is a master include file that includes other Windows header files. And the most important and most basic of these header files are:

windef.h: Basic type definitions

winnt.h: Type definitions for Unicode support.

winbase.h: Kernel functions.

winuser.h: User interface functions.

wingdi.h: Graphics device interface functions.

2. Program Entry Point

WinMain:

hInstance

[in] Handle to the current instance of the application.

hPrevInstance

[in] Handle to the previous instance of the application. This parameter is always NULL. If you need to detect whether another instance already exists, create a uniquely named mutex using the CreateMutex function. CreateMutex will succeed even if the mutex already exists, but the function will return ERROR_ALREADY_EXISTS. This indicates that another instance of your application exists, because it created the mutex first.

lpCmdLine

[in] Pointer to a null-terminated string specifying the command line for the application, excluding the program name. To retrieve the entire command line, use the GetCommandLine function.

nCmdShow

[in] Specifies how the window is to be shown. This parameter can be one of the following values.

See MSDN

A very simple code with CreateWindow

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, PSTR szCmdLine, int nCmdShow)

{

static TCHAR szAppName[] = TEXT("Hello Window");

HWND hWnd;

MSG msg;

WNDCLASS wndclass;

wndclass.cbClsExtra = 0;

wndclass.cbWndExtra = 0;

wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);

wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wndclass.hInstance = hInstance;

wndclass.lpfnWndProc = WndProc;

wndclass.lpszClassName = szAppName;

wndclass.lpszMenuName = NULL;

wndclass.style = CS_HREDRAW | CS_VREDRAW;

if(!RegisterClass(&wndclass))

{

MessageBox(0, TEXT("This program requires Windows NT!"), szAppName, MB_OK | MB_ICONERROR);

return 0;

}// if(!RegisterClass(&wndclass))

hWnd = CreateWindow(szAppName,

TEXT("The Hello Program"),

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

NULL,

NULL,

hInstance,

NULL);

ShowWindow(hWnd, nCmdShow);

UpdateWindow(hWnd);

while(GetMessage(&msg, NULL, 0, 0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}// while(GetMessage(&msg, NULL, 0, 0))

return msg.wParam;

}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

HDC hDC;

PAINTSTRUCT ps;

RECT rect;

switch(msg)

{

case WM_CREATE:

return 0;

case WM_PAINT:

hDC = BeginPaint(hWnd, &ps);

GetClientRect(hWnd, &rect);

DrawText(hDC, TEXT("Hello World~~~~"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

EndPaint(hWnd, &ps);

return 0;

case WM_DESTROY:

PostQuitMessage(0);

return 0;

}// switch(msg)

return DefWindowProc(hWnd, msg, wParam, lParam);

}

An Architectural Overview

* Remark: “Windows sends a message to the program” is meant that Widnows calls a function within the program – a function that you write and which is an essential part of your program’s code.

1. A windows including sub-windows like button, radio box, check box, and etc. is always created based on a “window class”.

2. When a Windows program begins execution, Windows creates a “message queue” for the program.

3. Message Loop and WndProc

WM_CREATE:

WM_PAINT:

WM_DESTROY:

PostQuitMessage(0) -> return msg.wParam

2008年3月26日 星期三

CakePHP 1.2 Model 資料驗証 (Data Validation) 簡介

Data Validation 是 Model 中最重要的功能,也是最重要的程式區塊。當要對資料庫進行Insert, Update時,都會執行這個區塊要設定來檢查資料是否正確。一般而言,寫網站程式會在對資料庫存取(不管是select, insert, update)都會先對資料做初步的檢查(例如:不能是空或空白字串;一定要數字;符合某種格式 - regex 檢查)。CakePHP在方面的設計,比1.1增強許多,在1.1的版本,只有Regex檢查,到了1.2可以針對單一個欄位做多種規則的檢查,也可以自訂函式來做驗証。CakePHP內建的規則可以查 your_cakephp1.2/cake/libs/validation.php

CakePHP的資料驗証設定很彈性,因此在設定上的寫法花樣很多,以下只例舉我常用的做法:

public $validate = array(
"account" => array(
/* 檢查是不是數字與英文字母的組合 */
"alphanumberic" => array(
"rule" => "alphaNumeric",
"message" => "只能是數字或英文字母",
"last" => true
), /* end alpha-numeric */
/* 檢查字串的長度在4~20之間 */
"length" => array(
"rule" => array("between", 4, 20),
"message" => "您的帳號長度不正確,只能 4~20字元",
"last" => true
), /* end length */
/* 檢查是不是唯一值 (自訂函式) */
"unique" => array(
"rule" => array("checkUnique", "account"),
"message" => "這個帳號已經註冊,請重新取一個",
"on" => "create"
) /* end unique */
),/* end for account */

}

資料驗証的使用方式是 "自訂的規則名稱" => array("rule" => "內建或自訂的規則", "message"=>"發生錯誤時,要顯示的訊息"),其中 "rule" => "內建或自訂的規則" ,如果使用的規則是Regex或者沒有額外參數的函式(像alphaNumeric,只要傳值去做驗証即可,不需要其他額外的參數),那就可以以字串的方式設定。相反的,如果規則是函式,且需要額外的參數時(像 between, 需要有最大、最小值),那就要以array的方式設定。

以下是其他參數的說明:

on: 值有"create"和"update",如果值是"create",意思是指在做insert時,才會使用這個驗証規則,同理是"update"時,只有在update資料庫時,才會使用。如果都不做設定的話,不論是要做insert或update,只要Controller有把這個欄位的值傳進來,就會使用這個規則做驗証。

last: 設定成 true 時(CakePHP預設值是false),是告知CakePHP只要這個規則沒通過時,就不要再執行同欄位的其他規則。

自訂函式說明:

以下是程式碼是截自CakePHP官網的說明,請見 http://book.cakephp.org/view/125/data-validation

function checkUnique($data, $fieldName) {
$valid = false;
if(isset($fieldName) && $this->hasField($fieldName))
{
$valid = $this->isUnique(array($fieldName => $data));
}
return $valid;
}

在寫自訂的函式時,請注意,第一個參數要保留給CakePHP把欄位的值傳進來,從第二個參數開始才是自訂的參數。像上面的checkUnique,$data是指傳進來的欄位值,$fieldName是指要檢查的欄位名稱。

參考資料:

http://book.cakephp.org/view/125/data-validation

CakePHP 1.2 Controller 簡介

依據MVC的概念,Controller通常都是負責網站規則、流程的控管,CakePHP也不例外。在程式的安排上,CakePHP建議一個Controller負責一個Model相關的網站規則與流程,簡單來說,在寫Controller時,如果沒特別的需求,一個Controller最好只使用一個Model;一來網站的程式好管理,二來Controller的程式在執行過程也減少記憶體的浪費。因此在寫網站時,可以依據這個法測,來安排規劃程式。Controller的method通常稱作Action,而每個Action,如果沒有特殊需求,通常會有專屬的View。Controller通常是繼承CakePHP的AppController (your_cake1.2/cake/libs/controller/app_controller.php),從source code來看,其實Controller也可以直接繼承 Controller(your_cake1.2/cake/libs/controller/controller.php),視需要而定。

Controller 屬性:
$name: 顧名思義,也就是Controller的名稱,但要注意的是,不是Controller的ClassName。例如:PostController (檔名:post_controller.php),那它的 $name 應該是 Post 而不是 PostController;而且$name這個變數,在CakePHP的設計中,如果Controller程式沒有修改使用那些Model的話,它預設會去找相同名稱的Model,也就是Post這個Model。

$uses: 設定使用那些Model,如果都沒使用Model時,可以設定成 null,如果在程式中,沒有修改這個值的話,則CakePHP會去尋找和Controller相同名稱的Model。要使用model時,以array的方式來設定。Ex: public $uses = array('Post');

$components: 在CakePHP的設計中,components是指協助Controller處理網站規則與流程。如網站常用會用到的Session, Cookie等,CakePHP本身也內建了相對應的component來協助處理。如果要使用component的話,以array的方式來設定即可。Ex: public $uses = array('Session', 'Cookie');

$helpers: 在CakePHP的設計中,components是指協助處理版型的,也就是View。如果沒有設定時,Controller會預設使用 Html 及 Form 這兩個Helper。同理,如果要使用其他的Helper時,以array的方式設定。Ex: public $helpers = array('Form', 'Ajax');

$layout: 指定Controller要使用那個外框的版型,預設是 default.ctp (your_cake1.2/app/views/layouts/defautl.ctp or your_cake1.2/cake/libs/view/layouts/default.ctp)。可以在Controller或者Action中,重新設定這個值,來使用不同的外框版型。Ex: $this->layout='ajax'; /* 會使用 layouts/ajax.ctp */

$pageTitle: 設定版型中 HTML的Title。使用的前題是,在外框的版型上,要保留 <?php echo $title_for_layout; ?> (請參考 CakePHP 1.2 第一個程式,換掉CakePHP說明首頁 )。

參數屬性:
$this->data: 在CakePHP的設計上,如果你的版型上,是寫
<input name="data[User][first_name]" value="" type="text" /> 且用POST傳給Controller時,那麼在Controller的Action中,就可以使用 $this->data['User']['first_name'] 來讀取這個值。(註:只有 POST 才行)。

Controller常用的函式:

set(string $var, mixed $value): 這個函式最常用,主要是要把資料,傳給View使用。

redirect(string $url, integer $status, boolean $exit): 轉址,類似 header('Location: /help.html'); 功能。

flash(string $message, string $url, integer $pause): 跳轉畫面。會先出現一段訊息後,再自動跳轉到指定的網址。

Callbacks:
beforeFilter(): 在呼叫任何Action之前,會先執行這個函式。

beforeRender(): 在產生View的結果前,會先執行這個函式。

afterFilter(): 在呼叫任何Action之後,會再執行這個函式。

afterRender(): 在產生View的結果之後,會再執行這個函式。

詳細的執行順序,請見 CakePHP處理HTTP Request 典型的流程

其他常用的函式:
loadModel('ModelName'): 自行載入要使用的Model。

disableCache(): 告知瀏覽器不要cache 網頁。

paginate(): 分頁的功能,請見 CakePHP Pagination (分頁功能) , CakePHP Pagination (分頁功能) 加入自己的參數

requestAction(string $url, array $options): 顧名思意,就是呼叫其他Controller的Action。如果需要被呼叫的Action的版型,請在options加'return'Ex: echo $this->requestAction('/post/all', array('return')); 如此就可以在網頁上顯示這個Action的結果。同理,不需要版型的話,就不要傳入'return'。這個函式可以應用的地方很多,也可以在View裏面使用(例如在某個action的.ctp用使用),簡單來說,可以視作一種呼叫函式的方法。

參考資料:http://book.cakephp.org/view/49/controllers



2008年3月25日 星期二

Open SSL 的 NO OPENSSL_Applink 錯誤

最近要寫一個透過Gmail來寄信的小程式,因為gmail的SMTP是有加SSL的,自然要找上Open SSL 這個好用的Open Source來做。

我用的環境是Windows XP + Vistual Studio 2005,在使用的過程中,可能有的人會發生 No OPEN_SSL_AppLink 的錯誤,在OpenSSL的FAQ中,有特別提到這個會錯誤發生的原因,主要是在編譯Open SSL時,預設的參數是 /MD (MultiThread DDL),而像我用的WTL + Vistual C++ 8,預設的編譯環境是 /MTD (MultiThread Debug),兩者編譯的環境不同,就會發生這個錯誤。

要解決這個問題,FAQ說要改成相同的編譯環境即可,我是要維持MTD的環境,所以只好去改OpenSSL產生的make檔(nt.mak),把/MD這個參數改成 /MTD。

如果有相同問題的人,可以試看看。

如何使用IWebBrowser2 加入自定的Header - MFC

相信很多人都曾有過在自己的視窗程式中,鉗入一個IE元件,也就是IWebBrowser2這個Interface,最近我也在寫類似的功能,這次要在HTTP的Header中,加入自訂的設定與資訊;在google找到的範例中,對於GET是可行的,也已經解決一半的問題,但是在遇到需要用POST時,會發現這些範例,不論怎麼傳,都還是GET的方式。
加入自訂的Header方式,主要是利用BeforeNavigate2這個Event。如果是用MFC來實作的話,你會有一個類似
BeforeNavigate2Explorer1(LPDISPATCH pDisp, VARIANT* URL, VARIANT* Flags, VARIANT* TargetFrameName, VARIANT* PostData, VARIANT* Headers, BOOL* Cancel) 這個函數。
首先在這個Event中,把Cancel設成TRUE (*Cancel = TRUE),然後再這個函數中,再呼叫一次Navigate2,並傳入自訂的header。
以下是我的方式。(請不要照抄,因為只是說明,並不是可以執行的程式)
void CMyDlg::BeforeNavigate2Explorer1(LPDISPATCH pDisp, VARIANT* URL, VARIANT* Flags, VARIANT* TargetFrameName, VARIANT* PostData, VARIANT* Headers, BOOL* Cancel)
{
// 用來判斷是不是自己發出的Navigate2,一開始m_bIsReNavigate是FALSE!
this->m_bIsReNavigate = !this->m_bIsReNavigate;

if(this->m_bIsReNavigate) {
*Cancel = TRUE;
_variant_t header;
// 加入自己的Header ex: AAA:BBBrn
header.SetString(my_header_string);

_variant_t p;
_variant_t e(VT_EMPTY);
// Copy使用者傳的POST資料,請用VariantCopyInd,不要用VariantCopy;因為PostData有safearray,如果用VariantCopy,並不會把array複製進來。
VariantCopyInd(&p, PostData);
// 重新再Request一次
this->m_browser.Navigate2(URL, &e, &e, &p, &header);
}// if(this->m_bIsReNavigate)
}

CakePHP Pagination (分頁功能) 加入自己的參數

如果還不知道什麼是 Pagination 或者還不了解如何使用,請參考: CakePHP Pagination (分頁功能)

通常在管理後台實作時,常設定許多查詢條件來查詢資料,比如:起始、結束時間。通常這些參數都是用GET的方式在傳遞。以下的範例程式,主要說明如何把GET的參數,融入CakePHP的分頁功能裏面。

Controller:post_controller.php

class PostController extends AppController {
public $name = 'Post';
public $uses = null;

function all() {
// 分頁設定
$this->paginate = array(
'fields' => array('Article.id', 'Article.title', 'Article.created'),
'limit' => 3,
'order' => 'Article.created desc',
);

/* 如果有自訂的GET參數,取得到,加到 Controller 的 passedArgs */
$tmp = '';

if(isset($_GET['a']) && ($tmp = trim($_GET['a'])) != "")
$this->passedArgs['a'] = $tmp;
if(isset($_GET['b']) && ($tmp = trim($_GET['b'])) != "")
$this->passedArgs['b'] = $tmp;

$this->loadModel("Article");
/*
$this->Article->recursive = false;*/

$this->Article->recursive = 0;

$this->set('articles', $this->paginate($this->Article,"Article.user_id='".$this->UserID."'"));

}// all

}// enc Post
?>

View: all.ctp

<?php
$args = $this->passedArgs;
unset($args['page']); /* 記得要去除 page 參數,否則 CakePHP 會當成一般的參數處理,屆時連結會多出很多page參數 */
// 把額外的參數傳給分頁模組
$paginator->options(
array('url'=>$args));
$th = array($paginator->sort("標題", "title"), $paginator->sort("建立時間", "created"), "操作");
echo $html->tableHeaders($th);

$size = count($articles);
for($i = 0; $i < $size; $i++) {
$tr = array($articles[$i]["Article"]["title"], $articles[$i]["Article"]["created"], "<a href='/post/edit/".$articles[$i]["Article"]["id"]."'>編輯</a>");
echo $html->tableCells($tr, array("bgcolor"=>"#999999"), array("bgcolor"=>"white"));

}// end for
?>
</table>
<center>
<?php
echo $paginator->first('第一頁'). ' ';
echo $paginator->prev('<< 前一頁').' ';
echo $paginator->numbers(array('separator'=>' - '));
echo ' '.$paginator->next('下一頁 >>');
echo ' '.$paginator->last("最後一頁");
?>
<br>
<?php
echo $paginator->counter(array(
'format' => '目前在第 %page% 頁 / 總共 %pages% 頁, 每頁 %current% 筆資料, 總共 %count% 筆, 目前是第 %start% 至 %end% 筆資料'
));
?>
</center>

HTML

<table border='1' bordercolor='#cccccc' cellspacing='0' cellpadding='5' width='80%' align='center'>
<tr><th><a href="/post/all/3/page:2/a:c/b:d/sort:title/direction:desc">標題</a></th>

<th><a href="/post/all/3/page:2/a:c/b:d/sort:created/direction:asc">建立時間</a></th>

<th>操作</th>

</tr>

<tr bgcolor="#999999>資料顯示在這裏(略)</tr></table>
<center>
<span><a href="/post/all/3/page:1/a:c/b:d/sort:title/direction:asc">第一頁</a></span> <a href="/post/all/3/page:1/a:c/b:d/sort:title/direction:asc">&lt;&lt; 前一頁</a> <span><a href="/post/all/3/page:1/a:c/b:d/sort:title/direction:asc">1</a></span> - <span class="current">2</span> - <span><a href="/post/all/3/page:3/a:c/b:d/sort:title/direction:asc">3</a></span> - <span><a href="/post/all/3/page:4/a:c/b:d/sort:title/direction:asc">4</a></span> <a href="/post/all/3/page:3/a:c/b:d/sort:title/direction:asc">下一頁 &gt;&gt;</a> <span><a href="/post/all/3/page:4/a:c/b:d/sort:title/direction:asc">最後一頁</a></span><br>
目前在第 2 頁 / 總共 4 頁, 每頁 3 筆資料, 總共 12 筆, 目前是第 4 至 6 筆資料</center>

由上面,可以看到自訂的參數也是以 /參數名稱:參數值 的方式,加到連結裏面。同理,在controller 中,可以透過passedArgs取得以/參數名稱:參數值的數值。

CakePHP Pagination (分頁功能)

原本 1.1 版本是沒有分頁功能,但可以藉由 Bakery 上的討論區找到寫好的 Component 和 Helper。到了1.2版之後,CakePHP很貼心地內建了這項功能。在開發網站上,分頁是一項很常用的功能,有了這項功能,更可以讓開發的速度加快。

分頁的功能,官網上面已經有說明了,Bakery 上也有很多相關的說明,都可以參考。我寫了一個簡單的範例程式來說明CakePHP的pagination如何使用。

Controller:post_controller.php

class PostController extends AppController {

public $name = 'Post';
public $uses = null;

function all() {
$this->paginate = array(
'fields' => array('Article.id', 'Article.title', 'Article.created'), /* 選擇要顯示的資料庫欄位 */
'limit' => 3, /* 每頁幾筆資料 */
'order' => 'Article.created desc' /* 預設的排序方式 */
);

$this->loadModel("Article"); /* 載入 Model */
//$this->Article->recursive = false; /* 如果 Model 有 hasOne, hasMany 等對應關係,且不需要時,把 recursive 設成 false */

$this->Article->recursive = 0; /* 如果 Model 有 hasOne, hasMany 等對應關係,且不需要時,把 recursive 設成 0 */

$this->set('articles', $this->paginate($this->Article,"Article.user_id='".$this->UserID."'")); /* 使用 paginate 函式取得資料,第二個參數可設定條件 */
}// all

}

View: all.ctp

<table border='1' bordercolor='#cccccc' cellspacing='0' cellpadding='5' width='80%' align='center'>
<?php
$th = array($paginator->sort("標題", "title"), $paginator->sort("建立時間", "created"), "操作"); /* 在版型的標題上,加入可以針對欄位做排序的功能 */
echo $html->tableHeaders($th);

$size = count($articles);
for($i = 0; $i < $size; $i++) {
$tr = array($articles[$i]["Article"]["title"], $articles[$i]["Article"]["created"], "<a href='/post/edit/".$articles[$i]["Article"]["id"]."'>編輯</a>"); /* 顯示資料 */
echo $html->tableCells($tr, array("bgcolor"=>"#999999"), array("bgcolor"=>"white"));

}// end for
?>
</table>
<center>
<?php
echo $paginator->first('第一頁'). ' ';
echo $paginator->prev('<< 前一頁').' ';
echo $paginator->numbers(array('separator'=>' - '));
echo ' '.$paginator->next('下一頁 >>');
echo ' '.$paginator->last("最後一頁");
?>
<br>
<?php
echo $paginator->counter(array(
'format' => '目前在第 %page% 頁 / 總共 %pages% 頁, 每頁 %current% 筆資料, 總共 %count% 筆, 目前是第 %start% 至 %end% 筆資料'
));
?>
</center>

最後得到的HTML碼會是:

<table border='1' bordercolor='#cccccc' cellspacing='0' cellpadding='5' width='80%' align='center'>
<tr><th><a href="/post/all/page:1/sort:title/direction:asc">標題</a></th>

<th><a href="/post/all/page:1/sort:created/direction:desc">建立時間</a></th>

<th>操作</th>

</tr>

<tr bgcolor="#999999>資料顯示在這邊(略)</tr></table>
<center>
<span class="current">1</span> - <span><a href="/post/all/page:2">2</a></span> - <span><a href="/post/all/page:3">3</a></span> - <span><a href="/post/all/page:4">4</a></span> <a href="/post/all/page:2">下一頁 &gt;&gt;</a> <span><a href="/post/all/page:4">最後一頁</a></span><br>
目前在第 1 頁 / 總共 4 頁, 每頁 3 筆資料, 總共 12 筆, 目前是第 1 至 3 筆資料</center>

由上面的HTML可以發現CakePHP已經將分頁的功能,自動做好了,而且分頁的參數是以 /page:n 的方式產生。在標題的部分,也做好排序的功能,可以試著點選看看,會自動做遞增、遞減的排序。

參考資料:

官網上有關 Pagination 說明:http://book.cakephp.org/view/164/pagination

Bakery上的討論:

http://bakery.cakephp.org/articles/view/basic-pagination-overview-3

http://bakery.cakephp.org/articles/view/advanced-pagination-1-2

http://bakery.cakephp.org/articles/view/pagination-recall

CakePHP處理HTTP Request 典型的流程

圖片來源:A Typical CakePHP Request

參考資料: A Typical CakePHP Request

上圖其實也說明了MVC的架構概念以及處理流程!

1. 使用者瀏覽網站(例加:瀏覽 /cake/buy,cake是controller, buy是action,也就是呼叫cake_controller.php 中的 buy函式),這時 your_cakephp1.2/app/webroot/index.php 會收到經由 mod_rewrite 轉換的網址(mod_rewrite會將 /cake/buy 轉成 index.php?url=/cake/buy),在index.php中,使用Dispatcher(your_cakephp1.2/cake/dispatcher.php),把網址傳給Dispatcher。以下是index.php的部分程式碼:

$Dispatcher = new Dispatcher();
$Dispatcher->dispatch($url);

2. Dispatcher中,透過Router (your_cakephp1.2/cake/libs/router.php),取得Controller和Action的名稱。

3. 在開始使用Controller的Action之前,先呼叫這個controller的beforeFilter(也就是圖上 Controller的藍色框所表示的意思)。

4.5.6. 經過 controller 的 beforeFilter 之後,開始呼叫 Action 函式,一般在Action 函式中,會使用者Models來處理資料庫相關的工作,用Components處理像Cookie, Session的相關工作,視你的程式而定,最後將資料透過 set() 的方式,傳給 View 處理。

7. 在View處理版型前,會呼叫 controller 的 beforeRender (也就是 7 箭頭上的藍框)。一般網頁的版型,會有layout、element和自身action的view版型組成,其中會使用helper來協助處理版型。

8. 在處理完版型,回覆給使用者前,Dispatcher會再呼叫 controller 的 afterFilter (也就是 8 箭頭上的藍框。註)

以上就是很典型CakePHP處理Request的流程。

註:

CakePHP官網上說明 Controller 有四個callback函式:beforeFilter, beforeRender, afterRender, afterFilter。不過我在 cake/libs/controller/controller.php 找不到afterRender的定義,也找不到afterRender使用的地方,不知道是我還沒讀透程式碼,還是官方誤植了這個函式。因此上圖的8 箭頭的另一個藍框,應該是指afterRender。

CakePHP 1.2 第一個程式,換掉CakePHP說明首頁。

前提:你已經做好相關設定,如果還沒有的話,請見:CakePHP 1.2 簡易安裝與設定

I. CakePHP 目錄說明:

在寫程式之前,先了解CakePHP是如果安排程式的目錄。基本上,1.1版和1.2版沒有什麼差別。

  • app
  • cake
  • docs
  • index.php
  • vendors

app: 主要是放你網站的程式與靜態的html網頁。

cake: CakePHP的核心程式,當有問題時,也可以到這邊來拆解CakePHP的程式;因為目前CakePHP的說明還不是非常的完整,有時會需要用拆解程式的方式來了解如何使用。

docs: 顧名思義,就是放一些CakePHP的說明文件。

vendors: 如果你有使用其他open source的套件,可以放在這個地方。

app內的目錄:

config: 放設定檔的目錄,可以參考 CakePHP 1.2 簡易安裝與設定
controllers: 放controller程式的目錄,不懂什麼是Controller? 請參考:CakePHP說明 - Controller
locale: 放多國語言的訊息設定檔,這個也是CakePHP 1.2 的新功能。
models: 放model 程式的目錄,不懂什麼是Model? 請參考:CakePHP說明 - Model
plugins: 放別人寫好的CakePHP程式。
tmp: 顧名思義,放一些暫存檔的目錄,有 log, cache 等。
vendors: 同上面的vendors。

views: 放網路程式版型的目錄。請參考:CakePHP說明 - View

webroot: 放靜態網頁的目錄滿,包含圖片, javascript, css等。

參考網頁: http://book.cakephp.org/view/19/cakephp-file-structure

II. 開始你的第一個程式:home_controller.php

首先在 your_cakephp1.2/app/controller 下產生一個檔名是 home_controller.php (為何是 程式_controller.php 方式命名?這是cakephp的規則,請照做!),內容如下:

<?php

class HomeController extends AppController {
public $name = 'Home';
public $uses = null;

// 首頁
public function index() {

}// end index

}

?>

在 your_cakephp1.2/app/views 下,產生一個目錄是 home,在 your_cakephp1.2/app/view/home/ 下,產生一個檔名是 index.ctp (註:1.1的副檔名是.thtml,1.2是 .ctp)。index.ctp內容如下:

<div>

<?php

echo 'hello world';

?>

</div>

程式完成後,請到 app/config下,修改 routes.php 把預設的 Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home')); 改成 Router::connect('/', array('controller' => 'home', 'action' => 'index', 'index')); 下面另外兩個設定,基本上也不需要了,可以把它們註解!

完成上面的步驟,再重新訪問你的網站,就可以看到你首頁的內容了。不過版型還是很奇怪,主要是因為現在的版型外框還是套用cakePHP的版型。請copy一份 your_cakephp1.2/cake/libs/layouts/default.ctp 到 your_cakephp1.2/app/views/layouts下。原本的 default.ctp 內容如下:

<?php

/* 一堆註解... (省略)*/
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="
http://www.w3.org/1999/xhtml">
<head>
<title>
<?php __('CakePHP: the rapid development php framework:'); ?>
<?php echo $title_for_layout; ?>
</title>
<?php
echo $html->charset();
echo $html->meta('icon');

echo $html->css('cake.generic');

echo $scripts_for_layout;
?>
</head>
<body>
<div id="container">
<div id="header">
<h1><?php echo $html->link(__('CakePHP: the rapid development php framework', true), 'http://cakephp.org'); ?></h1>
</div>
<div id="content">
<?php
if ($session->check('Message.flash')):
$session->flash();
endif;
?>

<?php echo $content_for_layout; ?>

</div>
<div id="footer">
<?php echo $html->link(
$html->image('cake.power.gif', array('alt'=> __("CakePHP: the rapid development php framework", true), 'border'=>"0")),
'http://www.cakephp.org/',
array('target'=>'_new'), null, false
);
?>
</div>
</div>
<?php echo $cakeDebug; ?>
</body>
</html>

其實index.ctp,可以當成是.php程式,可以有html和php程式碼。

Header內的程式碼:

<?php __('CakePHP: the rapid development php framework:'); ?> 這一行可以刪除,網頁的title總不能顯示不是我們網站的訊息吧。

<?php echo $title_for_layout; ?> 建議保留,如此一來,每個Controller的action可以自行定義網頁的title。當然如果不需要的話,也可刪除,直接填上你的網站title。
echo $html->charset();
建議保留,會去讀取 core.php 的App.encoding設定(預設是UTF-8)。

echo $html->meta('icon'); favicon 設定,可以刪除,改成自己的

echo $html->css('cake.generic'); css 設定,可以刪除,改成自己的。

echo $scripts_for_layout; javascript 程式,可以刪除。


Body內的程式碼:

除了 <?php echo $content_for_layout; ?> 外,其他都可以替換成自己的內容。<?php echo $content_for_layout; ?>主要是CakePHP處理版型時會用到的,也就是顯示剛剛的index.ctp內容的地方。另外,<?php echo $cakeDebug; ?> 也建議保留,在開發模式(core.php中的 Configure::write('debug', 3))時,會顯示所有的變數內容,當改成release模式時(core.php中的 Configure::write('debug', 0)),這裏是不會顯示任何內容的!

如果完成上面的步驟,已經改寫首頁了!

參考內容:

我之前的文章(CakePHP 1.1版本)

CakePHP 說明 - 什麼是MVC架構

CakePHP說明 - Controller

CakePHP說明 - Model

CakePHP說明 - View

CakePHP 1.2 簡易安裝與設定

首先到CakePHP官網 ,下載目前1.2 beta 版並解壓縮。

安裝 Apache + PHP + MySQL,安裝Apache時,建議安裝mod_rewrite。

I. 設定 Apache

在安裝Apache時,建議安裝mod_rewrite,讓你的網站網址更容易SEO接受與分析。

新增一組 Directory 設定,目錄是你的 your_cakephp1.2/app/webroot。設定如下:

<Directory "your_cakephp1.2/app/webroot/">
Options FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>

請設定 AllowOverride All,如此mod_rewrite會參照cakephp目錄中的.htaccess來做轉址。

最後將你的 DocumentRoot 指到your_cakephp1.2/app/webroot。

如果沒有意外的話,你就可以看到cakephp的說明首頁。

最後測試你的mod_rewrite有沒有成功,如果 http://your_domain/pages/displayhttp://your_domain/ 是同一頁,

那就ok了!

II. 設定 Cakephp

還沒開始設定 Cakephp之前,你的Cakephp的說明首頁會有許多警告訊息。

1. DB 設定

在 your_cakephp1.2/app/config/ 有 database.php.default 請copy 或 rename 成 database.php,依照你的Database帳號/密碼設定,加到這個文件檔中。Cakephp預設是使用MySQL,所以設定上應該沒什麼太大的問題。預設有兩組設定,一個是default, 一個是test,如果沒有特別的需求,就把這兩組都設成一樣。

2. Core 設定

在 your_cakephp1.2/app/config 的core.php,有很多cakephp的相關設定,當然你也可以把自己網站上的共用設定寫在這邊,寫法如同cakephp,使用Configure::write;這與cakephp1.1版使用define方式不同,屆時要讀取設定時,請用Configure::read

core.php中,最常用到的設定有:

(1). Configure::write('debug', 2); 這是設定你的網站目前是在開放給外人使用的模式,還是開發模式;簡單來說,當你要開放你的網站時,請把debug設定 0。 通常開發時,是設定成 2;在開發的過程中,你會看到cakephp是如何執行SQL命令。如果你要看更詳細的資訊,包括整個程式的所有變數值,那就設定成 3

(2). Configure::write('Cache.check', true); 這是使用網頁Cache機制的開關,預設是註解的,通常網站都會需要做cache,建議在開發時,先關閉這項功能,等到開發後期調整cache時,再打開使用。

(3). Configure::write('Session.save', 'php'); Session的處理方式,預設是使用php的方式來處理Session。Cakephp也有內建模組,使用Database來處理Session。如果你的網站主機有很多台機器時,那也許可以改用database的方式;另外建議把放Session的table設定在Memory(Heap)的模式,可以加快處理速度。

(4). Configure::write('Security.salt', 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi'); 在你的說明首頁上,一開始也會看到這則警告,請把這個值改成你自己的亂數字串即可。這個設定主要是使用在Cookie;Cakephp的Cookie模組有支援cipher加密功能,屆時Cookie會依照這個值來做加密。

(5). Cache::config('default', array('engine' => 'File')); Cache 設定,預設是用檔案的方式來做cache;Cakephp也內建模組,可以使用database, xcache, memcache, apc 等常見的方法來做cache。如果你是使用database也建議你把cache的table設定在Memory(Heap)的模式,可以加快處理速度。

III. 開始寫程式

如果都設定完成,基本上就可以開始寫程式了!

相關網站:

CakePHP官網:http://www.cakephp.org

1.1手冊: http://manual.cakephp.org/

1.2手冊: http://book.cakephp.org

API說明:http://api.cakephp.org/

CakePHP程式碼討論與分享: http://bakery.cakephp.org/