我在Zend框架中構建了一個抽象窗體。這是通過從數據庫創建動態表單元素(包含鍵/值的表)來完成的。 除了表單的setDefault/populate函數外,它大部分工作正常。讓我解釋。在zend框架中填充子窗體的問題
我有一個主表單,附有3個子表單(嚮導樣式表單)。表單的第3步有5個或更多的動態元素。 (例如:服務器機架的屬性)。
步驟3中的表單可以使用ajax進行克隆。 (所以你可以一次添加4個服務器機架)。提交表單時,preValidation()函數將檢查所有新的字段並將它們添加到子表單中。 太好了。現在問題開始了。
當添加字段,我用創建表單元素的工廠方法子窗體=>
$subForm->addElement($presentation_type,'fixedField'. $key.'_'.$formStep,
array('label' => $label.':',
'required' => $is_mandatory,
'filter' => $filters,
'value' => $value,
'description' => $unit .' '. $description,
'validators' => $validators));
開始一個新的未提交表單時也能正常工作,但沒有設定值參數表格提交時,他沒有填充值參數(同一個函數中的其他參數工作得很好)。 我升級了zend框架到最新版本,試圖找到我在谷歌和論壇上的問題,但沒有成功。
如果你解決的話,我會給你寄一份比利時啤酒:) 已經找了3天了。
還嘗試使用setDefault函數和填充函數。奇怪的是當我做「echo $ subForm-> getElement('xxxxxx') - > getValue();」我得到正確的輸出。所以似乎zend只是不會呈現元素內的值。
控制器代碼:
<?php
class TestController extends Zend_Controller_Action {
protected $_form;
protected $_namespace = 'TestController';
protected $_session;
/**
* Gets the add/edit form for the current object
*
* @access public
* @return object|void
* @param boolean $search_form Set to true if you want the search form object to be returned
*/
public function getForm()
{
if (null === $this->_form) {
$action = $this->getRequest()->getActionName();
$this->_form = new Application_Form_Test();
}
return $this->_form;
}
/**
* Action for the new page
*
* @access public
* @return void
*/
public function newAction(){
//Store the parent object in a session, this way we can use the var in the 3th form step
$this->getSessionNamespace();
// Either re-display the current page, or grab the "next"
// (first) sub form
if (!$form = $this->getCurrentSubForm()) {
$form = $this->getNextSubForm();
}
$this->view->form = $this->getForm()->prepareSubForm($form);
}
/**
* Action to process the multi step form
*
* @access public
* @return mixed
*/
public function processAction(){
//No form is set
if (!$form = $this->getCurrentSubForm()) {
return $this->_forward('new');
}
if (!$this->subFormIsValid($form, $this->getRequest()->getPost())) {
$this->view->form = $this->getForm()->prepareSubForm($form);
return $this->render('new');
}
if (!$this->formIsValid()) {
$form = $this->getNextSubForm();
$this->view->form = $this->getForm()->prepareSubForm($form);
return $this->render('new');
}
// Valid form!
// Let's save everything
//......
// All done, clear the sessions
Zend_Session::namespaceUnset($this->_namespace);
//$this->render('index');
$this->_forward('index');
}
/**
* Ajax action that returns the dynamic form field for step3 in the form
*/
public function newajaxformAction() {
if(!$this->getRequest()->isXmlHttpRequest()) throw new Zend_Controller_Action_Exception("This isn't a Ajax request !", 404);
$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContext('newfield', 'html')->initContext();
//Disable view
$this->_helper->viewRenderer->setNoRender();
$this->_helper->layout()->disableLayout();
$id = $this->_getParam('id', null);
$amount = $this->_getParam('amount', null);
$fieldsKeys = $_POST['key'];
$fieldsValues = $_POST['value'];
//This one adds multiple objects on one page
$po = new Test_Partial($id,$amount,$fieldsKeys,$fieldsValues);
echo $po->__toString();
}
/**
* Get the session namespace we're using
*
* @access public
* @return Zend_Session_Namespace
*/
public function getSessionNamespace()
{
if (null === $this->_session) {
$this->_session = new Zend_Session_Namespace($this->_namespace);
}
return $this->_session;
}
/**
* Get a list of forms already stored in the session
*
* @access public
* @return array
*/
public function getStoredForms()
{
$stored = array();
foreach ($this->getSessionNamespace() as $key => $value) {
$stored[] = $key;
}
return $stored;
}
/**
* Get list of all subforms available
*
* @access public
* @return array
*/
public function getPotentialForms()
{
return array_keys($this->getForm()->getSubForms());
}
/**
* What sub form was submitted?
*
* @access public
* @return false|Zend_Form_SubForm
*/
public function getCurrentSubForm()
{
$request = $this->getRequest();
if (!$request->isPost()) {
return false;
}
foreach ($this->getPotentialForms() as $name) {
if ($data = $request->getPost($name, false)) {
if (is_array($data)) {
return $this->getForm()->getSubForm($name);
break;
}
}
}
return false;
}
/**
* Get the next sub form to display
*
* @return Zend_Form_SubForm|false
* @access public
*/
public function getNextSubForm()
{
$storedForms = $this->getStoredForms();
$potentialForms = $this->getPotentialForms();
foreach ($potentialForms as $name) {
if (!in_array($name, $storedForms)) {
return $this->getForm()->getSubForm($name);
}
}
return false;
}
/**
* Is the sub form valid?
*
* @param Zend_Form_SubForm $subForm
* @param array $data
* @return bool
*/
public function subFormIsValid(Zend_Form_SubForm $subForm,array $data)
{
$name = $subForm->getName();
echo '<br />Submitted data(Send from Controller) = <pre>';
print_r($data);
echo '</pre>';
if ($subForm->isValid($data)) {
$this->getSessionNamespace()->$name = $subForm->getValues();
return true;
}
return false;
}
/**
* Is the full form valid?
*
* @return bool
* @access public
*/
public function formIsValid()
{
$data = array();
foreach ($this->getSessionNamespace() as $key => $info) {
$data[$key] = $info[$key];
}
return (count($this->getStoredForms()) < count($this->getPotentialForms()))? false : $this->getForm()->isValid($data);
}
}
?>
Form Code:
<?php
class Application_Form_Test extends Zend_Form {
public function init() {
//Set some filters for are fields
$this->setElementFilters(array('StringTrim'));
//Lets make some subforms = > each subform will be on a different page
//Step 1
$step1 = new Zend_Form_SubForm();
$step1->addElement('select', 'test', array( 'label' => 'Save in:',
'multiOptions' => array('choose'=>'Choose one ...','a'=>'a','b'=>'b'),
'required' => false,
'ignore' => true,
'value' => array('choose'),
'validators' => array(array('InArray',false,array(array_keys(array('choose'=>'Choose one ...','a'=>'a','b'=>'b')))))));
// Step 3
$step2 = new Zend_Form_SubForm();
// Add a remove and add button for the dynamic forms
$step2->addElement('text', 'addFormAmount', array('label' => '',
'required' => false,
'ignore'=> true,
'value' => 1,
'description' => 'objects.',
'order' => 99992
));
$step2->addElement('button', 'addForm', array('label' => 'Add',
'order' => 99991
));
$step2->getElement('addForm')->setAttrib('onClick', 'ajaxAddForm();');
// Add a hidden id field, this way we can use the id in javascript to count the numner of fields
$step2->addElement('hidden', 'id', array('value' => 1));
$this->addAbstractField($step2,'',1,'test value');
//test, let's put our prevalidation at the end of the form object
$this->preValidation($step2,$_POST);
// Finally attach sub forms to main form
$this->addSubForms(array(
'step1' => $step1,
'step2' => $step2
));
}
/**
* Create a sluggable string for forms or any other uri related string
*
* @return mixed
* @access public
* @param mixed $array
*/
protected function getSlug($string){
$slug = trim($string); // trim the string
$slug= preg_replace('/[^a-zA-Z0-9 -]/','',$slug); // only take alphanumerical characters, but keep the spaces and dashes too…
$slug= str_replace(' ','-', $slug); // replace spaces by dashes
$slug= strtolower($slug); // make it lowercase
return $slug;
}
/**
* Prepare a sub form for display
*
* @param string|Zend_Form_SubForm $spec
* @return Zend_Form_SubForm
*/
public function prepareSubForm($spec)
{
if (is_string($spec)) {
$subForm = $this->{$spec};
} elseif ($spec instanceof Zend_Form_SubForm) {
$subForm = $spec;
} else {
throw new Exception('Invalid argument passed to ' . __FUNCTION__ . '()');
}
$this->setSubFormDefaultDecorators($subForm)
->addSubmitButton($subForm)
->addSubFormActions($subForm);
return $subForm;
}
/**
* Add form decorators to an individual sub form
*
* @param Zend_Form_SubForm $subForm
* @return My_Form_Registration
*/
public function setSubFormDefaultDecorators(Zend_Form_SubForm $subForm)
{
$subForm->setDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'dl','class' => 'zend_form')),'Form',));
return $this;
}
/**
* Add a submit button to an individual sub form
*
* @param Zend_Form_SubForm $subForm
* @return My_Form_Registration
*/
public function addSubmitButton(Zend_Form_SubForm $subForm)
{
$subForm->addElement(new Zend_Form_Element_Submit(
'save',
array(
'label' => 'Save and continue',
'required' => false,
'ignore' => true,
'order' => 99999
)));
$subForm->getElement('save')->setAttrib('onClick', 'ajaxController(); $("#processing_alert").css("display", "block");');
return $this;
}
/**
* Add action and method to sub form
*
* @param Zend_Form_SubForm $subForm
* @return My_Form_Registration
*/
public function addSubFormActions(Zend_Form_SubForm $subForm)
{
$subForm->setAction('/test/process')
->setMethod('post')
->setEnctype(Zend_Form::ENCTYPE_MULTIPART);
return $this;
}
/**
* After post, pre validation hook
*
* Finds all fields where name includes 'newField' and uses addNewField to add
* them to the form object
*
* @param array $data $_GET or $_POST
*/
public function preValidation(Zend_Form_SubForm $subForm,array $data) {
// array_filter callback
function findFields($field) {
// return field names that include 'newField'
if (strpos($field, 'newField') !== false) {
return $field;
}
}
// Search $data for dynamically added fields using findFields callback
$newFields = array_filter(array_keys($data), 'findFields');
foreach ($newFields as $fieldName) {
// strip the id number off of the field name and use it to set new order
$ex1 = explode('newField', $fieldName);
$ex2 = explode('_',$ex1[1]);
$key = $ex2[0];
$order = $ex2[1];
$this->addAbstractField($subForm,$key, $order,$data[$fieldName]);
//echo 'order :'.$order." and key is " .$key."<br />"; test ok
}
}
/**
* Adds new fields to form dynamically
*
* @param string $name
* @param string $value
* @param int $order
* @param object $subForm
*
*/
public function addAbstractField(Zend_Form_SubForm $subForm, $key, $formStep=null,$value){
$subForm->addElement('text','fixedField'. $key.'_'.$formStep, array('label' => 'Test label:',
'required' => 'true',
'value' => $value,
'description' => 'test description'));
echo '<br />Added element to subform (Send from Form method) key = "fixedField'. $key.'_'.$formStep .'" and value "'.$value.'"<br />';
return $this;
}
}
?>
Form Partial code:
<?php
class Test_Partial {
protected $id;
public function __construct($id,$amount=1,$fieldsKeys=array(),$fieldsValues=array())
{
$this->id = $id;
$this->amount = is_int((int)$amount) ? $amount: 1 ;
$this->fields = array();
//Lets combine both arrays into one
foreach ($fieldsKeys as $key => $value){
$ex = explode('fixedField',$value);
$ex2 = explode('_',$ex[1]);
$this->fields[$ex2[0]] = $fieldsValues[$key];
}
}
public function get() {
$result_array = array();
$amount_counter = 1;
while ($amount_counter <= $this->amount) {
$result_array[] = new Zend_Form_Element_Text('newField'. $keyvalue['id'].'_'.($this->id+$amount_counter), array( 'label' => 'test:',
'required' => true,
'value' => 'this data will be lost'));
$tikk = new Zend_Form_Element_Button('removeForm'.($this->id+$amount_counter), array('label' => 'Remove'));
$tikk->setAttrib('onClick', 'ajaxRemoveForm('.($this->id+$amount_counter).')');
$result_array[] = $tikk;
++ $amount_counter;
}
return $result_array;
}
public function __toString()
{
return implode('', $this->get());
}
/**
* Create a sluggable string for forms or any other uri related string
*
* @return mixed
* @access public
* @param mixed $array
*/
protected function getSlug($string){
$slug = trim($string); // trim the string
$slug= preg_replace('/[^a-zA-Z0-9 -]/','',$slug); // only take alphanumerical characters, but keep the spaces and dashes too…
$slug= str_replace(' ','-', $slug); // replace spaces by dashes
$slug= strtolower($slug); // make it lowercase
return $slug;
}
}
?>
View:
<script type="text/javascript">
function getLastSubId(){
var maxc = 0;
// Remove all elements with a certain sub id
$('*').each(function() {
num = parseInt(this.id.split("_")[1],10);
if(num > maxc)
{
maxc = num;
}
});
return maxc;
}
// Retrieve new element's html from action controller
function ajaxAddForm(amount) {
// Diplay processing msg
$("#processing_alert").css("display", "block");
// Get value of id - integer appended to dynamic form field names and ids
var id = $("#step2-id").val();
if(typeof amount == 'undefined'){
var amount = $("#step2-addFormAmount").val();
}
var fields = '';
// Collect all field keys and values and include them in the ajax request.
$('*[id*=step2-fixedField]:visible').each(function() {
var key = $(this).attr('id');
var value = $(this).attr('value');
fields += '&key[]='+key+'&value[]='+value;
});
$.ajax(
{
type: "POST",
url: "<?php echo $this->url(array('action' => 'newAjaxForm')); ?>",
data: "id=" + id + "&amount=" + amount + fields,
async: false,
success: function(newForm) {
// Insert new element before the Add button
var counter = parseInt(id) + parseInt(amount);
$("#addForm-label").before(newForm);
$("#step2-id").val(counter);
}
}
);
// Disable processing msg
$("#processing_alert").css("display", "none");
}
function ajaxRemoveForm(id) {
// Diplay processing msg
$("#processing_alert").css("display", "block");
// Remove the "remove" button that we just pressed + label
$("#removeForm"+id+"-label").remove();
$("#removeForm"+id+"-element").remove();
// Remove all elements with a certain sub id
$('*[id*=_'+id+'-]:visible').each(function() {
$(this).remove();
});
// Disable processing msg
$("#processing_alert").css("display", "none");
}
</script>
<?php echo $this->form; ?>