From 703fa29c85ff9a0a2439ed97faa7f8b023d48f92 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Fri, 11 Jan 2019 08:52:12 -0500 Subject: [PATCH] Improvements to searchability of FieldtypeOptions fields, now enabling it to match either value or title. --- .../FieldtypeOptions/FieldtypeOptions.module | 33 ++++++++++++++----- .../SelectableOptionManager.php | 32 +++++++++++------- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/wire/modules/Fieldtype/FieldtypeOptions/FieldtypeOptions.module b/wire/modules/Fieldtype/FieldtypeOptions/FieldtypeOptions.module index a6a3fe5e..617acbeb 100644 --- a/wire/modules/Fieldtype/FieldtypeOptions/FieldtypeOptions.module +++ b/wire/modules/Fieldtype/FieldtypeOptions/FieldtypeOptions.module @@ -100,11 +100,11 @@ class FieldtypeOptions extends FieldtypeMulti implements Module { $inputfield->addOption((int) $option->id, $option->getTitle()); } - if($field->initValue) { + if($field->get('initValue')) { $value = $page->getUnformatted($field->name); if($field->required && !$field->requiredIf) { if(empty($value) || !wireCount($value)) { - $page->set($field->name, $field->initValue); + $page->set($field->name, $field->get('initValue')); } } else if($this->wire('process') != 'ProcessField' && !wireCount($value)) { $this->warning( @@ -230,10 +230,10 @@ class FieldtypeOptions extends FieldtypeMulti implements Module { $sleepValue = array(); if(empty($value) || !count($value)) { // value is empty - if($field->required && $field->initValue) { + if($field->required && $field->get('initValue')) { // value is required, and an initial value is supplied // so populate the initial value - $initValue = $field->initValue; + $initValue = $field->get('initValue'); if(!is_array($initValue)) $initValue = array($initValue); foreach($initValue as $v) { $sleepValue[] = (int) $v; @@ -306,19 +306,34 @@ class FieldtypeOptions extends FieldtypeMulti implements Module { if($subfield == 'data' && (ctype_digit("$value") || empty($value))) { // this is fine (presumed to be an option_id) } else { - // some other subfield + // some other subfield, which needs to be mapped to either value or title + $options = array(); - if(!$subfield || !SelectableOption::isProperty($subfield)) { - // if empty subfield or not a subfield we recognize, just assume title - $subfield = 'title'; + if($subfield === 'data' && ($operator === '=' || $operator === '!=')) { + // subfield not specified: matching some string value, is it a value or a title? (allow for either) + $options = $this->manager->getOptions($query->field, array( + 'value' => $value, + 'title' => $value, + 'or' => true + )); } - $options = $this->manager->findOptionsByProperty($query->field, $subfield, $operator, $value); + + if(!count($options)) { + if(!$subfield || !SelectableOption::isProperty($subfield)) { + // if empty subfield or not a subfield we recognize, just assume title + $subfield = 'title'; + } + $options = $this->manager->findOptionsByProperty($query->field, $subfield, $operator, $value); + } + $option = $options->first(); + if($operator != '=' && $operator != '!=') { // for fulltext operations... // since we are now just matching IDs of already found options $operator = '='; } + $subfield = 'data'; $value = $option ? $option->id : null; } diff --git a/wire/modules/Fieldtype/FieldtypeOptions/SelectableOptionManager.php b/wire/modules/Fieldtype/FieldtypeOptions/SelectableOptionManager.php index d52434c1..52d1db43 100644 --- a/wire/modules/Fieldtype/FieldtypeOptions/SelectableOptionManager.php +++ b/wire/modules/Fieldtype/FieldtypeOptions/SelectableOptionManager.php @@ -97,41 +97,51 @@ class SelectableOptionManager extends Wire { 'id' => array(), 'title' => array(), 'value' => array(), + 'or' => false, // change conditions from AND to OR? ); $sortKey = true; $sorted = array(); $filters = array_merge($defaults, $filters); + $wheres = array(); // make sure that all filters are arrays foreach($defaults as $key => $unused) { if(!is_array($filters[$key])) $filters[$key] = array($filters[$key]); } - $sql = 'SELECT * FROM ' . self::optionsTable . ' WHERE fields_id=:fields_id '; - if(count($filters['id'])) { - $sql .= 'AND option_id IN('; + $s = 'option_id IN('; foreach($filters['id'] as $id) { $id = (int) $id; - $sql .= "$id,"; + $s .= "$id,"; $sorted[$id] = ''; // placeholder } - $sql = rtrim($sql, ',') . ')'; + $s = rtrim($s, ',') . ')'; $sortKey = 'filters-id'; + $wheres[] = $s; } - + foreach(array('title', 'value') as $property) { if(!count($filters[$property])) continue; - $sql .= "AND `$property` IN("; + $s = "`$property` IN("; foreach($filters[$property] as $val) { - $sql .= $this->wire('database')->quote($val) . ','; + $s .= $this->wire('database')->quote($val) . ','; $sorted[$val] = ''; // placeholder } - $sql = rtrim($sql, ',') . ')'; + $s = rtrim($s, ',') . ')'; $sortKey = "filters-$property"; + $wheres[] = $s; } - + + $sql = 'SELECT * FROM ' . self::optionsTable . ' WHERE fields_id=:fields_id '; + if(count($wheres) > 1) { + $andOr = $filters['or'] ? ' OR ' : ' AND '; + $sql .= 'AND (' . implode($andOr, $wheres) . ') '; + } else if(count($wheres) === 1) { + $sql .= 'AND ' . reset($wheres); + } + if($sortKey === true) $sql .= 'ORDER BY sort ASC'; $query = $this->wire('database')->prepare($sql); @@ -173,7 +183,7 @@ class SelectableOptionManager extends Wire { * Perform a partial match on title of options * * @param Field $field - * @param string $property Either 'title' or 'value' + * @param string $property Either 'title' or 'value'. May also be blank (to imply 'either') if operator is '=' or '!=' * @param string $operator * @param string $value Value to find * @return SelectableOptionArray