Jump to content

[SOLVED] Problem saving values from dynamic created inputfields in the database


Recommended Posts

Hello @ all,

I am creating a new inputfield/fieldtype to store opening hours, but I am struggeling to save values from multiple dynamic created inputfields in 1 column of the database.

Scenario:

The user can enter one or more opening times per day in a UI.

Fe:

  • Monday open from 08:00 to 12:00 and from 14:00 to 17:00
  • Tuesday open from 08:00 to 12:00 and from 14:00 to 19:00 and so on

skizze.jpg.02d04c57464b88d20cd79a599fa064fd.jpg

Via a little JavaScript you can add as much opening times as you need per day - the additional inputfield will be created dynamically.

After form submission all the values are in the POST array -> this works (see example below):

ProcessWire\WireInputData Object
(
	[openinghours_mo-0-start] => 09:00
    [openinghours_mo-0-finish] => 13:00
    [openinghours_mo-1-start] => 14:00
    [openinghours_mo-1-finish] => 18:00
    [openinghours_mo-2-start] => 21:00
    [openinghours_mo-2-finish] => 23:00
    [openinghours_tu-0-start] => 09:00
    [openinghours_tu-0-finish] => 13:00
    [openinghours_tu-1-start] => 14:00
    [openinghours_tu-1-finish] => 18:00
    [openinghours_we-0-start] => 09:00
    [openinghours_we-0-finish] => 13:00
    [openinghours_we-1-start] => 14:00
    [openinghours_we-1-finish] => 18:00
    [openinghours_th-0-start] => 09:00
    [openinghours_th-0-finish] => 13:00
    [openinghours_th-1-start] => 14:00
    [openinghours_th-1-finish] => 18:00
    [openinghours_fr-0-start] => 09:00
    [openinghours_fr-0-finish] => 13:00
    [openinghours_fr-1-start] => 14:00
    [openinghours_fr-1-finish] => 18:00
    [openinghours_sa-0-start] => 
    [openinghours_sa-0-finish] => 
    [openinghours_so-0-start] => 
    [openinghours_so-0-finish] => 
)

The property name is always the name attribute of the field ? . If the property is empty means closed on that day.

Now I need to combine all those values into 1 array (or json array) and store it in the database in 1 column called 'hours' in my case (see screenshot below):

Screenshot(4).png.03b4e61c044030eaa905530e1e80f80b.png

In my ___processInput(WireInputData $input) method I have tried to make it work like this:

public function ___processInput(WireInputData $input): self
    {

        $name = $this->attr('name');
        $value = $this->attr('value');

        //input object includes always every input on the page, so lets filter out only inputs from this field
        //we need to do this, because the number of values is variable - so extract only values that starts with $name.'_'
        $nameAttributes = [];
        foreach($input as $key=>$value){
          if(substr($key, 0, strlen($name.'_')) === $name.'_'){
            $nameAttributes[$key] = $value;
          }
        }

        // loop through all inputfields of this fieldtype
        $time_values = [];
        foreach($nameAttributes as $nameAttr => $value) {
            $time_values[$nameAttr] = $value;
          }
        }

        //save it in the database
        $input->set('hours', serialize($time_values));
        return $this;

    }

The only important part of this code is the last part with the serialize function.

After saving it will create a record in the database, but the value is always NULL (default value) (see below).

Screenshot(5).png.dbc818090e35e9be9669df84f33ca8a1.png

Checking $time_values returns all the values, but printing out "$this" shows me that the property "hours" inside the Openinghours object is empty (see below) - so the mistake must be there, but I dont know where?!?!?!?

[title] => Home
                            [openinghours] => ProcessWire\OpeningHours Object
                                (
                                    [data] => Array
                                        (
                                            [hours] => 
                                        )

                                )

If I check the sleepValue() method or the sanitizeValue() - they are also empty. So it seems that the values will not reach these methods. I havent found a clear documentation of whats going on behind the saving process of an inputfield.

