CForm

I don't see how this CForm would work for all situations.  Sometimes forms are more complicate then a series of fields one after another.  For instance,  I have a form where the first row has two inputs: a text field, and a checkbox on the right of it.  Then there are more form elements below this.  Like this:



Date:   _____ - or - <checkbox> N/A


Field2: _____


Etc:    _____


Using a template that loops through the elements and prints them does not make this possible.

And what might not be cool, is if someone set up there forms using CForm and then realize they need to make an adjustment that is not possible with CForm.  Then they need to re-write all there forms the “old” way.  I know people don’t like typing out forms, but maybe that should be left to the yiic program

You should be able to create the form view like this too, as apposed to looping through the elements:



<?php


$form->begin();





echo $element->label['fieldA'].': '.$element['fieldA']->render().' - or - '.$elements['fieldB']->render().' '.$elements['fieldB']->label;








$element = $elements['email'];


echo $element->label."<br/>".$element->render()."<br/>";





$form->end();


?>





CForm can be rendered in two ways.

The first way is using a predefined form template. Its benefit is fast prototyping and can render forms in consistent design styles. Its drawback is that the rendering layout may be too rigid to be used for complicated forms.

The second way is explicitly rendering each element in CForm in a view, just like we currently do using CHtml active methods.



echo $form['username']=>renderLabel();


echo $form['username']=>renderInput();


Quote

CForm can be rendered in two ways.

The first way is using a predefined form template. Its benefit is fast prototyping and can render forms in consistent design styles. Its drawback is that the rendering layout may be too rigid to be used for complicated forms.

The second way is explicitly rendering each element in CForm in a view, just like we currently do using CHtml active methods.



echo $form['username']=>renderLabel();


echo $form['username']=>renderInput();


Exactly. Also with optional arguments:



echo $form['username']=>renderLabel('optional new label');


echo $form['username']=>renderInput('optional initial value');


I vote for the widgets idea. It would give infinite extensible options.

And I think we can learn from Django:



class ContactForm(forms.Form):


    subject = forms.CharField(max_length=100)


    message = forms.CharField()


    sender = forms.EmailField()


    cc_myself = forms.BooleanField(required=False)


Maybe using a factory:



class ContactForm extends CForm


{


    public subject = CWidget::text(array('max_length' => 100));


    public message = CWidget::textarea();


    public sender = CWidget::email();


    public cc_myself = CWidget::boolean(false);


}


I personally don't like to throw it all with config files. They are not extensible, they are hard to port and other reasons…

I totally dislike the idea of widgets. What could ever be added to them?

If widgets will get implemented, I hope CHtml will be still available.

What about this?

Taken the Idea of Jonah of have CField (etc) do something like this:

Each C*Field can be related with a property of a model

Example:



<?php 


$name = new CTextFied(array('model'=>$model,'property'=>'someProepertyOfModel',... otjer properties...)) ; // note that this can be done in a controller.


The class CForm may have a method named addFieds(), which receives an array of instances of CField (or subclass).

Then in the controller we can do:



<?php


$form = new CForm;


$form->addFields(array('name'=>$name, 'pass'=>$pass));


// etc.


And we can render the form (as Qiang says) in two ways

Quote

CForm can be rendered in two ways.

The first way is using a predefined form template. Its benefit is fast prototyping and can render forms in consistent design styles. Its drawback is that the rendering layout may be too rigid to be used for complicated forms.

The second way is explicitly rendering each element in CForm in a view, just like we currently do using CHtml active methods.



$form->begin()


echo $form->name=>renderLabel();


echo $form->pass=>renderInput();


$form->end();


Quote

I totally dislike the idea of widgets. What could ever be added to them?

? Widgets can be extended… Also on your model you have a "filename" textfield.

How do you make the form display an uploadfile field? Hacks? Nah…

Make the Form be the link between a field in the model and a Widget which will represent that field.

Also with Widgets you can have multiple outputs for different page viewers (browser, mobile, iphone) The logic don't change, only the View layer.

Did you mean the file-type input? Why would they need an additional widget?

But I can change the view even without widgets.

Adding fundamentally new layers to this system makes totally no sense to me. Is robustness inevitable in open source softwares?

Can’t these widgets be added as a separate extension?

So you say to limit the CForm to the "default" fields? And what is your proposal to define a new form class? And the output.

Widgets are more versatile. It would be easier to have a CEmailField class which extends the CTextField class + adds javascript email validation. Or have a CDatetimeField which shows a neat calendar through javascript. One widget can show multiple textfields for a birthdate for example, a field for the year/month/day.

I'm not saying CHtml form methods should be removed, many widgets might use those methods but for form generation it'll be much neater to have widgets.

