Tagged: 

This topic contains 6 replies, has 3 voices, and was last updated by  Alex Kladov 1 week, 2 days ago.

  • Author
    Posts
  • #9428

    Alex Kladov
    Participant

    Hi,

    I am trying to figure out why is Piklist saving my Setting field as an array? This is how the filed is configured:

    piklist( 'field', array(
      'type'        => 'number',
      'field'       => 'max_past_orders_to_show',
      'label'       => __( 'Past Orders to Display', 'theme_domain' ),
      'help'        => __( 'Maximum number of Past Orders to display by default.', 'theme_domain' ),
      'description' => __( 'Set to 0 to always show all past orders right away. This will also permanently hide "Show All Past Orders" button. ', 'theme_domain' ),
      'value'       => 20,
      'columns'     => 3,
      'required'    => true,
      'attributes' => array(
        'min'  => 0,
        'step' => 5,
        'required' => 'required', // HTML 5 validation
      ),
      'validate' => array(
        array(
          'type'    => 'limit',
          'options' => array(
            'min' => 0,
          ),
        ),
      ),
    ) );

    And this is what’s saved in the wp_options for that value:

    [max_past_orders_to_show] => Array
    (
      [0] => 30
    )

    I have almost exactly the same field and it saves it as a plain number. Here is how that field is configured:

    piklist( 'field', array(
      'type'        => 'number',
      'field'       => 'sync_frequency',
      'label'       => __( 'Sync Frequency', 'theme_domain' ),
      'help'        => __( 'How often do you want to pull data from XYZ Service?', 'theme_domain' ),
      'description' => __( 'Set data sync frequency (in minutes). A lower value will lead to a more frequent synchronisation sycle & therefore might use more server resources. If you notice a significant performance impact, try increasing this value.', 'theme_domain' ),
      'value'       => 1,
      'columns'     => 3,
      'required'    => true,
      'attributes' => array(
        'min'  => 1,
        'step' => 1,
      ),
      'attributes' => array(
        'required' => 'required', // HTML 5 validation
      ),
      'validate' => array(
        array(
          'type'    => 'limit',
          'options' => array(
            'min' => 1,
          ),
        ),
      ),
    ) );

    And it’s value is simply [sync_frequency] => 1

    Why would there be a difference, if they are configured almost exactly the same way?

    Regards,
    Alex

  • #9429

    Steve
    Keymaster

    Piklist settings are saved using the WordPress settings api, which saves all data as an array.

    In the Piklist Demos we replicate the core WordPress SETTINGS > READING page, which saves data as separate values. Here are the examples from the Demos.

    You would need to create a form, like this:
    https://github.com/piklist/piklist/blob/develop/add-ons/piklist-demos/parts/forms/reading-settings.php

    And then create an admin page where you would embed your form:
    https://github.com/piklist/piklist/blob/c16ebde4eef529ad97337c83ac54c3192e0cddbd/add-ons/piklist-demos/parts/admin-pages/reading-settings.php#L26

  • #9435

    Alex Kladov
    Participant

    Hi @Steve,

    Piklist settings are saved using the WordPress settings api, which saves all data as an array.

    I understand that. My settings page looks something like this:

    <?php
    /**
     * Title: General
     * Tab: General
     * Setting: app_settings
     * Flow: App Workflow
     **/
    
    piklist( 'field', array(
      'type'        => 'number',
      'field'       => 'sync_frequency',
      'label'       => __( 'Sync Frequency', 'app_domain' ),
      'help'        => __( 'How often do you want to pull data from XYZ Service?', 'app_domain' ),
      'description' => __( 'Set data sync frequency (in minutes). A lower value will lead to a more frequent synchronisation sycle & therefore might use more server resources. If you notice a significant performance impact, try increasing this value.', 'app_domain' ),
      'value'       => 1,
      'columns'     => 3,
      'required'    => true,
      'attributes' => array(
        'min'  => 1,
        'step' => 1,
      ),
      'attributes' => array(
        'required' => 'required', // HTML 5 validation
      ),
      'validate' => array(
        array(
          'type'    => 'limit',
          'options' => array(
            'min' => 1,
          ),
        ),
      ),
    ) );
    
    piklist( 'field', array(
      'type'        => 'number',
      'field'       => 'max_past_orders_to_show',
      'label'       => __( 'Past Orders to Display', 'app_domain' ),
      'help'        => __( 'Maximum number of Past Orders to display by default.', 'app_domain' ),
      'description' => __( 'Set to 0 to always show all past orders right away. This will also permanently hide "Show All Past Orders" button. ', 'app_domain' ),
      'value'       => 20,
      'columns'     => 3,
      'required'    => true,
      'attributes' => array(
        'min'  => 0,
        'step' => 5,
        'required' => 'required', // HTML 5 validation
      ),
      'validate' => array(
        array(
          'type'    => 'limit',
          'options' => array(
            'min' => 0,
          ),
        ),
      ),
    ) );

    However, when I run var_export( get_option( 'app_settings' ) ); I get:

    array (
      'sync_frequency' => '1',
      'max_past_orders_to_show' => array (
        0 => '10',
      ),
    )

    Why is sync_frequency saved as a string (which could be casted to an int easily, so it’s ok), yet max_past_orders_to_show, which is setup exactly the same way, is saved as an array of strings? It’s so strange. I didn’t set it up to be a repeater field, why is Piklist doing it?

  • #9437

    Steve
    Keymaster

    I did some quick testing and for some reason 'min' => 0, forces the field to save as an array. I haven’t dug into the reason yet, but I thought it might help you to know.

  • #9438

    Alex Kladov
    Participant

    I knew that had something to do with it! Because it was one of the only configuration differences between the two..

    Please let me know here if/when you will release a patch for it. Because right now I was forced to use a workaround for it and request that value like this:

    $app_settings = get_option('app_settings');
    $max_past_orders_to_show = $app_settings['max_past_orders_to_show'][0];

    So if you would fix this bug, and field like this would revert to being a regular int, that will break my plugin’s functionality (plus I might not be the only one who had to resort to this “hackery”).

    • #9439

      mcmaster
      Participant

      Alex, instead of
      $max_past_orders_to_show = $app_settings['max_past_orders_to_show'][0];

      I recommend that you test the value and only index it if it’s an array.

      I have a little utility function I use for situations like this. If you don’t like recursion you may want to rewrite it. 😉

      /**
       *	First Leaf
       *	Finds the first non-array element (singular or object)
       *	The key() function gives us the first key in the array
       */
      public static function first_leaf ( $value ) {
      	return !is_array( $value ) ? $value : first_leaf( $value[key($value)] );
      }
      
  • #9440

    Alex Kladov
    Participant

    Hi @mcmaster,

    Thanks for the suggestion. It’s a handy little extractor function for sure. However, when would it ever be a nested array in this scenario? Just seems like an overkill for this simple scenario..

    Plus, I don’t like that it adds a level of uncertainty to the expected functionality. Instead of checking, if it is_array(), and then looping through that array until you hit a simple value, I would much rather check for is_int() and abort in all other cases. Because I can’t know what the hell Piklist added into those nested arrays (since they shouldn’t even be arrays to begin with), so even the first values can’t be fully trusted. What if it decides to return an Object or basically anything else, that’s not an int. I can’t trust any of those values.

    But for now, given that I know the exact problem, I am just trusting that Piklist will add the right value in the first position, even if it’s in an array, and then I grab that value, after checking with a simple inline ! is_array( $app_settings['max_past_orders_to_show'] ) ? '' : $app_settings['max_past_orders_to_show'] before I use it. But it’s definitely a bug that should be fixed, since there is no reason it should be saved as an array in this situation. It’s just bound to cause problems down the road, when this will be fixed one day.

You must be logged in to reply to this topic.