Jump to content

New created field type cannot store float values


Juergen
 Share

Recommended Posts

Hello @ all,

I have created a new fieldtype which should be able to store 4 float values. Therefore I have tried to adapt this fieldtype from Soma.

http://modules.processwire.com/modules/fieldtype-dimension/

Instead of storing integer values as in Somas fieldtype I want to store float values (fe. 3.5).

Here is what it looks like in the admin:

post-2257-0-14234100-1461150415_thumb.jp

Problem: It stores the values only as integer and not as float. This means if I enter fe 2.5 it stores always 2.

Here are the 2 files for the fieldtype:

1) FieldtypeProductDate.module

<?php

/**
 * ProcessWire ProductData Fieldtype
 * Field that stores 4 float values for length/weight/minsize/maxsize.
 *
 * ProcessWire 2.x
 * Copyright (C) 2010 by Ryan Cramer
 * Licensed under GNU/GPL v2, see LICENSE.TXT
 *
 * http://www.processwire.com
 * http://www.ryancramer.com
 *
 */

class FieldtypeProductData extends Fieldtype {

    public static function getModuleInfo() {
        return array(
            'title' => __('ProductData Fieldtype', __FILE__), // Module Title
            'summary' => __('Field that stores 4 float values for length, height and needle minsize and needle maxsize.', __FILE__), // Module Summary
            'version' => 100,
            'author' => 'Juergen',
            'installs' => 'InputfieldProductData'
        );
    }

    /**
     * Format value for output
     *
     */
    public function ___formatValue(Page $page, Field $field, $value) {
        return $value;
    }

    /**
     *
     * Add mapping to different name for use in page selectors
     * This enables us to use it like "field.length=100, field.weight<=200, field.needlemax>100"
     */
    public function getMatchQuery($query, $table, $subfield, $operator, $value) {
        if($subfield == 'length') $subfield = 'data_length';
        if($subfield == 'weight') $subfield = 'data_weight';
        if($subfield == 'needlemin') $subfield = 'data_needlemin';
        if($subfield == 'needlemax') $subfield = 'data_needlemax';
        return parent::getMatchQuery($query, $table, $subfield, $operator, $value);
    }

    /**
     * get Inputfield for this fieldtype, set config attributes so they can be used in the inputfield
     *
     */
    public function getInputfield(Page $page, Field $field) {
        $dim = $this->modules->get('InputfieldProductData');
        return $dim;
    }

    /**
     * there's none compatible
     *
     */
    public function ___getCompatibleFieldtypes(Field $field) {
        return null;
    }

    /**
     * blank value is an WireData object ProductData
     *
     */
    public function getBlankValue(Page $page, Field $field) {
        return new ProductData();
    }

    /**
     * Any value will get sanitized before setting it to a page object
     * and before saving the data
     *
     * If value not of instance ProductData return empty instance
     */
    public function sanitizeValue(Page $page, Field $field, $value) {

        if(!$value instanceof ProductData) $value = $this->getBlankValue($page, $field);

        // report any changes to the field values
        if($value->isChanged('length')
                || $value->isChanged('weight')
                || $value->isChanged('needlemin')
                || $value->isChanged('needlemax')) {
            $page->trackChange($field->name);
        }

        return $value;
    }

    /**
     * get values converted when fetched from db
     *
     */
    public function ___wakeupValue(Page $page, Field $field, $value) {

        // get blank dim
        $dim = $this->getBlankValue($page, $field);

        // populate the dim
        $dim->length = (float) $value['data_length'];
        $dim->weight = (float) $value['data_weight'];
        $dim->needlemin = (float) $value['data_needlemin'];
        $dim->needlemax = (float) $value['data_needlemax'];

        return $dim;
    }

    /**
     * return converted from object to array for storing in database
     *
     */
    public function ___sleepValue(Page $page, Field $field, $value) {

        // throw error if value is not of the right type
        if(!$value instanceof ProductData)
            throw new WireException("Expecting an instance of ProductData");

        $sleepValue = array(
            'data_length' => (float) $value->length,
            'data_weight' =>  (float) $value->weight,
            'data_needlemin' => (float) $value->needlemin,
            'data_needlemax' => (float) $value->needlemax
            );
        return $sleepValue;
    }

    /**
     * Get the database schema for this field
     *
     * @param Field $field In case it's needed for the schema, but usually should not.
     * @return array
     */
    public function getDatabaseSchema(Field $field) {

        $schema = parent::getDatabaseSchema($field);
        $schema['data_length'] = 'float NOT NULL default 0';
        $schema['data_weight'] = 'float NOT NULL default 0';
        $schema['data_needlemin'] = 'float NOT NULL default 0';
        $schema['data_needlemax'] = 'float NOT NULL default 0';
        // key for data will already be added from the parent
        $schema['keys']['data_weight'] = 'KEY data_weight(data_weight)';
        $schema['keys']['data_needlemin'] = 'KEY data_needlemin(data_needlemin)';
        $schema['keys']['data_needlemax'] = 'KEY data_volume(data_needlemax)';
        return $schema;
    }

    /**
     * Get any inputfields used for configuration of this Fieldtype.
     *
     * This is in addition any configuration fields supplied by the parent Inputfield.
     *
     * @param Field $field
     * @return InputfieldWrapper
     *
     */
    public function ___getConfigInputfields(Field $field) {
        $inputfields = parent::___getConfigInputfields($field);
        // nothing yet
        return $inputfields;
    }

}

/**
 * Helper WireData Class to hold a dimension object
 *
 */
class ProductData extends WireData {

    public function __construct() {
        $this->set('length', null);
        $this->set('weight', null);
        $this->set('needlemin', null);
        $this->set('needlemax', null);
    }

