diff --git a/README.md b/README.md index 0d29742..b569b2b 100755 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Input field that provides built-in number validation and numeric sorting. The number field provides two additional datasource filtering methods: -### Range filtering +### 1) Range filtering You can easily filter by a numeric range on the number field on your datasource. Simply enter something like this: @@ -24,13 +24,18 @@ Just like any other datasource filter, you can make these values dynamic: {$url-lower-limit} to {$url-upper-limit} -This would let you pass through the upper and lower limit as url parameters. E.g. `/products/?lower-limit-10&upper-limit=20` +This would let you pass through the upper and lower limit as url parameters. E.g. `/products/?lower-limit=10&upper-limit=20` -### Less than or greater than +### 2) Less than or greater than You can also use standard greater than or less than symbols in the filter value or you can use words. e.g. > 20 greater than 20 -This will return all entries that have a value greater than 20. \ No newline at end of file +This will return all entries that have a value greater than 20. + + <= 20 + equal to or less than 20 + +This will return all entries that have a value of 20 or less. \ No newline at end of file diff --git a/extension.driver.php b/extension.driver.php index dc0288d..94187f7 100755 --- a/extension.driver.php +++ b/extension.driver.php @@ -3,18 +3,30 @@ Class extension_numberfield extends Extension { public function uninstall() { - Symphony::Database()->query("DROP TABLE `tbl_fields_number`"); + return Symphony::Database() + ->drop('tbl_fields_number') + ->ifExists() + ->execute() + ->success(); } public function install() { - return Symphony::Database()->query(" - CREATE TABLE `tbl_fields_number` ( - `id` int(11) unsigned NOT NULL auto_increment, - `field_id` int(11) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `field_id` (`field_id`) - ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci - "); + return Symphony::Database() + ->create('tbl_fields_number') + ->ifNotExists() + ->fields([ + 'id' => [ + 'type' => 'int(11)', + 'auto' => true, + ], + 'field_id' => 'int(11)', + ]) + ->keys([ + 'id' => 'primary', + 'field_id' => 'unique', + ]) + ->execute() + ->success(); } } diff --git a/extension.meta.xml b/extension.meta.xml index 3fe24bf..fc06b4f 100644 --- a/extension.meta.xml +++ b/extension.meta.xml @@ -1,6 +1,6 @@ - Number Field + Field: Number Dedicated number storage https://github.com/symphonycms/numberfield http://getsymphony.com/discuss/thread/144/ @@ -14,6 +14,13 @@ + + - Update for Symphony 4.x + - Code refactoring for Database and EQFA + + + - PHP7 Compatibility for Symphony 2.x.x + - Set 'entry_id' as unique diff --git a/fields/field.number.php b/fields/field.number.php index f3a991b..3f663b0 100755 --- a/fields/field.number.php +++ b/fields/field.number.php @@ -2,12 +2,16 @@ require_once FACE . '/interface.exportablefield.php'; require_once FACE . '/interface.importablefield.php'; + require_once EXTENSIONS . '/numberfield/lib/class.entryquerynumberadapter.php'; class FieldNumber extends Field implements ExportableField, ImportableField { public function __construct() { parent::__construct(); + $this->entryQueryFieldAdapter = new EntryQueryNumberAdapter($this); + $this->_name = __('Number'); $this->_required = true; + $this->set('required', 'no'); } @@ -36,16 +40,27 @@ public function canPrePopulate() { } public function createTable() { - return Symphony::Database()->query( - "CREATE TABLE IF NOT EXISTS `tbl_entries_data_" . $this->get('id') . "` ( - `id` int(11) unsigned NOT NULL auto_increment, - `entry_id` int(11) unsigned NOT NULL, - `value` double default NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `entry_id` (`entry_id`), - KEY `value` (`value`) - ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci" - ); + return Symphony::Database() + ->create('tbl_entries_data_' . $this->get('id')) + ->ifNotExists() + ->fields([ + 'id' => [ + 'type' => 'int(11)', + 'auto' => true, + ], + 'entry_id' => 'int(11)', + 'value' => [ + 'type' => 'double', + 'null' => true, + ], + ]) + ->keys([ + 'id' => 'primary', + 'entry_id' => 'unique', + 'value' => 'key', + ]) + ->execute() + ->success(); } /*------------------------------------------------------------------------- @@ -55,7 +70,7 @@ public function createTable() { public function displaySettingsPanel(XMLElement &$wrapper, $errors = null) { parent::displaySettingsPanel($wrapper, $errors); - $div = new XMLElement('div', NULL, array('class' => 'two columns')); + $div = new XMLElement('div', null, array('class' => 'two columns')); $this->appendRequiredCheckbox($div); $this->appendShowColumnCheckbox($div); $wrapper->appendChild($div); @@ -85,11 +100,11 @@ public function displayPublishPanel(XMLElement &$wrapper, $data = null, $flagWit $label->appendChild( Widget::Input( 'fields'.$fieldnamePrefix.'['.$this->get('element_name').']'.$fieldnamePostfix, - (strlen($value) != 0 ? $value : NULL) + (strlen($value) != 0 ? $value : null) ) ); - if($flagWithError != NULL) { + if($flagWithError != null) { $wrapper->appendChild(Widget::Error($label, $flagWithError)); } else { @@ -98,7 +113,7 @@ public function displayPublishPanel(XMLElement &$wrapper, $data = null, $flagWit } public function checkPostFieldData($data, &$message, $entry_id = null) { - $message = NULL; + $message = null; if($this->get('required') == 'yes' && strlen($data) == 0){ $message = __('ā€˜%s’ is a required field.', array($this->get('label'))); @@ -113,7 +128,7 @@ public function checkPostFieldData($data, &$message, $entry_id = null) { return self::__OK__; } - public function processRawFieldData($data, &$status, &$message=null, $simulate = false, $entry_id = null) { + public function processRawFieldData($data, &$status, &$message = null, $simulate = false, $entry_id = null) { $status = self::__OK__; if (strlen(trim($data)) == 0) return array(); @@ -229,8 +244,16 @@ public function fetchFilterableOperators() ), array( 'title' => 'between', - 'filter' => 'x to y', - 'help' => __('Find values between two values with, %s to %s', array( + 'filter' => 'between: ', + 'help' => __('Find values between two values with %s and %s', array( + '$x', + '$y' + )) + ), + array( + 'title' => 'to', + 'filter' => ' to ', + 'help' => __('Find values between two values with %s to %s', array( '$x', '$y' )) diff --git a/lib/class.entryquerynumberadapter.php b/lib/class.entryquerynumberadapter.php new file mode 100644 index 0000000..e4ea908 --- /dev/null +++ b/lib/class.entryquerynumberadapter.php @@ -0,0 +1,139 @@ +field->get('id')); + $filter = $this->field->cleanValue($filter); + $matches = []; + preg_match('/^between:? ?(-?(?:\d+(?:\.\d+)?|\.\d+)) and (-?(?:\d+(?:\.\d+)?|\.\d+))$/i', $filter, $matches); + + $conditions = []; + foreach ($columns as $key => $col) { + $conditions[] = [$this->formatColumn($col, $field_id) => ['between' => [(int)$matches[1], (int)$matches[2]]]]; + } + if (count($conditions) < 2) { + return $conditions; + } + return ['or' => $conditions]; + } + + public function isFilterTo($filter) + { + return preg_match('/^(-?(?:\d+(?:\.\d+)?|\.\d+)) to (-?(?:\d+(?:\.\d+)?|\.\d+))$/i', $filter); + } + + public function createFilterTo($filter, array $columns) + { + $field_id = General::intval($this->field->get('id')); + $filter = $this->field->cleanValue($filter); + $matches = []; + preg_match('/^(-?(?:\d+(?:\.\d+)?|\.\d+)) to (-?(?:\d+(?:\.\d+)?|\.\d+))$/i', $filter, $matches); + + $conditions = []; + foreach ($columns as $key => $col) { + $conditions[] = [$this->formatColumn($col, $field_id) => ['between' => [(int)$matches[1], (int)$matches[2]]]]; + } + if (count($conditions) < 2) { + return $conditions; + } + return ['or' => $conditions]; + } + + public function isFilterEqualLesserGreater($filter) + { + return preg_match('/^(equal to or )?(less|greater) than\s*(-?(?:\d+(?:\.\d+)?|\.\d+))$/i', $filter); + } + + public function createFilterEqualLesserGreater($filter, array $columns) + { + $field_id = General::intval($this->field->get('id')); + $filter = $this->field->cleanValue($filter); + $matches = []; + preg_match('/^(equal to or )?(less|greater) than\s*(-?(?:\d+(?:\.\d+)?|\.\d+))$/i', $filter, $matches); + + switch($matches[2]) { + case 'less': + $expression .= '<'; + break; + + case 'greater': + $expression .= '>'; + break; + } + + if($matches[1]){ + $expression .= '='; + } + + $conditions = []; + foreach ($columns as $key => $col) { + $conditions[] = [$this->formatColumn($col, $field_id) => [$expression => (int)$matches[3]]]; + } + if (count($conditions) < 2) { + return $conditions; + } + return ['or' => $conditions]; + } + + public function isFilterSymbol($filter) + { + return preg_match('/^(=?[<>]=?)\s*(-?(?:\d+(?:\.\d+)?|\.\d+))$/i', $filter); + } + + public function createFilterSymbol($filter, array $columns) + { + $field_id = General::intval($this->field->get('id')); + $filter = $this->field->cleanValue($filter); + $matches = []; + preg_match('/^(=?[<>]=?)\s*(-?(?:\d+(?:\.\d+)?|\.\d+))$/i', $filter, $matches); + + $conditions = []; + foreach ($columns as $key => $col) { + $conditions[] = [$this->formatColumn($col, $field_id) => [$matches[1] => (int)$matches[2]]]; + } + if (count($conditions) < 2) { + return $conditions; + } + return ['or' => $conditions]; + } + + /** + * @see EntryQueryFieldAdapter::filterSingle() + * + * @param EntryQuery $query + * @param string $filter + * @return array + */ + protected function filterSingle(EntryQuery $query, $filter) + { + General::ensureType([ + 'filter' => ['var' => $filter, 'type' => 'string'], + ]); + if ($this->isFilterBetween($filter)) { + return $this->createFilterBetween($filter, $this->getFilterColumns()); + } elseif ($this->isFilterTo($filter)) { + return $this->createFilterTo($filter, $this->getFilterColumns()); + } elseif ($this->isFilterEqualLesserGreater($filter)) { + return $this->createFilterEqualLesserGreater($filter, $this->getFilterColumns()); + } elseif ($this->isFilterSymbol($filter)) { + return $this->createFilterSymbol($filter, $this->getFilterColumns()); + } + return $this->createFilterEquality($filter, $this->getFilterColumns()); + } +}