• Resolved milerm

    (@milerm)


    (This is a repost from https://stackoverflow.com/questions/53086726/cmb2-custom-repetable-fields-appear-six-times-each-and-wont-save – I got no help nor even views from there, though seems chances of getting help here are not much better).

    I’m trying to deploy a couple of custom repeatable fields, phone and mobile. Each field has several inputs (Country code, area code (for phone), number, and extension(s) (for phone)). These fields will go along with some others as part of a ‘contacts’ set, which are into group fields. So, repeatable custom field into a group field.

    On a existing class I was using to return values from certain CPTs, and based on the example of an US address field type provided by CMB2 devs (which to be honest I couldn’t get to work – it failed saying it couldn’t find the CMB2_Type_Base class) I included the following to render, sanitize and escape such fields:

    class cmb2Opciones
    {
        public function __construct()
        {
            add_action( 'cmb2_sanitize_fijos', array( $this, 'cmb2_sanitize_telefonos_callback' ), 10, 5 );
            add_action( 'cmb2_sanitize_celulares', array( $this, 'cmb2_sanitize_telefonos_callback' ), 10, 5 );
            add_action( 'cmb2_types_esc_fijos', array( $this, 'cmb2_escape_telefonos_callback' ), 10, 4 );
            add_action( 'cmb2_types_esc_celulares', array( $this, 'cmb2_escape_telefonos_callback' ), 10, 4 );
            add_action( 'cmb2_render_fijos', array( $this, 'cmb2_render_fijos_callback' ), 10, 5 );
            add_action( 'cmb2_render_celulares', array( $this, 'cmb2_render_celulares_callback' ), 10, 5 );
        }
    
        public function cmb2_escape_telefonos_callback( $check, $meta_value, $field_args, $field_object )
        {
            if ( ! is_array( $meta_value ) || ! $field_args['repeatable'] )
            {
                return $check;
            }
    
            foreach ( $meta_value as $key => $val )
            {
                if ( empty ( implode ( '', $val )))
                {
                    unset ( $meta_value[$key] );
                }
    
                else
                {
                    $meta_value[ $key ] = esc_attr ( $val );
                }
            }
    
            return array_filter( $meta_value );
        }
    
        public function cmb2_sanitize_telefonos_callback( $check, $meta_value, $object_id, $field_args, $sanitize_object )
        {
            if ( ! is_array( $meta_value ) || ! $field_args['repeatable'] )
            {
                return $check;
            }
    
            foreach ( $meta_value as $key => $val )
            {
                $meta_value[ $key ] = array_filter( array_map( 'sanitize_text_field', $val ) );
                }
    
            return array_filter( $meta_value );
        }
    
        public function cmb2_render_fijos_callback( $field, $escaped_value, $object_id, $object_type, $field_type )
        {
            $escaped_value = wp_parse_args( $escaped_value, array(
                'fijo_pais' => '',
                'fijo_area' => '',
                'fijo_numero' => '',
                'fijo_extension' => '',
            ));
    
            $output  = '<div style="overflow:hidden">';
            $output .= '<div class="alignleft" style="width:4em">';
            $output .= $field_type->input( array(
                'name'  => $field_type->_name( '[fijo_pais]' ),
                'id'  => $field_type->_id( '_fijo_pais' ),
                'value'  => $escaped_value['fijo_pais'],
                'type' => 'number',
            ));
            $output .= '</div>';
            $output .= '<div class="alignleft" style="width:3em">';
            $output .= $field_type->input( array(
                'name'  => $field_type->_name( '[fijo_area]' ),
                'id'  => $field_type->_id( '_fijo_area' ),
                'value'  => $escaped_value['fijo_area'],
                'type' => 'number',
            ));
            $output .= '</div>';
            $output .= '<div class="alignleft" style="width:calc(100% - 25em)">';
            $output .= $field_type->input( array(
                'name'  => $field_type->_name( '[fijo_numero]' ),
                'id'  => $field_type->_id( '_fijo_numero' ),
                'value'  => $escaped_value['fijo_numero'],
                'type' => 'number',
            ));
            $output .= '</div>';
            $output .= '<div class="alignleft" style="width:15em">';
            $output .= $field_type->input( array(
                'name'  => $field_type->_name( '[fijo_extension]' ),
                'id'  => $field_type->_id( '_fijo_extension' ),
                'value'  => $escaped_value['fijo_extension'],
                'type' => 'text',
            ));
            $output .= '</div></div>';
    
            echo $output;
            echo $field_type->_desc( true );
        }
    
        public function cmb2_render_celulares_callback( $field, $escaped_value, $object_id, $object_type, $field_type )
        {
            $escaped_value = wp_parse_args( $escaped_value, array(
                'celular_pais' => '',
                'celular_numero' => '',
            ));
    
            $output  = '<div style="overflow:hidden">';
            $output .= '<div class="alignleft" style="width:4em">';
            $output .= $field_type->input( array(
                'name'  => $field_type->_name( '[celular_pais]' ),
                'id'  => $field_type->_id( '_celular_pais' ),
                'value'  => $escaped_value['celular_pais'],
                'type' => 'number',
            ));
            $output .= '</div>';
            $output .= '<div class="alignleft" style="width:calc(100% - 5em)">';
            $output .= $field_type->input( array(
                'name'  => $field_type->_name( '[celular_numero]' ),
                'id'  => $field_type->_id( '_celular_numero' ),
                'value'  => $escaped_value['celular_numero'],
                'type' => 'number',
            ));
            $output .= '</div></div>';
    
            echo $output;
            echo $field_type->_desc( true );
        }
    
    // Other non-related CMB2 functions...
    }

    And for the initialization I did the following:

    global $cmb2; // This is calling an instance of "cmb2Opciones", created at init
    
    $box->add_group_field( $group, array(
        'name' => __('Teléfono fijo', 'uniagraria'),
        'id' => $prefix . 'contactos_fijos',
        'type' => 'fijos',
        'repeatable' => true,
        'sanitization_cb' => array( $cmb2, 'cmb2_sanitize_telefonos' ),
        'escape_cb' => array( $cmb2, 'cmb2_escape_telefonos' ),
        'text' => array(
            'add_row_text' => __('Agregar otro teléfono fijo', 'uniagraria'),
        ),
    ));
    
    $box->add_group_field( $group, array(
        'name' => __('Celular', 'uniagraria'),
        'id' => $prefix . 'contactos_celulares',
        'type' => 'celulares',
        'repeatable' => true,
        'sanitization_cb' => array( $cmb2, 'cmb2_sanitize_telefonos' ),
        'escape_cb' => array( $cmb2, 'cmb2_escape_telefonos' ),
        'text' => array(
            'add_row_text' => __('Agregar otro celular', 'uniagraria'),
        ),
    ));

    The problem is that for each repeatable field it shows 6 times the custom field, and if I fill up a row it just won’t save just because the web is magic and wonderful:

    I’m not sure if there’s something I’m doing wrong or if this is some kind of bug, ’cause I recall there were some fixes in CMB2 to make it more field-repeating friendly. However, I will glad if someone can point me out on what is going on.

    The page I need help with: [log in to see the link]

Viewing 7 replies - 1 through 7 (of 7 total)
  • Plugin Contributor Michael Beckwith

    (@tw2113)

    The BenchPresser

    For some initial debugging, have you tried the fields in your second snippet of code without the custom callbacks, to see if they behave properly at that point? If yes, then I can definitely continue helping to see if anything obvious comes up with the rest of the provided code.

    Thread Starter milerm

    (@milerm)

    Hello,

    Thank you so much for your answer.

    I did commented the sanitization_cb and escape_cb lines on the second part and tried again, but it had no effect: it’s showing six times the inputs for field. Also I tried filling them (once with just one row of inputs, another one with all the inputs) but it didn’t save.

    Still I have no idea about what’s going on – bet I screwed it at some point but can’t figure out where.

    Best regards

    Plugin Contributor Michael Beckwith

    (@tw2113)

    The BenchPresser

    Can you provide the entirety of your code being used for your CMB2 usage? For example that $group variable assignment, plus anything else? It’ll help me set up and potentially recreate.

    Thread Starter milerm

    (@milerm)

    Yeah, sure. Here you go (It’s pretty large, so I thought it would be embarrasing to just paste it here right away):

    https://pastebin.com/RA5S2WWh

    Thank you so much

    Plugin Contributor Michael Beckwith

    (@tw2113)

    The BenchPresser

    I have done some testing and have found that the separation of some of your code, specifically the methods used to render your fields, is causing the repeated nature. I’m fairly certain that that secondary class, the cmb2Opciones one is getting instantiated multiple times, and with the add_actions all being in the __construct() method, it’s all getting run excessively. Once I moved those render methods into the same class that’s setting up the fields, I was only getting 1 line at a time like expected. I also have to hedge some bets in that this is also why the sanitization/escaping aren’t working quite right either.

    For what it’s worth, I do see this following blob of content saved in the postmeta:

    a:2:{i:0;a:8:{s:37:"_educacionpermanente_contactos_nombre";s:0:"";s:36:"_educacionpermanente_contactos_cargo";s:0:"";s:36:"_educacionpermanente_contactos_fijos";a:2:{i:0;s:5:"Array";i:1;s:5:"Array";}s:40:"_educacionpermanente_contactos_celulares";a:2:{i:0;s:5:"Array";i:1;s:5:"Array";}s:37:"_educacionpermanente_contactos_correo";s:0:"";s:35:"_educacionpermanente_contactos_sede";s:0:"";s:46:"_educacionpermanente_contactos_notificarcorreo";b:0;s:38:"_educacionpermanente_contactos_mostrar";b:0;}i:1;a:2:{s:36:"_educacionpermanente_contactos_fijos";a:2:{i:0;s:5:"Array";i:1;s:5:"Array";}s:40:"_educacionpermanente_contactos_celulares";a:2:{i:0;s:5:"Array";i:2;s:5:"Array";}}}
    

    The presence of “Array” indicates that things aren’t set up quite right yet. It’s trying to either save or display arrays, when it needs to iterate over the array data to output the individual strings. You’d need to work out what exactly is getting saved at the time, and what your data state is.

    Thread Starter milerm

    (@milerm)

    You are absolutely right! cmb2Opciones was being instantiated multiple times and I didn’t even noticed. Calling it from a global var solved the multiple rendering thing.

    Will now take a look into why it’s saving it as an array, but you saved my ass. Thank you so much for all your help. 🙂

    Plugin Contributor Michael Beckwith

    (@tw2113)

    The BenchPresser

    Welcome 🙂

Viewing 7 replies - 1 through 7 (of 7 total)

The topic ‘Custom repetable fields appear six times each and won’t save’ is closed to new replies.