Adding temporary field in CRUD -> ADD

#1

Time for another tip. This time i’ll explain how you can add a “temporary” field on the CRUD’s default “Add” form and after record is added use the value of this form to perform further action.

In my scenario I have the following models:

Model_Syndicate

$this->addField('name');
$this->hasMany('Syndicate_Manager');

Model_User

$this->addField('name');
$this->hasMany('Syndicate_Manager');

and finally Model_Syndicate_Manager:

$this->hasOne('User');
$this->hasOne('Syndicate');

This sets you up for a classical many-to-many relation. Normally if you build admin, you would add something like this:

$crud = $this->add('CRUD');
$crud->setModel('Syndicate');
$crud->addRef('Syndicate_Manager');

Now you have a simple CRUD, where you can add a Syndicate, then use expander to add a manager. In my business logic, however, syndicate without manager does not make any sense, and my client asked to make it possible to select first manager right away by having a dropdown at the Add form:

Here are the steps how you can implement this functionality:

Step1: create model before assigning it to CRUD

$crud = $this->add('CRUD');
$m = $this->add('Model_Syndicate');

// More code to tweak model here

$crud->setModel($m);

Step2: Come up with a plan

To implement what we want, I’ll have to add a new field into the model, but then I need to remove the data and the field before it hits the database. I’ll have to use hooks beforeInsert and afterSave. I also will have to copy the field value into a model property so that’s it’s not lost.

Before I add the hooks, however, I want to make sure the field is added, so I’m creating a big IF block:

if($cr->isEditing('add')){
    $m->addHook('beforeInsert',function($m,$dsql){   // 1
        unset( $dsql->args['set']['manager_id']);    // 2
        $m->getElement('manager_id')->destroy();     // 3
        $m->first_manager= $m['manager_id'];         // 4
    });

    $m->addHook('afterSave',function($m){    // 5
        if(!$m->first_manager)return;        // 6
        $sm = $m->ref('Syndicate_Manager');  // 7
        $sm['user_id'] = $m->first_manager;  // 8
        $sm->save();                         // 9
    });
}

Next I describe what each line do:

  1. beforeInsert hook will be triggered after model decides which fields needs saving. I’m working with $m and $dsql
  2. I need to prevent $dsql from attempting to insert manager_id because that field does not actually existing in syndicate table.
  3. I will not need the field anymore, otherwise model will attempt to select data from this field.
  4. I copy value of manager_id which would be acquired from the form into a model property, where it would be safe until the next hook.
  5. afterSave will be executed after model is saved and re-loaded. At this moment $m['manager_id'] will be gone, but the property will remain.
  6. If save() was triggered by modify action the first_manager property will not be present, therefore no need for us to do anything.
  7. Traverse into Syndicate_Manager entity
  8. set user_id of our first manager to saved value
  9. save our many-to-many entity.

I hope you will find this useful. Remember that if you need to further modify your “ADD” form, often it’s better to disable default “add” action and add a custom action to the CRUD instead of trying to tweak the default functionality. Also you can look at extending CRUD and overriding its configureAdd method.

#2

Once we also required this so we approached in different style

$m = $this->add('Model_Lead');

$crud = $this->add('CRUD');

if($crud->isEditing('add')){
    $assos_j = $m->join('lead_category_assos.lead_id');
    $assos_j->hasOne('Category','category_id')->mandatory(true);
}

$crud->setModel($m);

and it was done.

Is it okey or can result in any breaks in any situation???

#3

That’s a good solution too, however you simply adding record into database, if lead_category_assos has any hooks, you might be loosing them out, but I don’t think it’s a case with many-to-many relations, so great addition Gowrav.

1 Like