on_after_save explanation

edited February 2011 in Modules
I'm building an intranet site for a guy who owns a server farm. They wanted to be able to show the status of their various servers to select customers. I've built an intranet_users table, as well as a servers and a users_to_servers table. The intranet_users and servers tables each have an id column. The users_to_servers table will store records consisting of user_id and server_id. The servers_model has the minimal stuff:
class Servers_model extends Base_module_model { public $required = array('server_name','permalink'); function __construct() { parent::__construct('servers'); //table } }
The intranet_users model has the following:
class Intranet_users_model extends Base_module_model { public $required = array('first_name','last_name', 'username', 'password', 'pass_question', 'pass_code'); public $foreign_keys = array('server_id' => 'servers_model'); public $has_and_belongs_to_many = array('user_id' => 'users_to_servers_model'); function __construct() { parent::__construct('intranet_users'); //table } function form_fields($values = array()) { $fields = parent::form_fields(); $CI =& get_instance(); $CI->load->model('servers_model'); $CI->load->model('users_to_servers_model'); $server_options = $CI->servers_model->options_list('id', 'server_name', array('published' => 'yes'), 'server_name'); $server_values = (!empty($values['id'])) ? array_keys($CI->users_to_servers_model->find_all_array_assoc('server_id', array('user_id' => $values['id'], 'servers.published' => 'yes'))) : array(); $fields['view_servers'] = array('label' => 'Check servers you want to allow this user to view', 'type' => 'array', 'options' => $server_options, 'value' => $server_values, 'mode' => 'multi'); return $fields; } // add servers function on_after_save($values) { $CI =& get_instance(); $data = $this->normalized_save_data['view_servers']; $this->save_related('users_to_servers_model', array('user_id' => $values['id']), array('server_id' => $data)); } }
The users_to_servers model has the following:
class Users_to_servers_model extends MY_Model { public $record_class = 'User_to_server'; public $foreign_keys = array('user_id' => 'intranet_users_model', 'server_id' => 'servers_model'); function __construct() { parent::__construct('users_to_servers'); } function _common_query() { $this->db->select('users_to_servers.*, intranet_users.id as user_id, serers.id as server_id'); $this->db->join('intranet_users', 'users_to_servers.user_id = intranet_users.id', 'left'); $this->db->join('servers', 'users_to_servers.server_id = servers.id', 'left'); } } class User_to_server_model extends Data_record { public $server_name = ''; public $first_name = ''; public $last_name = ''; public $user_id; }
A list of checkboxes shows up on the intranet users detail admin page in the CMS but on update, only the last checked box is entered in the users_to_servers table. In other words, only one record. I need to be able to do a loop and save all checked boxes where it looks to see if the record containing that user_id and that server_id already exists - if so, don't add another one, if not add the record. So, if 4 boxes are checked and we're saving this user for the first time, all 4 records are added to the users_to_servers table.

What are the best fuel methods to use in order to achieve this?

Thanks in advance.

Comments

  • edited 5:03PM
    By the way, it's not a list of checkboxes that shows up but a select list (when I first started this I had set the mode to auto instead of multi)
  • edited 5:03PM
    I think what you have there is what you want. One fix I would make would be to do this to check the data exists and if not create an empty array (the example documentation in the tutorial was updated a while ago to reflect this).
    $data = (!empty($this->normalized_save_data['view_servers'])) ? $this->normalized_save_data['view_servers'] : array();
    This method will first delete the values and in the lookup table associated with that user, and then save the new values from the $_POST.

    Also, as an FYI, the $has_and_belongs_to_many property doesn't exist in MY_Model (may be left over from an earlier version).
  • edited 5:03PM
    Thanks for the reply, David. Ok, I've commented out the $has_and_belongs_to_many line and have replaced $data = $this->normalized_save_data['view_servers'];with$data = (!empty($this->normalized_save_data['view_servers'])) ? $this->normalized_save_data['view_servers'] : array();
    What I can't figure out is why multiple records are not being added to the users_to_servers table? Only one server id is being added. In fact, if you update a record and add a server to the list for a user, it replaces the one that's in the table with the one you added.

    One thing that was happening too is the code was adding a dropdown menu just before the multi-select list. I commented out the line:public $foreign_keys = array('server_id' => 'servers_model');in the intranet_users_model as I remembered this being something that fuel looks for and adds a dropdown menu. The extra dropdown menu went away after I commented that line out.

    In trying to figure out why only one server was being saved, I changed the on_after_save() to be:function on_after_save($values) { $CI =& get_instance(); $CI->load->model('servers_model'); $servers = $CI->servers_model->find_all(array('published' => 'yes')); $total_servers = count($servers); for($i=0;$i<$total_servers;$i++) { $data = (!empty($this->normalized_save_data["view_servers[$i]"])) ? $this->normalized_save_data["view_servers[$i]"] : array(); $this->save_related('users_to_servers_model', array('user_id' => $values['id']), array('server_id' => $data)); } }The above actually deletes all the records with that user_id from the users_to_servers table.

    Please notice in the screenshot I'm sending to you that the multi-select form element name is showing up as an array but the id isn't. It looks like this:
    <select multiple="multiple" id="view_servers" name="view_servers[]" style="display: none;"> <option label="T08-INS-01" value="2">T08-INS-01</option> <option label="T08-INS-02" value="3">T08-INS-02</option> <option label="T08-INS-03" value="4">T08-INS-03</option> <option label="T08-INS-07" value="1">T08-INS-07</option> </select>
  • edited 5:03PM
    The name of the field is correct. Using square brackets for an ID is invalid markup so they get stripped out when the form is created. The name needs the square bracket so the $_POST value will be an array of values.

