表單由元素組成,它一般對應於 HTML 表單輸入。Zend_Form_Element 封裝了單個表單元素,並完成下列工作: 校正(提交的資料有效乎。) 抓取校正錯誤碼和訊息 過濾(在校正和/或輸出之前元素如何轉義或正常化。) 解析(元素如何顯示。) 中繼資料和屬性(什麼資訊進一步修飾元素。)
基礎類 Zend_Form_Element 對許多類有合理的預設設定,但最好還是繼承這個類來完成特殊意圖的元素。另外,Zend Framework 帶有許多標準的 XHTML 元素。
1. 外掛程式載入器
Zend_Form_Element 利用 Zend_Loader_PluginLoader 來使開發人員指定替代校正器、過濾器和裝飾器的位置。每個都有它自己攜帶的外掛程式載入器,並使用通用的訪問器來讀取或修改它們。
下列載入器類型和各種各樣的外掛程式載入器方法一起使用:'validate'、 'filter' 和 'decorator'。類型名是大小寫敏感的。
外掛程式載入器互動使用的方法如下: setPluginLoader($loader, $type):$loader 是外掛程式載入器對象自己,而 $type 是上述類型之一。這個方法為給定類型的外掛程式載入器設定為最新的特定的載器對象。 getPluginLoader($type): 用攜帶的 $type 來讀取外掛程式載入器。 addPrefixPath($prefix, $path, $type = null): 添加 prefix/path 聯合給由 $type 指定的載入器。如果 $type 是 null,它將嘗試通過追加首碼給 "_Validate"、"_Filter", 和 "_Decorator" 來添加路徑到所有載入器,並用 "Validate/"、"Filter/" 和 "Decorator/". 追加路徑。如果你在通用的等級下有所有額外的表單元素類,這是個用來設定基礎首碼給它們的方便的方法。 addPrefixPaths(array $spec): 讓你一次性添加許多重路徑給一個或多個外掛程式載入器。它需要每個數組條目是個帶有 'path'、'prefix' 和 'type'. 鍵的數組。
定製的校正器、過濾器和裝飾器是在表單和封裝定製功能之間共用功能的簡單的辦法。
Example 1. 定製標籤
外掛程式的一個普通用例是來為標準類提供替換。例如,如果想實現一個不同的 'Label' 裝飾器 -- 如總要追加冒號 --你可以建立你自己的帶有類首碼的 'Label' 裝飾器,然後把它加到你的首碼路徑。
讓我們從定製標籤裝飾器來開始,給它一個類首碼"My_Decorator",類檔案就是 "My/Decorator/Label.php"。
<?phpclassMy_Decorator_Label extends Zend_Form_Decorator_Abstract{ protected $_placement = 'PREPEND'; public function render($content) { if (null === ($element =$this->getElement())) { return $content; } if (!method_exists($element,'getLabel')) { return $content; } $label = $element->getLabel() . ':'; if (null === ($view =$element->getView())) { return$this->renderLabel($content, $label); } $label =$view->formLabel($element->getName(), $label); return $this->renderLabel($content,$label); } public function renderLabel($content,$label) { $placement = $this->getPlacement(); $separator = $this->getSeparator(); switch ($placement) { case 'APPEND': return $content . $separator .$label; case 'PREPEND': default: return $label . $separator .$content; } }}
現在,當元素尋找裝飾器時,就使用這個外掛程式路徑:
$element->addPrefixPath('My_Decorator','My/Decorator/', 'decorator');
另外,我們可以在表單一級來做以確保所有的裝飾器使用這個路徑:
$form->addElementPrefixPath('My_Decorator','My/Decorator/', 'decorator');
用這個添加的路徑,當添加一個裝飾器,將首先搜尋 'My/Decorator/' 路徑來檢查是否存在裝飾器。結果,如果請求 'Label' 裝飾器,'My_Decorator_Label' 將被使用。
2. 過濾器
在校正之前對輸入執行正常化常常是有用的並/或必需的 - 例如,你可能想要剝離所有 HTML,在剩下的東東上運行校正來確保提交有效。或者你可能想把輸入的資料兩邊的空格都消掉,這樣 StringLength 校正器就不會返回失敗。這些操作使用 Zend_Filter 來執行,並且 Zend_Form_Element 對過濾鏈有支援,讓你指定多個連續的過濾器來用。在校正期間和通過 getValue() 讀取元素值的時候都會發生校正:
<?php$filtered =$element->getValue();?>
有兩個辦法添加過濾器: 傳遞一個具體的過濾器執行個體 提供過濾器名稱 - 短名或完整的類名都可以
看一些例子:
<?php// 具體的過濾器執行個體:$element->addFilter(newZend_Filter_Alnum()); // 合格的全類名:$element->addFilter('Zend_Filter_Alnum'); // 短過濾器名:$element->addFilter('Alnum');$element->addFilter('alnum');?>
短名一般就是過濾器名去掉首碼,預設就是去掉 'Zend_Filter_' 首碼。另外,首字母不需要大寫。
| |
使用定製的過濾器類 |
| 如果你有自己的一組過濾器,可以通過 addPrefixPath() 來告訴 Zend_Form_Element。例如,如果你在 'My_Filter' 首碼下有過濾器,這樣來告訴 Zend_Form_Element: <?php$element->addPrefixPath('My_Filter', 'My/Filter/', 'filter');?>
(回憶一下第三個參數用來指示哪個是執行這個動作的外掛程式載入器) |
任何時候需要非過濾的資料,使用 getUnfilteredValue() 方法:
<?php$unfiltered =$element->getUnfilteredValue();?>
過濾器帶有這些方法: addFilter($nameOfFilter, array $options = null) addFilters(array $filters) setFilters(array $filters) (重寫所有過濾器) getFilter($name) (按名字讀取過濾器對象) getFilters() (讀取所有過濾器) removeFilter($name) (按名字刪除過濾器) clearFilters() (刪除所有過濾器)
3. 校正器
如果你贊同安全咒語“過濾輸入,轉義輸出”,你將會校正(“過濾輸入”)你的表單輸入。在 Zend_Form 裡,每個元素包含它自己的由 Zend_Validate_* 校正器組成的校正器鏈。
兩個辦法添加校正器到校正器鏈: 傳遞一個具體的校正器執行個體 提供一個校正器名 - 短名或者完整的類名都可以
看一些例子:
<?php// Concrete validatorinstance:$element->addValidator(newZend_Validate_Alnum()); // Fully qualifiedclass name:$element->addValidator('Zend_Validate_Alnum'); // Short validatorname:$element->addValidator('Alnum');$element->addValidator('alnum');?>
短名一般就是校正器名去掉首碼,預設就是去掉 'Zend_Validate_' 首碼。另外,首字母不需要大寫。
| |
使用定製的校正器類 |
| 如果你有自己的一組校正器,可以通過 addPrefixPath() 來告訴 Zend_Form_Element。例如,如果你在 'My_Validator' 首碼下有校正器,這樣來告訴 Zend_Form_Element: <?php $element->addPrefixPath('My_Validator', 'My/Validator/', 'validate'); ?> (回憶一下第三個參數用來指示哪個是執行這個動作的外掛程式載入器) |
如果特定的校正失敗,為阻止後面的校正工作,傳遞第二個參數 true (布爾型):
<?php$element->addValidator('alnum',true);?>
如果你使用一個字串名來添加一個校正器,並且這個校正器接受參數給構造器,你可以把這個第三個參數addValidator() 作為數組傳遞:
<?php$element->addValidator('StringLength',false, array(6, 20));?>
這樣傳遞參數應該按照它們在構造器裡定義的順序進行。上述例子將帶參數$min 和 $max 執行個體化 Zend_Validate_StringLenth 類:
<?php$validator = newZend_Validate_StringLength(6, 20);?>
| |
提供定製的校正錯誤訊息 |
| 有些開發人員可能想為校正器提供定製的錯誤訊息。 Zend_Form_Element::addValidator() 的 $options 參數讓你通過提供 'messages' 鍵並把它設定為鍵/值對(用來設定訊息模板)的數組來完成。你需要知道特定校正器的各種各樣的校正錯誤類型的錯誤碼。 稍好的選擇是在表單中使用 Zend_Translate_Adapter。錯誤碼通過預設的錯誤裝飾器自動傳遞給適配器,然後你可以通過為你的校正器的各種錯誤碼設定翻譯來指定自己的錯誤訊息字串。 |
你也可以使用 addValidators() 一次性設定許多校正器。基本的用法是傳遞數組的數組,每個數組包含 1 到 3 個匹配 addValidator() 的構造器的值:(一般這個可能會用到的比較多。)
<?php$element->addValidators(array( array('NotEmpty', true), array('alnum'), array('stringLength', false, array(6, 20)),));?>
如果你想做的更明確和更詳細,可以使用數組鍵 'validator'、'breakChainOnFailure' 和 'options':
<?php$element->addValidators(array( array( 'validator' => 'NotEmpty', 'breakChainOnFailure' => true), array('validator' => 'alnum'), array( 'validator' => 'stringLength', 'options' => array(6, 20)),));?>
這個用法展示如何在設定檔中配置校正器:
element.validators.notempty.validator= "NotEmpty"element.validators.notempty.breakChainOnFailure= trueelement.validators.alnum.validator= "Alnum"element.validators.strlen.validator= "StringLength"element.validators.strlen.options.min= 6element.validators.strlen.options.max= 20
注意每個條目有一個鍵,不管是否需要,這是使用設定檔規定的 --但它也協助清楚理解哪個參數用於什麼。請記住任何校正器選項必需按順序指定。
為校正一個元素,傳遞值給 isValid():
<?phpif($element->isValid($value)) { // valid} else { // invalid}?>
註:上面這些重點,經常會用到。
| |
在過濾後的值上校正 |
| Zend_Form_Element::isValid() 在校正之前通過提供過濾器鏈來過濾(輸入)值。 |
| |
校正上下文 |
| Zend_Form_Element::isValid() 支援另外的參數 $context。當校正一個表單 Zend_Form::isValid() 傳遞由 $context 處理過的資料的整個數組,Zend_Form_Element::isValid() 接著把它傳遞給每個校正器。這意味著你可以寫知道資料傳遞給其它表單元素的校正器,例如,標準的註冊表單有密碼和密碼確認元素,得有一個校正它們是否匹配。這樣的校正器看起來如下: <?phpclass My_Validate_PasswordConfirmation extends Zend_Validate_Abstract{ const NOT_MATCH = 'notMatch'; protected $_messageTemplates = array( self::NOT_MATCH => 'Password confirmation does not match' ); public function isValid($value, $context = null) { $value = (string) $value; $this->_setValue($value); if (is_array($context)) { if (isset($context['password_confirm']) && ($value == $context['password_confirm'])) { return true; } } elseif (is_string($context) && ($value == $context)) { return true; } $this->_error(self::NOT_MATCH); return false; }}?>
|
校正器按順序處理,除非用 breakChainOnFailure 為 true 建立的校正器並且校正失敗,否則每個校正器都要處理。確認按合理的順序指定你的校正器。
校正失敗後,你可以從校正器鏈讀取錯誤碼和訊息:
<?php$errors = $element->getErrors();$messages =$element->getMessages();?>
(注意:錯誤訊息返回的是一個有錯誤碼/錯誤訊息對的聯合數組)
除了校正器外,你可以用 setRequired(true) 指定必需的元素。預設地,這個標誌是 false,如果沒有值傳遞給 isValid(),校正器鏈將被跳過。你也可以用許多辦法來修改它的行為: 預設地,當元素是必需的,標誌 'allowEmpty'也是 true。這意味著如果傳遞給 isValid() 的值為空白,校正器將被跳過。可以用訪問器 setAllowEmpty($flag) 來切換這個標誌。當標誌為 false,並且傳遞了一個值,校正器將仍然運行。 預設地,如果元素是必需的,但不包括 'NotEmpty' 校正器,isValid() 就用 breakChainOnFailure 標誌設定添加一個到棧頂。這使得要求的標誌有語義意義:如果沒有傳遞值,我們立即使提交的資料無效並通知使用者,並防止其它校正器繼續校正我們已知的無效資料。
如果你不想這樣,傳遞給 setAutoInsertNotEmptyValidator($flag) 一個 false 值使它關閉。這將防止 isValid() 在校正器鏈裡放置一個 'NotEmpty' 校正器。
| |
使用 Zend_Form_Elements 作為通用的校正器 |
| Zend_Form_Element 實現 Zend_Validate_Interface,意味著元素可以在其它非表單相關的校正鏈裡被用做校正器。 |
校正相關的方法包括: setRequired($flag) 和 isRequired() 讓你設定和讀取 'required' 標誌的狀態。當設定為布爾 true,這個標誌要求元素在由 Zend_Form 處理的資料中。 setAllowEmpty($flag) 和 getAllowEmpty() 讓你修改可選元素的行為(例如,要求的標誌為 false 的元素)。當 'allow empty' 標誌為 true 時,空值將傳遞給校正器鏈。 setAutoInsertNotEmptyValidator($flag) 當元素是必需時,讓你指定是否 'NotEmpty' 校正器預先準備給校正器鏈。預設地,這個標誌為 true 。 addValidator($nameOrValidator, $breakChainOnFailure = false, array $options = null) addValidators(array $validators) setValidators(array $validators) (重寫所有校正器) getValidator($name) (按名讀取校正器對象) getValidators() (讀取所有校正器) removeValidator($name) (按名刪除校正器) clearValidators() (刪除所有校正器)
4. 定製錯誤訊息
有時,你想定製一條或多條特定的錯誤訊息來替代由附加到元素上的校正器所帶的錯誤訊息。另外,有時候你想自已標幟表單無效,從 1.6.0 版開始,通過下列方法來實現這個功能。 addErrorMessage($message): 添加一條來顯示當校正失敗時的錯誤訊息。 可以多次調用,新訊息就追加到堆棧。 addErrorMessages(array $messages): 添加多條錯誤訊息來顯示校正錯誤。 setErrorMessages(array $messages): 添加多條錯誤訊息來顯示校正錯誤,並覆蓋先前的錯誤訊息。 getErrorMessages(): 讀取已定義的定製的錯誤訊息列表。 clearErrorMessages(): 刪除已定義的定製的錯誤訊息。 markAsError(): 標記表單已經有失敗的校正。 hasErrors(): 確定是否元素有失敗校正或標記為無效。 addError($message): 添加一條訊息給定製錯誤訊息棧並標記表單無效。 addErrors(array $messages): 添加數條訊息給定製錯誤訊息棧並標記表單無效。 setErrors(array $messages): 覆蓋定製的錯誤訊息堆棧並標記表單無效。
所有用這個方式設定的錯誤可以被翻譯。
5. 裝飾器
對許多 web 開發人員來說一個特別的痛苦是 XHTML 表單自己的產生。對於每個元素,開發人員需要為元素自己產生 markup,label 是一個典型,並且,如果他們對使用者很好,需要為顯示校正錯誤訊息產生 markup。在頁面元素越多,任務就越不瑣碎。
Zend_Form_Element 試圖用 "裝飾器" 來解決這個問題。裝飾器就是個類,可以訪問元素和用於解析內容的方法。
Zend_Form_Element 所使用的預設的裝飾器是: ViewHelper: 指定一個視圖助手用於解析元素。'helper' 元素屬性可用來指定使用哪個視圖助手。預設地,Zend_Form_Element 指定 'formText' 視圖助手,但個別的子類指定不同的助手。 Errors: 使用 Zend_View_Helper_FormErrors 追加錯誤訊息給元素,如果沒有錯誤,就不追加。 HtmlTag: 在一個 HTML <dd> 標籤裡封裝元素和錯誤。 Label: 使用 Zend_View_Helper_FormLabel 預先準備一個標籤給元素,並把它封裝在一個 <dt> 標籤裡。如果沒有提供標籤(Label),就解析定義術語(definition term)標籤(tag)。
| |
不需要載入預設裝飾器 |
| 預設地,在對象初始化過程中載入預設裝飾器。你可以通過傳遞 'disableLoadDefaultDecorators' 選項給構造器來關閉它: <?php$element = new Zend_Form_Element('foo', array('disableLoadDefaultDecorators' => true));
該選項可以和企圖選項混合,它們都是數組選項或在 Zend_Config 對象裡。 |
因為裝飾器註冊順序的原因 --先註冊先執行--你需要確保按合適的順序來註冊裝飾器,或者確保以健全的方式設定替換選項。這個是註冊預設裝飾器的例子:
<?php$this->addDecorators(array( array('ViewHelper'), array('Errors'), array('HtmlTag', array('tag' => 'dd')), array('Label', array('tag' => 'dt')),));?>
初始內容由 'ViewHelper' 裝飾器產生,它產生表單元素自己。接著,'Errors' 裝飾器從元素裡抓取錯誤訊息。如果有任何錯誤,就傳遞給 'FormErrors' 視圖助手來解析。下一個裝飾器 'HtmlTag' 在一個 HTML <dd> 標籤裡封裝元素和錯誤。最後,'label' 裝飾器讀取元素的標籤並傳遞給 'FormLabel' 視圖助手,封裝在一個 HTML <dt> 標籤裡。預設地,資料預先準備給內容,輸出結果基本上是這樣的:
<dt><labelfor="foo" class="optional">Foo</label></dt><dd> <input type="text"name="foo" id="foo" value="123" /> <ul class="errors"> <li>"123" is not analphanumeric value</li> </ul></dd>
| |
使用同類型的多重裝飾器 |
| 在內部,當讀取裝飾器時,Zend_Form_Element 使用裝飾器的類作為查詢機制。結果,你不能註冊同類型的多重裝飾器,後來的裝飾器就重寫以前存在的裝飾器。 為解決這個問題,你可以使用 aliases。不是傳遞裝飾器或裝飾器名作為第一個參數給 addDecorator(),而是傳遞帶有一個單個元素的數組,並且別名指向裝飾器對象或名字: <?php// Alias to 'FooBar':$element->addDecorator(array('FooBar' => 'HtmlTag'), array('tag' => 'div')); // And retrieve later:$decorator = $element->getDecorator('FooBar');?>
在 addDecorators() 和 setDecorators() 方法中,你需要在表示裝飾器的數組中傳遞 'decorator' 選項: <?php// Add two 'HtmlTag' decorators, aliasing one to 'FooBar':$element->addDecorators( array('HtmlTag', array('tag' => 'div')), array( 'decorator' => array('FooBar' => 'HtmlTag'), 'options' => array('tag' => 'dd') ),); // And retrieve later:$htmlTag = $element->getDecorator('HtmlTag');$fooBar = $element->getDecorator('FooBar');?>
|
裝飾器帶有的方法包括: addDecorator($nameOrDecorator, array $options = null) addDecorators(array $decorators) setDecorators(array $decorators) (重寫所有裝飾器) getDecorator($name) (按名讀取裝飾器對象) getDecorators() (讀取所有裝飾器) removeDecorator($name) (按名刪除裝飾器) clearDecorators() (刪除所有裝飾器)
6. 中繼資料和屬性
Zend_Form_Element 處理廣泛的屬性和元素中繼資料,基本屬性包括: name: 元素名,使用 setName() 和 getName() 訪問器。 label: 元素標籤,使用 setLabel() 和 getLabel() 訪問器。 order: 在表單中出現的元素的索引,使用 setOrder() 和 getOrder() 訪問器。 value: 當前元素的值,使用 setValue() 和 getValue() 訪問器。 description: 元素的描述,常用於提供工具提示或 javascript 上下文提示,描述元素的意圖,使用 setDescription() 和 getDescription() 訪問器。 required: 當執行表單校正時,指示元素是否必需的標誌,使用 setRequired() 和 getRequired() 訪問器,預設為 false。 allowEmpty: 指示可選的元素是否應該校正空值的標誌,當為 true,並且要求的標誌為 false,空值就不傳遞給校正器鏈,並假定為 true。使用 setAllowEmpty() 和 getAllowEmpty() 訪問器,預設為 true。 autoInsertNotEmptyValidator: 當元素是必需時,指示是否插入一個 'NotEmpty' 校正器。預設地,這個標誌為 true,用 setAutoInsertNotEmptyValidator($flag) 來設定該標誌並用 autoInsertNotEmptyValidator() 來確定它的值。
表單元素可能要求另外的中繼資料。例如,對於 XHTML 表單元素,你可能想指定屬性如類或 id,有一組訪問器來完成它: setAttrib($name, $value): 添加屬性 setAttribs(array $attribs): 像 addAttribs() 一樣,但重寫 getAttrib($name): 讀取一個單個的屬性值 getAttribs(): 以鍵/值對讀取所有屬性
然而大多數時候,你可以把它們當作對象屬性來訪問,因為 Zend_Form_Element 利用重載來簡便訪問它們:
<?php// Equivalent to$element->setAttrib('class', 'text'):$ele