Yii Composite Form Extension ¶
Extension that can greatly simplify complex forms processing that have multiple relations.
Weavora's Git Repo - https://github.com/weavora/wform
Features:
- Easy composite form processing
- Fast configuration
- Support of all standard relations: has_one, belongs_to, has_many and many_many
Configuration ¶
1) Download and unpack source into protected/extensions/ folder.
2) Below you can see config settings for import:
<?php
// main.php
return array(
...
'import' => array(
...
'ext.wform.*',
),
...
);
3) Extension require changing of CActiveRecord::onUnsafeAttribute. Here are few options for that:
a) Extend all your models/forms from WActiveRecord instead of CActiveRecord
b) If you already has modified class for active record, then extend it from WActiveRecord or add onUnsafeAttribute method:
<?php
// protected/components/ActiveRecord.php
// extend from WActiveRecord
class ActiveRecord extends WActiveRecord
{
// you custom code here (if reqired)
}
// or add onUnsafeAttribute method
class ActiveRecord extends CActiveRecord
{
// you custom code here (if required)
/**
* Raise onUnsafeAttribute event for active record
* Required for wform extension
*
* @param $name unsafe attribute name
* @param $value unsafe attribute value
*/
public function onUnsafeAttribute($name, $value)
{
$event = new CEvent($this, array('name' => $name, 'value' => $value));
$this->raiseEvent('onUnsafeAttribute', $event);
return parent::onUnsafeAttribute($name, $value);
}
}
Usage ¶
1) Modify model: define relations and attach behavior. You can also create separate class for the form extended from your model.
<?php
class MyModel extends WActiveRecord {
...
public function relations()
{
return array(
'hasOneRelation' => array(self::HAS_ONE, 'HasOneModel', 'my_model_fk_into_related_model'),
'belongsToRelation' => array(self::BELONGS_TO, 'BelongsToModel', 'related_model_fk_into_my_model'),
'hasManyRelation' => array(self::HAS_MANY, 'HasManyModel', 'my_model_fk_into_related_model'),
'manyManyRelation' => array(self::MANY_MANY, 'ManyManyModel', 'linker(my_model_id,related_model_id)'),
);
}
...
public function behaviors() {
return array(
// attach wform behavior
'wform' => array(
'class' => 'ext.wform.WFormBehavior',
// define relations which would be processed
'relations' => array('hasOneRelation', 'belongsToRelation', 'hasManyRelation', 'manyManyRelation'),
),
// or you could allow to skip some relation saving if it was submitted empty
'wform' => array(
'class' => 'ext.wform.WFormBehavior',
'relations' => array(
'hasOneRelation' => array('required' => true), // default for HAS_ONE: false
'belongsToRelation' => array('required' => true), // default for BELONGS_TO: false
'hasManyRelation' => array('required' => false), // default for HAS_MANY: true
'manyManyRelation' => array('required' => false), // default for MANY_MANY: true
),
),
);
}
...
}
2) Create action to process form.
<?php
class MyController extends Controller {
...
// form create & edit processed by single action
public function actionEdit($id = null)
{
$myModel = $id ? MyModel::model()->with('hasManyRelation','manyManyRelation')->findByPk($id) : new MyModel();
if(Yii::app()->request->isPostRequest) {
$myModel->attributes = Yii::app()->request->getPost('MyModel');
if ($myModel->save()) {
$this->redirect('some/page');
}
}
$this->render('edit', array(
'model' => $myModel
));
}
}
3) Include js/jquery.multiplyforms.js jquery plugin into your layout
4) Define form using WForm instead of CActiveForm
// protected/views/my/edit.php
<h1><?php echo ($model->isNewRecord ? "Create" : "Update " . $model->name);?></h1>
<?php $form = $this->beginWidget('WForm'); ?>
<!-- MyModel form fields -->
<div class="row">
<?php echo $form->labelEx($model, 'name'); ?>
<?php echo $form->textField($model, 'name'); ?>
<?php echo $form->error($model, 'name'); ?>
</div>
<!-- fields of embeded forms -->
<!-- has_one relation -->
<div class="row">
<?php echo $form->labelEx($model, 'hasOneRelation.name'); ?>
<?php echo $form->textField($model, 'hasOneRelation.name'); ?>
<?php echo $form->error($model, 'hasOneRelation.name'); ?>
</div>
<!-- belongs_to relation -->
<div class="row">
<?php echo $form->labelEx($model, 'belongsToRelation.name'); ?>
<?php echo $form->textField($model, 'belongsToRelation.name'); ?>
<?php echo $form->error($model, 'belongsToRelation.name'); ?>
</div>
<!-- has_many relation -->
<div class="row hasManyRelation">
<!-- exists items -->
<?php if ($model->hasManyRelation): ?>
<?php foreach ($model->hasManyRelation as $index => $item): ?>
<div class="has-many-item">
<?php if (!$item->isNewRecord): ?>
<?php echo $form->hiddenField($model, "hasManyRelation.$index.id"); ?>
<?php endif; ?>
<?php echo $form->labelEx($model, "hasManyRelation.$index.text"); ?>
<?php echo $form->textField($model, "hasManyRelation.$index.text"); ?>
<?php echo $form->error($model, "hasManyRelation.$index.text"); ?>
<a href="#" class="delete">Delete</a>
</div>
<?php endforeach ?>
<?php endif; ?>
<!-- create new items -->
<div class="has-many-item just-empty-form-template-hasManyRelation">
<?php echo $form->labelEx($model, "hasManyRelation..text"); ?>
<?php echo $form->textField($model, "hasManyRelation..text"); ?>
<?php echo $form->error($model, "hasManyRelation..text"); ?>
<a href="#" class="delete">Delete</a>
</div>
<a href="#" class="add">Add more</a>
</div>
<!-- many_many relation -->
<div class="row manyManyRelation">
<!-- exists items -->
<?php if ($model->manyManyRelation): ?>
<?php foreach ($model->manyManyRelation as $index => $item): ?>
<div class="many-many-item">
<?php if (!$item->isNewRecord): ?>
<?php echo $form->hiddenField($model, "manyManyRelation.$index.id"); ?>
<?php endif; ?>
<?php echo $form->labelEx($model, "manyManyRelation.$index.note"); ?>
<?php echo $form->textField($model, "manyManyRelation.$index.note"); ?>
<?php echo $form->error($model, "manyManyRelation.$index.note"); ?>
<a href="#" class="delete">Delete</a>
</div>
<?php endforeach ?>
<?php endif; ?>
<!-- create new items -->
<div class="many-many-item just-empty-form-template-manyManyRelation">
<?php echo $form->labelEx($model, "manyManyRelation..note"); ?>
<?php echo $form->textField($model, "manyManyRelation..note"); ?>
<?php echo $form->error($model, "manyManyRelation..note"); ?>
<a href="#" class="delete">Delete</a>
</div>
<a href="#" class="add">Add more</a>
</div>
<div class="row buttons">
<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>
</div>
<?php $this->endWidget(); ?>
<script type="text/javascript">
$(document).ready(function(){
// init controls for multiply form
$('.hasManyRelation').multiplyForms({
embedClass: 'has-many-item',
templateClass: 'just-empty-form-template-hasManyRelation'
});
$('.manyManyRelation').multiplyForms({
embedClass: 'many-many-item',
templateClass: 'just-empty-form-template-manyManyRelation',
addLink: '.add',
deleteLink: '.delete',
afterAdd: function(embedForm, multiplyFormInstance){},
beforeDelete: function(embedForm, multiplyFormInstance){}
});
});
</script>
Real Examples ¶
Changelog ¶
0.2
- Cascade delete
- Remove old related item during update
- Multiple bug fixes
- jQuery plugin: append mode
- jQuery plugin: replace callbacks with events
0.1
- Public release
Thank you!
I Just onder one thing, the first inputfield for a manymanyrelation shows up disabled?
But when I press Add, the new ones that appear works. Also works perfectly fine to save it even if the first field is disabled and empty.. how do i fix this?:)
one small problem
There is one small problem with many relations if you want to get the ID of the input field via
CHtml::activeId($model, "manyManyRelation.$index.note");
you will get a wrong id back.
You will have to use CHtml::activeId($model, "manyManyRelation[$index][note]");
to get the correct id back.
Amazing extension
Guys this is an amazing extension, I was working in something like this, but it handles the AR relations really beautiful, just some cuestions, can it handle HAS_MANY|HAS_ONE with the "through" parameter? and how many levels of relations(depth) can I go?, again, GREAT extension.
Thank you.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.