As far as I know the saving process starts with the form submission. The values are in the POST array and will be processed by the processInput() method. Before they will be saved in the database they will be sanitized by the sanitizeValue() mehtod and afterwards they will be prepared for storage in the sleepValue() method.  The last step is the storage itself.

Has someone an idea what is missing by storing values from multiple fields into 1 database column or has someone a working example of such a scenario on github to help me out.

A clear explanation of the storage process will be also helpful.

Thanks and best regards

Link to comment
Share on other sites

OK, I am 1 step forward :-)

Adding

$this->value['hours'] = $time_values;

to the processInput() method adds the values to the object

Here is the complete code of this method:

public function ___processInput(WireInputData $input): self
    {

        $name = $this->attr('name');
        $value = $this->attr('value');

        //input object includes always every input on the page, so lets filter out only inputs from this field
        //we need to do this, because the number of values is variable - so extract only values that starts with $name.'_'
        $nameAttributes = [];
        foreach($input as $key=>$value){
          if(substr($key, 0, strlen($name.'_')) === $name.'_'){
            $nameAttributes[$key] = $value;
          }
        }



        // loop through all inputfields and set the value (if changed or not) back
        $time_values = [];
        foreach($nameAttributes as $nameAttr => $value) {
            $time_values[$nameAttr] = $value;
        }

        //add it to the object
        $this->value['hours'] = $time_values;

        return $this;

    }

Now the values are also reachable in sleepValue() method and the sanitizeValue() method.

ProcessWire\OpeningHours Object
(
    [data] => Array
        (
            [hours] => Array
                (
                    [openinghours_mo-0-start] => 09:00
                    [openinghours_mo-0-finish] => 13:00
                    [openinghours_mo-1-start] => 14:00
                    [openinghours_mo-1-finish] => 18:00
                    [openinghours_mo-2-start] => 21:00
                    [openinghours_mo-2-finish] => 23:00
                    [openinghours_tu-0-start] => 09:00
                    [openinghours_tu-0-finish] => 13:00
                    [openinghours_tu-1-start] => 14:00
                    [openinghours_tu-1-finish] => 18:00
                    [openinghours_we-0-start] => 09:00
                    [openinghours_we-0-finish] => 13:00
                    [openinghours_we-1-start] => 14:00
                    [openinghours_we-1-finish] => 18:00
                    [openinghours_th-0-start] => 09:00
                    [openinghours_th-0-finish] => 13:00
                    [openinghours_th-1-start] => 14:00
                    [openinghours_th-1-finish] => 18:00
                    [openinghours_fr-0-start] => 09:00
                    [openinghours_fr-0-finish] => 13:00
                    [openinghours_fr-1-start] => 14:00
                    [openinghours_fr-1-finish] => 18:00
                    [openinghours_sa-0-start] => 
                    [openinghours_sa-0-finish] => 
                    [openinghours_so-0-start] => 
                    [openinghours_so-0-finish] => 
                    [openinghours_ho-0-start] => 
                    [openinghours_ho-0-finish] => 
                )

        )

)

Nevertheless the value in the DB is NULL :-( 

Link to comment
Share on other sites

OK, I figured it out.

I transformed the values of the inputfields inside the sleepValue function to a json array and now the values will be stored in the database.

public function sleepValue(Page $page, Field $field, $value)
    {
      // throw error if value is not of the right type
      if (!$value instanceof OpeningHours) {
          throw new \Exception($this->_('Expecting an instance of OpeningHours'));
      }
      $content = json_encode($value->data['hours']);
      $sleepValue = ['hours' => $content];
      return $sleepValue;
    }

So the responsible lines are

$content = json_encode($value->data['hours']);
$sleepValue = ['hours' => $content];

 

Screenshot(6).png.e61b8d90f245d99774ae43da04876465.png

The problem of the storage was that the column 'hours' was not defined in the sleepValue method. This was the important part because without it the system doesnt know where to store the value.

 

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...