    With regards to it not saving, perhaps try doing it without using the save_related method to see if you can get it to work. If you look at the MY_Model.php file you'll see roughly what is done, and you can try and replicate that. Basically, you grab the $_POST data, delete everything associated with the user_id in the users_to_servers_model, then loop through the data and save each one to the users_to_servers_model.
  • edited February 2011
    I think the problem might be my understanding of how the post data makes its way to the on_after_save function. I changed it to this and now nothing is happening:
    function on_after_save($values) { $CI =& get_instance(); $CI->load->model('users_to_servers_model'); $user_id = $this->id; // delete all the existing records from the users_to_servers table that contain this user's id $where = array('user_id'=>$user_id); $CI->db->delete('users_to_servers',$where); // loop through the post data and insert records for this user id where the server was selected in the post data for($i=0;$i<count($values['view_servers']);$i++) { $server_id = $values['view_servers'][$i]; $data = array('user_id'=>$user_id,'server_id'=>$server_id); $query = $this->db->insert($data); } }
    Am I correct in thinking that the array of server id's posted from the form are an array found in $values['view_servers'] and should be accessible as an array like:
    $values['view_servers'][0]
    $values['view_servers'][1]
    $values['view_servers'][2]
    and so on?
  • edited 5:03PM
    Well, geeez. It was the structure of the users_to_servers table. Both user_id and server_id needed to be set as primary keys. Only user_id was set as that. This is now working like a charm:function on_after_save($values) { $CI =& get_instance(); $CI->load->model('users_to_servers_model'); $posted = $this->normalized_save_data; $user_id = $posted['id']; // delete all the existing records from the users_to_servers table that contain this user's id $where['user_id'] = $user_id; $this->db->delete('users_to_servers',$where); // loop through the post data and insert records for this user id where the server was selected in the post data for($i=0;$i<count($posted['view_servers']);$i++) { $server_id = $posted['view_servers'][$i]; $data = array('user_id'=>$user_id,'server_id'=>$server_id); $query = $this->db->insert('users_to_servers',$data); } }
  • edited 5:03PM
    OK... glad you figured it out.
  • edited 5:03PM
    I have the same problem but i can't figure out.
    I get this error:
    Fatal error: Call to undefined method stdClass::save() in E:\wamp\www\project\fuel\application\core\MY_Model.php on line 1134

    my classes_model.php
    function form_fields($values = array())
    {
    $fields = parent::form_fields($values);

    $CI =& get_instance();
    $CI->load->module_model(EDUCATION_FOLDER, 'sections_model');
    $CI->load->module_model(EDUCATION_FOLDER, 'sections_to_classes_model');

    $section_options = $CI->sections_model->options_list('id', 'section_name', array('published' => 'yes'), 'section_name');
    $section_values = (!empty($values['id'])) ? array_keys($CI->sections_to_classes_model->find_all_array_assoc('section_id', array('class_id' => $values['id'], 'sections.published' => 'yes'))) : array();

    $fields['sections'] = array('label' => 'Sections', 'type' => 'array', 'class' => 'add_edit classes', 'options' => $section_options, 'value' => $section_values, 'mode' => 'multi');


    return $fields;
    }

    function on_after_save($values)
    {

    $this->load->module_model(EDUCATION_FOLDER, 'sections_to_classes_model');
    //$this->_tables = $CI->config->item('tables');

    $data = (!empty($this->normalized_save_data['sections'])) ? $this->normalized_save_data['sections'] : array();
    $this->save_related(array('education' => 'sections_to_classes_model'), array('class_id' => $values['id']), array('section_id' => $data));

    }

    sections_to_classes_model:

    public $key_field = array('class_id', 'section_id');
    private $_tables = array();
    public $record_class = 'Sections_to_classes';
    public $foreign_keys = array('section_id' => array('education' => 'sections_model'), 'class_id' => array('education' => 'classes_model'));

    function __construct()
    {
    parent::__construct('sections_to_classes'); // table name
    }

    function _common_query()
    {
    $this->db->select('sections_to_classes.*, classes.class_name, sections.section_name, sections.published');
    $this->db->join('classes', 'sections_to_classes.class_id = classes.id', 'left');
    $this->db->join('sections', 'sections_to_classes.section_id = sections.id', 'left');
    }

    }

    class Section_to_class_model extends Data_record {
    public $class_name = '';
    public $section_name = '';
    //public $title = '';
    }


    sections_model:

    function list_items($limit = NULL, $offset = NULL, $col = 'section_name', $order = 'asc')
    {
    $this->db->select('
    id,
    section_name,
    published
    ', FALSE);
    $data = parent::list_items($limit, $offset, $col, $order);
    return $data;
    }


    function form_fields($values = array())
    {
    $fields = parent::form_fields($values);


    return $fields;
    }
    }
    }
  • edited October 2011
    I believe you need to add the "record_class" property to your model like so because the model is looking for record classes without the "s" and not those without an "es":

    class Pages_model extends Base_module_model { public $record_class = 'Section_to_class'; ....
  • edited 5:03PM
    Thanks. it worked
Sign In or Register to comment.