Jump to content

Recommended Posts

Posted

Hello,

i am trying to develop a simple Fieldtype module and followed Tutorial on developing a Fieldtype module.

I installed the module (Fieldtype and Inputfield) created a field of that type, add such a field to a template and edited a page.

After entering values and click save button the method processInput() is invoked but then nothing happens,  sleepValue() and wakeupValue() are never invoked, the resulting editor page ends up showing empty entry fields.

What i'm getting wrong?

 

<?php

namespace ProcessWire;

class InputfieldTimeframe  extends Inputfield  {

    /** 
     * getModuleInfo is a module required by all modules to tell ProcessWire about them.
     * Information about your module and some settings and definition of module dependcies
     * @return array
     */ 
    public static function getModuleinfo()  {
        return [
            'title'    => __('Inputfield Timeframe',                   __FILE__),
            'summary'  => __('Inputfield for start date and end date', __FILE__),
            'author'   => 'T. Heinlein',
            'version'  => "0.1.2",
            'icon'     => 'calendar',
            'permanent' => false,
            'requires' => [
                'PHP>=8.0.0',
                'ProcessWire>=3.0.244',
                'FieldtypeTimeframe'
            ],
        ];
    }


    public function __construct() {
        parent::__construct();
    }



    public function ___render()  {
        $value = $this->attr('value');
        $field = $this->field;

        $html = "<div class='interval'>"
        . "<input type='date' name='datefrom'>"
        . "<input type='date' name='dateto'>"
        . "</div>";
        return "<div style='border:solid 1px red;'>$html</div>";
    }


    public function renderValue()  {
        $value = $this->attr('value');
        return "$value";   // TODO:  locale format
    }

    public function ___processInput(WireInputData $input) :self {
        $value = $this->attr('value');
        $input->datefrom = trim($input->datefrom);
        $input->dateto   = trim($input->dateto);

        $value['datefrom'] = $input->datefrom;
        $value['dateto']   = $input->dateto;

        bd($value, "processInput");

        return $this;
    }
}

 

<?php

namespace ProcessWire;


class FieldtypeTimeframe extends Fieldtype  {


	/** 
     * getModuleInfo is a module required by all modules to tell ProcessWire about them.
     * Information about your module and some settings and definition of module dependcies
     * @return array
     */ 
    public static function getModuleinfo()  {
        return [
            'title'    => __('Timeframe',                           __FILE__),
            'summary'  => __('Fieldtype representing a timeframe with start and end date', __FILE__),
            'author'   => 'T. Heinlein',
            'version'  => "0.1.1",
            'icon'     => 'calendar',
            'installs' => 'InputfieldTimeframe',
            'requires' => [
                'PHP>=8.0.0',
                'ProcessWire>=3.0.244'
            ]
        ];
    }


    public function __construct() {
        parent::__construct();
    }


    /**
     * define the table columns needed for that field type AND an additional column 'data' for a primary key.
     * (this is low level input for the database)
     *  *** this method MUST be implemented when writing a custom fieldtype
     */
    public function getDatabaseSchema(Field $field) : array  {
        $schema = parent::getDatabaseSchema($field);
        $schema['data']      = 'varchar(64) NOT NULL';    /* data is required field for system purposes */
        $schema['datefrom']  = 'date';
        $schema['dateto']    = 'date';
        return $schema;
    }

    public function ___sleepValue(Page $page, Field $field, $value)  {
        bd($value, "sleepValue");

        $df = $value['datefrom'];
        $dt = $value['dateto'];

        $res = ['datefrom'=> $df, 'dateto' => $dt];
        return $res;
    }


    public function wakeupValue(Page $page, Field $field, $value)  {
        bd($value, "wakeupValue");
        return $value;
    }

    public function getBlankValue(Page $page, Field $field) {
        return array();
    }


    public function getInputfield(Page $page, Field $field) {
        $inputfield = $this->wire('modules')->get('InputfieldTimeframe');
        $inputfield->class = $this->className(); /* adds the class name as a css class to the wrapper element */

        $pageValue = $page->get($field->name);
        $inputfield->setAttribute('value', $pageValue);

        return $inputfield;
    }
    

