Yii 1.1: wform

Yii Composite Form Extension
29 followers

logo.jpg

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

Related forum thread

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

Product form example

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

Total 3 comments

#12712 report it
quarkmarino at 2013/04/07 04:13pm
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.

#7653 report it
horizons at 2012/04/05 05:13am
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.

#7047 report it
Sampa at 2012/02/21 05:56am
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?:)

Leave a comment

Please to leave your comment.

Create extension