    public function set($key, $value) {

        if($key == 'length' || $key == 'weight' || $key == 'needlemin' || $key == 'volume') {
            // if value isn't numeric, don't change the value if already
            // one set, else set it to 0 and throw an exception so it can be seen on API usage
            if(!is_float($value) && !is_null($value)) {
                $value = $this->$key ? $this->$key : 0;
                throw new WireException("ProductData Object only accepts number values");
            }
        }
        return parent::set($key, $value);
    }

    public function get($key) {
        return parent::get($key);
    }

}

2) InputfieldProductData.module

<?php

/**
 * ProcessWire ProductData Inputfieldtype
 * ProcessWire 2.x
 * Copyright (C) 2010 by Ryan Cramer
 * Licensed under GNU/GPL v2, see LICENSE.TXT
 *
 * http://www.processwire.com
 * http://www.ryancramer.com
 *
 */

class InputfieldProductData extends Inputfield {

    public static function getModuleInfo() {
        return array(
            'title' => __('ProductData Inputfield', __FILE__), // Module Title
            'summary' => __('Simple dimension input field.', __FILE__), // Module Summary
            'version' => 100,
            'author' => 'Juergen',
            'requires' => array("FieldtypeProductData")
        );
    }

    /**
     * Construct the Inputfield, setting defaults for all properties
     *
     */

    /**
     * Per the Module interface, init() is called when the system is ready for API usage
     *
     */
    public function init() {
        return parent::init();
    }

    /**
     * Return the completed output of this Inputfield, ready for insertion in an XHTML form
     *
     * @return string
     *
     */
    public function ___render() {

        $out = '';
        $value = $this->attr('value') ? $this->attr('value') : new ProductData();

        $label_length = $this->_("Length");
        $label_weight = $this->_("Weight");
        $label_needlemin = $this->_("Needlesize min");
        $label_needlemax = $this->_("Needlesize max");

        $out .= "<div class='dimension_col'>";
        $out .= "<label>{$label_length}: <input type='number' min='0' style='width:100px' name='{$this->name}_length' id='Inputfield_{$this->name}_length' value='{$value->length}'/> m</label>";
        $out .= "</div>";

        $out .= "<div class='dimension_col'>";
        $out .= "<label>{$label_weight}: <input type='number' min='0' style='width:100px' name='{$this->name}_weight' id='Inputfield_{$this->name}_weight' value='{$value->weight}'/> g</label>";
        $out .= "</div>";

        $out .= "<div class='dimension_col'>";
        $out .= "<label>{$label_needlemin}: <input type='number' step='0.5' min='0' style='width:100px' name='{$this->name}_needlemin' id='Inputfield_{$this->name}_needlemin' value='{$value->needlemin}'/></label>";
        $out .= "</div>";
        
        $out .= "<div class='dimension_col'>";
        $out .= "<label>{$label_needlemax}: <input type='number' step='0.5' min='0' style='width:100px' name='{$this->name}_needlemax' id='Inputfield_{$this->name}_needlemax' value='{$value->needlemax}'/></label>";
        $out .= "</div>";

        return $out;
    }

    /**
     * Process the input from the given WireInputData (usually $input->get or $input->post), load and clean the value for use in this Inputfield.
     *
     * @param WireInputData $input
     * @return $this
     *
     */
    public function ___processInput(WireInputData $input) {

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

        $dim_names = array(
                'length' => $name . "_length",
                'weight' => $name . "_weight",
                'needlemin' => $name . "_needlemin",
                'needlemax' => $name . "_needlemax"
            );

        // loop all inputs and set them if changed
        foreach($dim_names as $key => $name) {
            if(isset($input->$name)) {
                if($value->$key != $input->$name) {
                    if(!is_numeric($input->$name) && !empty($input->$name)) {
                        // in case the input isn't numeric show an error
                        $this->error($this->_("Field only accepts number values"));
                    } else {
                        $value->set($key, (float) $input->$name);
                        $this->trackChange('value');
                    }

                }
            }
        }

        return $this;
    }

}

I have tried to change different parameters but nothing works. Has anyone an idea?

Link to comment
Share on other sites

The numbers are entered with a dot and not a comma. I have used the arrows on the right side of the inputfield to increase or decrease the number.

It seems that the decimal will be rounded.

On the other side, if I enter a decimal directly in the sql table I got a blank output in the field at the admin side.

So it outputs and stores only integer.

Link to comment
Share on other sites

Good idea but no effect.

Sanitization:

    public function sanitizeValue(Page $page, Field $field, $value) {
        $value = str_replace(",",".",$value);
        if(!$value instanceof ProductData) {
        $value = $this->getBlankValue($page, $field);
        } 
        // report any changes to the field values
        if($value->isChanged('length')
                || $value->isChanged('weight')
                || $value->isChanged('needlemin')
                || $value->isChanged('needlemax')) {
            $page->trackChange($field->name);
        }
        
        return $value;
    }

DB schema changed to decimal:

    public function getDatabaseSchema(Field $field) {

        $schema = parent::getDatabaseSchema($field);
        $schema['data_length'] = 'DECIMAL(12,1) NOT NULL default 0';
        $schema['data_weight'] = 'DECIMAL(12,1) NOT NULL default 0';
        $schema['data_needlemin'] = 'DECIMAL(12,1) NOT NULL default 0';
        $schema['data_needlemax'] = 'DECIMAL(12,1) NOT NULL default 0';
        // key for data will already be added from the parent
        $schema['keys']['data_weight'] = 'KEY data_weight(data_weight)';
        $schema['keys']['data_needlemin'] = 'KEY data_needlemin(data_needlemin)';
        $schema['keys']['data_needlemax'] = 'KEY data_volume(data_needlemax)';
        return $schema;
    }
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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...