        /**
     * @param Page $page
     * @param Field $field
     * @param int|object|WireArray|string $value
     * @return int|null|object|OpeningHours|WireArray|string
     * will be called on before sleepValue and after wakeupValue
     */
    public function sanitizeValue(Page $page, Field $field, $value)  {
        if (! is_array($value) ) {
            $value = $this->getBlankValue($page, $field);
        }
        return $value;
    }
}

 

Posted

@thei I think the issue may be that the 'data' column isn't used here, and ProcessWire wants you to use it. So you could perhaps use 'data' as your "date_from" and then add another column called "to" or something, and use that as your "date_to". The only methods that would need to know about "data" and "to" would be those that communicate with the database: getDatabaseSchema(), wakeupValue() and sleepValue(). Everywhere else can refer to date_from and date_to. The wakeupValue would convert "data" and "to" to "date_from" and "date_to", while the sleepValue() would convert "date_from" to "data" and "date_to" to "to" (or whatever column names you decide to use). Example of the Fieldtype portion below:


public function getBlankValue(Page $page, Field $field) {
  return [ 
    'date_from' => '', 
    'date_to' => '' 
  ]; 
}

public function getDatabaseSchema(Field $field) : array  {
  $schema = parent::getDatabaseSchema($field);
  $schema['data'] = 'date default null'; // date_from
  $schema['to'] = 'date default null'; // date_to
  $schema['keys']['data'] = 'KEY data (`data`, `to`)';
  $schema['keys']['to'] = 'KEY `to` (`to`)';
  return $schema; 
}

public function ___sleepValue(Page $page, Field $field, $value)  {
  $value = $this->sanitizeValue($page, $field, $value); 
  
  // store blank as null in DB
  if(empty($value['date_from'])) $value['date_from'] = null;
  if(empty($value['date_to'])) $value['date_to'] = null;
  
  // return value ready to store in DB
  return [ 
    'data' => $value['date_from'], 
    'to' => $value['date_to'] 
  ];
}

public function ___wakeupValue(Page $page, Field $field, $value)  {
  if(!is_array($value)) return $this->getBlankValue($page, $field); 
  // return value ready for $page->fieldName
  return [ 
    'date_from' => "$value[data]", 
    'date_to' => "$value[to]" 
  ];    
}

public function sanitizeValue(Page $page, Field $field, $value)  {
  if(!is_array($value)) $value = [];
  $value = array_merge($this->getBlankValue($page, $field), $value);
  
  $dateFrom = $value['date_from']; 
  if($dateFrom) $dateFrom = wireDate('Y-m-d', $dateFrom);
  
  $dateTo = $value['date_to']; 
  if($dateTo) $dateTo = wireDate('Y-m-d', $dateTo); 
  
  return [ 
    'date_from' => $dateFrom, 
    'date_to' => $dateTo 
  ]; 
}

Next, I don't think your Inputfield processInput() method will work as-is, so I'd suggest changing it to something like this:

public function ___processInput(WireInputData $input) {
  $value = [ 'date_from' => '', 'date_to' => '' ];
  
  foreach(array_keys($value) as $key) {
    $date = $input->get($key); 
    $value[$key] = $date ? wireDate('Y-m-d', $date) : ''; 
  }

  $this->val($value);

  return $this;
}

Once you've got it all working, you might consider changing the array value to a WireData value. That will make it so that you only ever have to define the date_from/date_to array once, and it can sanitize itself rather than these other methods. 

Posted (edited)

Dear Ryan,

thank you for your help.

But after i uninstalled and purged the old stuff and installed your refactored code the behaviour is similar. The db table is ok and looks like defined in getDatabaseSchema().

processInput receives the input values and 

$this->val($value);
holds the right data. After that no further data processing. Even in FieldType.php no savePageField() (which was called when using my posted old code but does not detect a change and therefore returned
if(!$page->isChanged($field->name)) return true; 

 

Edited by thei

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
  • Recently Browsing   1 member

×
×
  • Create New...