Subversion Repositories web.creative

Rev

Blame | Last modification | View Log | Download

<?php namespace ProcessWire;

/**
 * ProcessWire Integer Fieldtype
 *
 * Field that stores an integer value. 
 *
 * For documentation about the fields used in this class, please see:  
 * /wire/core/Fieldtype.php
 * 
 * ProcessWire 3.x, Copyright 2020 by Ryan Cramer
 * https://processwire.com
 * 
 * @todo allow for more integer types (tiny, small, medium, big) and unsigned option
 * 
 */

class FieldtypeInteger extends Fieldtype {

  public static function getModuleInfo() {
    return array(
      'title' => 'Integer',
      'version' => 102,
      'summary' => 'Field that stores an integer',
      'permanent' => true, 
    );
  }

  /**
   * Get compatible Fieldtypes
   * 
   * @param Field $field
   * @return Fieldtypes
   * 
   */
  public function ___getCompatibleFieldtypes(Field $field) {
    $fieldtypes = parent::___getCompatibleFieldtypes($field); 
    foreach($fieldtypes as $type) {
      if(!$type instanceof FieldtypeInteger && !$type instanceof FieldtypeFloat && $type != 'FieldtypeText') {
        $fieldtypes->remove($type); 
      }
    }
    return $fieldtypes; 
  }

  /**
   * Is given value considered empty by this Fieldtype?
   * 
   * @param Field $field
   * @param mixed $value
   * @return bool
   * 
   */
  public function isEmptyValue(Field $field, $value) {
    if(($value === "0" || $value === 0) && $field->get('zeroNotEmpty')) {
      // when zeroNotEmpty option is set, we don't count a literal "0" is being a blank value
      return false;
    }
    return empty($value); 
  }

  /**
   * Is value one that should be deleted rather than stored in the DB?
   * 
   * @param Page $page
   * @param Field $field
   * @param mixed $value
   * @return bool
   * 
   */
  public function isDeleteValue(Page $page, Field $field, $value) {
    return $this->isEmptyValue($field, $value); 
  }

  /**
   * Get the blank value for this Fieldtype
   * 
   * @param Page $page
   * @param Field $field
   * @return string
   * 
   */
  public function getBlankValue(Page $page, Field $field) {
    return '';
  }

  /**
   * Sanitize value to integer or blank string (for no value)
   * 
   * @param Page $page
   * @param Field $field
   * @param int|string|mixed $value
   * @return int|string
   * 
   */
  public function sanitizeValue(Page $page, Field $field, $value) {

    if(is_string($value) && strlen($value) && !ctype_digit(ltrim($value, '-'))) {
      $value = $this->sanitizeValueString($value);
    } else {
      $value = strlen("$value") ? (int) $value : '';
    }

    return $value;

  }

  /**
   * Sanitize string to integer or blank string if empty
   * 
   * @param string $value
   * @return int|string
   * 
   */
  protected function sanitizeValueString($value) {
    
    // string value with one or more non-digit characters
    $value = trim($value);
    
    // trim off common currency symbols
    $value = trim($value, '$€ ');

    if(ctype_digit("$value")) {
      // trimming reduced it to an int

    } else if(preg_match('/^(\de\d|0x\d+|\+\d+)/', $value)) {
      // likely a valid number, but in a non-native format to PW 
      // examples: 1e123213, 0x1234, +123 (intval handles these)
      $value = intval($value);

    } else if(preg_match('/^[^-+\d.]+/', $value)) {
      // string starting with something we don't recognize, let PHP decide
      // example: bd#79
      $value = intval($value);
      if($value === 0) $value = ''; // blank rather than zero

    } else {
      // string value that looks like a number but has some other stuff in it

      // see if there are some definitely non-number chars in there, and truncate
      // the string to that point if we find any
      if(preg_match('/^(-?[\d,. ]+)([^\d,. ]+)/', $value, $matches)) {
        $value = $matches[1];
      }

      // check to see if we're dealing with a potential float val or thousands separators
      if(strpos($value, '.') !== false || strpos($value, ',') !== false || strpos($value, ' ') !== false) {
        // convert float values to rounded integers
        // also handles values with thousands separators
        $value = $this->wire()->sanitizer->float($value, array('blankValue' => ''));
        if($value !== '') $value = round($value);

      } else if(is_numeric($value)) {
        // let PHP decide how to convert it 
        $value = intval($value);

      } else {
        // default: replace non numeric characters
        $negative = substr(trim($value), 0, 1) == '-';
        $value = preg_replace('/[^\d]/', '', $value);
        $value = strlen($value) ? (int) $value : '';
        if($negative && is_int($value)) $value = $value * -1;
      }
    }

    return strlen("$value") ? (int) $value : '';
  }

  /**
   * Get Inputfield for this Fieldtype
   * 
   * @param Page $page
   * @param Field $field
   * @return Inputfield|InputfieldInteger
   * 
   */
  public function getInputfield(Page $page, Field $field) {
    /** @var InputfieldInteger $inputfield */
    $inputfield = $this->wire()->modules->get('InputfieldInteger'); 
    $inputfield->addClass($this->className());
    return $inputfield; 
  }

  /**
   * Get DB schema
   * 
   * @param Field $field
   * @return array
   * 
   */
  public function getDatabaseSchema(Field $field) {
    $schema = parent::getDatabaseSchema($field);
    $schema['data'] = 'int NOT NULL';
    return $schema;
  }

  /**
   * Get Inputfields to configure integer field
   * 
   * @param Field $field
   * @return InputfieldWrapper
   * 
   */
  public function ___getConfigInputfields(Field $field) {
    $inputfields = parent::___getConfigInputfields($field); 
    
    /** @var InputfieldRadios $f */
    $f = $this->wire()->modules->get('InputfieldRadios'); 
    $f->label = $this->_('Are blank and 0 equivalent?'); 
    $f->description = $this->_('This affects how ProcessWire matches pages during database find operations.') . ' ' . 
      $this->_('If 0 and blank are equivalent (the Yes option) then a search for **field=0** or **field=""** will produce the same results.') . ' ' . 
      $this->_('If they are not equivalent (the No option) then a search for **field=0** will only match fields containing the value 0, and **field=""** will only match fields with no value.') . ' ' . 
      $this->_('As another example, with the Yes option **field<1** would match both the value 0 and no value, and with the No option it would match only 0.'); 
    $f->attr('name', 'zeroNotEmpty'); 
    $f->addOption(0, $this->_('Yes - Blank and 0 are equivalent'));
    $f->addOption(1, $this->_('No - Blank and 0 have different meanings')); 
    $f->attr('value', (int) $field->get('zeroNotEmpty'));
    $inputfields->add($f); 
  
    /** @var InputfieldInteger $f */
    $f = $this->wire()->modules->get('InputfieldInteger');
    $f->attr('name', 'defaultValue');
    $f->label = $this->_('Default value'); 
    $f->description = $this->_('This value is assigned as the default for this field on newly created pages. It does not affect existing pages.');
    $f->collapsed = Inputfield::collapsedBlank;
    $f->attr('value', strlen($field->get('defaultValue')) ? (int) $field->get('defaultValue') : ''); 
    $inputfields->add($f);
    
    return $inputfields; 
  }

}