From bbddcf1ca0bfd0783461398f3eb7e7863e783282 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Tue, 12 Nov 2019 11:06:02 -0500 Subject: [PATCH] Add support for Fieldtypes to specify that a Field should use a custom class that extends the Field class, rather than always using the "Field" class. --- wire/core/Field.php | 2 +- wire/core/Fields.php | 39 +++++++++++++++++++ wire/core/Fieldtype.php | 17 +++++++- wire/core/Fieldtypes.php | 12 +++--- wire/core/WireSaveableItems.php | 39 ++++++++++++++----- .../FieldtypeRepeater.module | 15 +++++++ .../FieldtypeRepeater/RepeaterField.php | 5 +++ 7 files changed, 112 insertions(+), 17 deletions(-) create mode 100644 wire/modules/Fieldtype/FieldtypeRepeater/RepeaterField.php diff --git a/wire/core/Field.php b/wire/core/Field.php index ad3fffc1..eac9e54d 100644 --- a/wire/core/Field.php +++ b/wire/core/Field.php @@ -616,7 +616,7 @@ class Field extends WireData implements Saveable, Exportable { } else if(is_string($type)) { $typeStr = $type; - $fieldtypes = $this->wire('fieldtypes'); + $fieldtypes = $this->wire('fieldtypes'); /** @var Fieldtypes $fieldtypes */ if(!$type = $fieldtypes->get($type)) { $this->error("Fieldtype '$typeStr' does not exist"); return $this; diff --git a/wire/core/Fields.php b/wire/core/Fields.php index a18f320d..572757c0 100644 --- a/wire/core/Fields.php +++ b/wire/core/Fields.php @@ -155,6 +155,45 @@ class Fields extends WireSaveableItems { return $this->wire(new Field()); } + /** + * Make an item and populate with given data + * + * @param array $a Associative array of data to populate + * @return Saveable|Wire + * @throws WireException + * @since 3.0.147 + * + */ + public function makeItem(array $a = array()) { + + if(empty($a['type'])) return parent::makeItem($a); + + /** @var Fieldtypes $fieldtypes */ + $fieldtypes = $this->wire('fieldtypes'); + if(!$fieldtypes) return parent::makeItem($a); + + /** @var Fieldtype $fieldtype */ + $fieldtype = $fieldtypes->get($a['type']); + if(!$fieldtype) return parent::makeItem($a); + + $class = $fieldtype->getFieldClass($a); + if(empty($class) || $class === 'Field') return parent::makeItem($a); + + if(strpos($class, "\\") === false) $class = wireClassName($class, true); + if(!class_exists($class)) return parent::makeItem($a); + + $field = new $class(); + $this->wire($field); + + foreach($a as $key => $value) { + $field->$key = $value; + } + + $field->resetTrackChanges(true); + + return $field; + } + /** * Per WireSaveableItems interface, return all available Field instances * diff --git a/wire/core/Fieldtype.php b/wire/core/Fieldtype.php index 7660792f..05006451 100644 --- a/wire/core/Fieldtype.php +++ b/wire/core/Fieldtype.php @@ -605,7 +605,7 @@ abstract class Fieldtype extends WireData implements Module { * * #pw-internal * - * @param array Field $field + * @param Field $field * @return array * */ @@ -813,6 +813,21 @@ abstract class Fieldtype extends WireData implements Module { return $schema; } + /** + * Get class name to use Field objects of this type (must be class that extends Field class) + * + * Return blank if default class (Field) should be used. + * + * @param array $a Field data from DB (if needed) + * @return string Return class name or blank to use default Field class + * @since 3.0.147 + * + */ + public function getFieldClass(array $a = array()) { + if($a) {} // ignore + return ''; + } + /** * Returns verbose array of database schema information * diff --git a/wire/core/Fieldtypes.php b/wire/core/Fieldtypes.php index 2a560f30..ff100edb 100644 --- a/wire/core/Fieldtypes.php +++ b/wire/core/Fieldtypes.php @@ -49,10 +49,11 @@ class Fieldtypes extends WireArray { if($this->preloaded) return; $debug = $this->isAPI && $this->wire('config')->debug; if($debug) Debug::timer('Fieldtypes.preload'); - foreach($this->data as $key => $module) { + $modules = $this->wire('modules'); /** @var Modules $modules */ + foreach($this->data as $moduleName => $module) { if($module instanceof ModulePlaceholder) { - $fieldtype = $this->wire('modules')->get($module->className()); - $this->data[$key] = $fieldtype; + $fieldtype = $modules->getModule($moduleName); + $this->data[$moduleName] = $fieldtype; } } if($debug) Debug::saveTimer('Fieldtypes.preload'); @@ -130,11 +131,12 @@ class Fieldtypes extends WireArray { if(strpos($key, 'Fieldtype') !== 0) $key = "Fieldtype" . ucfirst($key); if(!$fieldtype = parent::get($key)) { - $fieldtype = $this->wire('modules')->get($key); + $fieldtype = $this->wire('modules')->getModule($key); + if($fieldtype) $this->set($key, $fieldtype); } if($fieldtype instanceof ModulePlaceholder) { - $fieldtype = $this->wire('modules')->get($fieldtype->className()); + $fieldtype = $this->wire('modules')->getModule($fieldtype->className()); if($fieldtype) $this->set($key, $fieldtype); } diff --git a/wire/core/WireSaveableItems.php b/wire/core/WireSaveableItems.php index 323a43ec..3f5745fa 100644 --- a/wire/core/WireSaveableItems.php +++ b/wire/core/WireSaveableItems.php @@ -42,6 +42,25 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate { */ abstract public function makeBlankItem(); + /** + * Make an item and populate with given data + * + * @param array $a Associative array of data to populate + * @return Saveable|Wire + * @throws WireException + * @since 3.0.147 + * + */ + public function makeItem(array $a = array()) { + $item = $this->makeBlankItem(); + $this->wire($item); + foreach($a as $key => $value) { + $item->$key = $value; + } + $item->resetTrackChanges(true); + return $item; + } + /** * Return the name of the table that this DAO stores item records in * @@ -163,6 +182,7 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate { */ protected function ___load(WireArray $items, $selectors = null) { + /** @var WireDatabasePDO $database */ $database = $this->wire('database'); $sql = $this->getLoadQuery($selectors)->getQuery(); @@ -170,21 +190,20 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate { $query->execute(); while($row = $query->fetch(\PDO::FETCH_ASSOC)) { - $item = $this->makeBlankItem(); - $this->wire($item); - foreach($row as $field => $value) { - if($field == 'data') { - if($value) $value = $this->decodeData($value); - else continue; + if(isset($row['data'])) { + if($row['data']) { + $row['data'] = $this->decodeData($row['data']); + } else { + unset($row['data']); } - $item->$field = $value; } - $item->setTrackChanges(true); - $items->add($item); + $item = $this->makeItem($row); + if($item) $items->add($item); } + $query->closeCursor(); - $items->setTrackChanges(true); + return $items; } diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module b/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module index ee246f0e..337cabbe 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module +++ b/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module @@ -199,6 +199,21 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { $initFields[$className] = 1; } */ + + /** + * Get class name to use Field objects of this type (must be class that extends Field class) + * + * Return blank if default class (Field) should be used. + * + * @param array $a Field data from DB (if needed) + * @return string Return class name or blank to use default Field class + * @since 3.0.147 + * + */ + public function getFieldClass(array $a = array()) { + require_once(dirname(__FILE__) . '/RepeaterField.php'); + return 'RepeaterField'; + } /** * Get the class used for repeater Page objects diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/RepeaterField.php b/wire/modules/Fieldtype/FieldtypeRepeater/RepeaterField.php new file mode 100644 index 00000000..2bc448c0 --- /dev/null +++ b/wire/modules/Fieldtype/FieldtypeRepeater/RepeaterField.php @@ -0,0 +1,5 @@ +