@zerone,

File type input is a default field. I agree with Qiang’s basic proposal without widgets.

@dalip,

But client-side javascript (like calendar) is not something basic that should be integrated into the core package, is it?

Quote

@zerone,

File type input is a default field. I agree with Qiang's basic proposal without widgets.

@dalip,

But client-side javascript (like calendar) is not something basic that should be integrated into the core package, is it?

But how do you represent a filefield?!!

And dalip only wrote a few examples of extensions to widgets.

Am I really getting something such wrong? :-[

Aren’t we talking about [font=“Courier New”]<input type=“file”>[/font]?

Also, I didn’t shoot everything that moves, just used dalip’s examples as a reason not to include these widgets into core.

peace…it seems there’s some misunderstanding here. ;)

What in my mind currently is that we will reuse CHtml's active methods while still allows an element to use a widget. For example,



'elements'=>array(


    'username'=>array('type'=>'TextField'),


    'password'=>array('type'=>'MyFancyPasswordWidget'),


),


So 'username' will use CHtml::activeTextField() to render the input field while 'password' will use a widget. As a result, we will no longer need to implement widgets for those basic input fields since they are already provided by CHtml.

Will there be a way to pass values to the field/widget constructors?

Yes, the name-value pairs (except some reserved names) in each array will be passed to the activeMethod's htmlOptions or widget's properties.

An initial implementation is checked in. Using the blog demo as an example, the create action and view can be rewritten like the following:

actionCreate:



<?php


public function actionCreate()


{


	$form=new PostForm($this, new Post);


	if($form->submitted())


	{


		if($form->clicked('preview'))


			$form->model->validate();


		else if($form->clicked('create') && $form->model->save())


			$this->redirect(array('show','id'=>$form->model->id));


	}


	$this->render('create',array('form'=>$form,'model'=>$form->model));


}


create.php



<?php


<h2>Create New Post</h2>





<?php echo $form->render(); ?>





<?php if($form->clicked('preview') && !$model->hasErrors()): ?>


...display preview here...


PostForm:



<?php


class PostForm extends CForm


{


	public function init()


	{


		$spec=array(


			'elements'=>array(


				'title'=>array(


					'type'=>'text',


					'size'=>65,


					'maxlength'=>128,


				),


				'content'=>array(


					'type'=>'textarea',


					'rows'=>20,


					'cols'=>50,


				),


				'tags'=>array(


					'type'=>'text',


					'size'=>65,


				),


				'status'=>array(


					'type'=>'dropdownlist',


					'items'=>Post::model()->statusOptions,


				),


			),


			'buttons'=>array(


				'update'=>array(


					'type'=>'submit',


					'label'=>'Save',


					'on'=>'update'


				),


				'create'=>array(


					'type'=>'submit',


					'label'=>'Create',


					'on'=>'insert',


				),


				'preview'=>array(


					'type'=>'submit',


					'label'=>'Preview',


				),


			),


		);


		$this->configure($spec);


	}


}


Features:

  • Supports sub-forms and multiple models

  • Supports widgets and plain strings to be embedded in the form

  • Arbitrary manipulation of the whole form hierarchy, using the familiar array syntax. For example



<?php


$form['title']->size=100;


Pros:

  • The _form.php partial view is eliminated, which is replaced by the single call $form->render().

  • Code relevant to input configuration is separated from the rest of the presentational code.

  • By customizing the base form's render() method, we can change the appearance of the forms in an application consistently and easily.

Cons:

  • The form rendering logic is relatively rigid since it treats all kinds of forms the same.

  • It introduces an additional layer of abstraction, which means additional learning curve.

Quote

Pros:
  • The _form.php partial view is eliminated, which is replaced by the single call $form->render().

Wow, it’s elegant.  :o

Quote

Cons:
  • It introduces an additional layer of abstraction, which means additional learning curve.

I think the learning effort is already high enough  ;) as the functionalities of the yii is so rich. We can use yiic or other automation in order to reduce the effort. So I am for CForm.

@mocapapa,

Yes, it’s elegant as long as you don’t need customized layout.

Perhaps it would be a good idea to render the corresponding form class for each activerecord model during code generation.

@For those, who have not much experience with PHP:

  • In CForm subclasses, in init() you have to call $this->configure() with a single parameter, which is in array containing the specification (which can be stored elsewhere, even in configuration files, or as application parameter).

  • In the current implementation, output is rendered as follows: the main form and all its subforms are enclosed by fieldset tags; all visible input elements are shown as items of unordered lists.

@pestaa: you already read the code! The render() method is not finished yet. Needs to make the default rendering more pretty.