From 3cb9c46e7dd9ca4866ab37f26860df0a9715a47b Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Fri, 1 Sep 2017 10:58:21 -0400 Subject: [PATCH] Some additional updates for repeater single mode --- .../FieldtypeRepeater.module | 87 +++++++++++-------- .../InputfieldRepeater.module | 11 ++- .../Fieldtype/FieldtypeRepeater/config.php | 17 +++- 3 files changed, 73 insertions(+), 42 deletions(-) diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module b/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module index 9d70a76f..8c83d9d4 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module +++ b/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module @@ -436,7 +436,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { if(!$p->id) continue; $p->addStatus(Page::statusSystemOverride); $p->removeStatus(Page::statusSystem); - $this->message("Deleted repeater page {$p->path}", Notice::debug); + $this->message("Deleted page {$p->path}", Notice::debug); $this->wire('pages')->delete($p, true); } } @@ -889,11 +889,11 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { foreach($changesByField as $fieldName => $count) { $updateCounts[] = "$fieldName ($count)"; } - $messages[] = "$numUpdated repeater page(s) updated – " . implode(', ', $updateCounts); + $messages[] = "$numUpdated '$fieldName' page(s) updated – " . implode(', ', $updateCounts); } - if($numAdded) $messages[] = "$numAdded new repeater page(s) added"; - if($numDeleted) $messages[] = "$numDeleted repeater page(s) DELETED"; + if($numAdded) $messages[] = "$numAdded new '$fieldName' page(s) added"; + if($numDeleted) $messages[] = "$numDeleted '$fieldName' page(s) DELETED"; foreach($messages as $message) { $pageArray->message("$field->name: $message"); @@ -966,7 +966,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { if($this->deletePageField === $field->get('parent_id')) return $parent; $parent->save(); - $this->message("Created Repeater Page Parent: " . $parent->path, Notice::debug); + $this->message("Created '$field' page parent: " . $parent->path, Notice::debug); return $parent; } @@ -1005,7 +1005,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { $parent->title = $field->name; $parent->addStatus(Page::statusSystem); $parent->save(); - $this->message('Created Repeater Parent: ' . $parent->path, Notice::debug); + $this->message("Created '$field' parent: $parent->path", Notice::debug); } if($parent->id) { @@ -1015,7 +1015,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { $field->save(); } } else { - throw new WireException("Unable to create repeater parent {$repeatersRootPage->path}$parentName"); + throw new WireException("Unable to create parent {$repeatersRootPage->path}$parentName"); } return $parent; @@ -1061,13 +1061,13 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { $this->populateRepeaterTemplateSettings($template); $template->save(); - if(!$template->id) throw new WireException("Unable to create repeater template: $templateName"); + if(!$template->id) throw new WireException("Unable to create template: $templateName"); // save the template_id setting to the field $field->set('template_id', $template->id); $field->save(); - $this->message("Created Repeater Template $template", Notice::debug); + $this->message("Created '$field' template: $template", Notice::debug); return $template; } @@ -1218,12 +1218,18 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { * @param string $operator * @param string $value * @return DatabaseQuerySelect + * @throws WireException * */ public function getMatchQuery($query, $table, $subfield, $operator, $value) { $field = $query->field; - $singlePageMode = $this->className() == 'FieldtypeFieldsetPage'; + $schema = $this->getDatabaseSchema($field); + $singlePageMode = !isset($schema['count']); + + if($singlePageMode && in_array($subfield, array('count', 'parent', 'parent_id'))) { + throw new WireException("The count subfield option is not for field '$field'"); + } if($subfield == 'count') { $value = (int) $value; @@ -1236,7 +1242,8 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { foreach($field->getTemplates() as $template) $templateIDs[] = (int) $template->id; if(count($templateIDs)) { $templateIDs = implode(',', $templateIDs); - $sql = "($table.count{$operator}$value OR " . + $sql = + "($table.count{$operator}$value OR " . "($table.count IS NULL AND pages.templates_id IN($templateIDs)))"; $query->where($sql); } else { @@ -1269,11 +1276,12 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { } if(in_array($operator, array('*=', '~=', '^=', '$=', '%='))) { + if($singlePageMode) throw new WireException("Operator $operator not supported for $field.data"); /** @var DatabaseQuerySelectFulltext $ft */ $ft = $this->wire(new DatabaseQuerySelectFulltext($query)); $ft->match($table, $subfield, $operator, $value); - } else if(empty($value)) { + } else if(empty($value) && !$singlePageMode) { // match where count is 0 $query->where("$table.count{$operator}0"); @@ -1283,29 +1291,36 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { } else { $f = $this->wire('fields')->get($subfield); - if($f) { - // match fields from the repeater template - // perform a separate find() operation for the subfield - $pageFinder = $this->wire(new PageFinder()); - $value = $this->wire('sanitizer')->selectorValue($value); - $templateID = $field->get('template_id'); - $includeMode = $singlePageMode ? "include=all" : "check_access=0"; - $selectors = $this->wire(new Selectors("templates_id=$templateID, $includeMode, $f->name$operator$value")); - $matches = $pageFinder->find($selectors); + if(!$f) return $query; // unknown subfield + + // match fields from the repeater template + // perform a separate find() operation for the subfield + $pageFinder = $this->wire(new PageFinder()); + $value = $this->wire('sanitizer')->selectorValue($value); + $templateID = $field->get('template_id'); + $includeMode = $singlePageMode ? "include=all" : "check_access=0"; + $selectors = $this->wire(new Selectors("templates_id=$templateID, $includeMode, $f->name$operator$value")); + $matches = $pageFinder->find($selectors); - // use the IDs found from the separate find() as our getMatchQuery - if(count($matches)) { - $ids = array(); - $matchKey = $singlePageMode ? 'id' : 'parent_id'; + // use the IDs found from the separate find() as our getMatchQuery + if(count($matches)) { + $ids = array(); + if($singlePageMode) { foreach($matches as $match) { - $ids[$match[$matchKey]] = $match[$matchKey]; + $id = (int) $match['id']; + $ids[$id] = $id; + } + $query->where("$table.data IN(" . implode(',', $ids) . ")"); + } else { + foreach($matches as $match) { + $parentID = (int) $match['parent_id']; + $ids[$parentID] = $parentID; } $query->where("$table.parent_id IN(" . implode(',', $ids) . ")"); - } else { - $query->where("1>2"); // force a non-match } + } else { + $query->where("1>2"); // force a non-match } - } return $query; @@ -1392,7 +1407,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { $value->remove($p); $p = $this->wire('pages')->clone($p, $repeaterParent, false, $saveOptions); $value->add($p); - $this->message("Cloned to repeater {$p->path} from field $field", Notice::debug); + $this->message("Cloned to {$p->path} from field $field", Notice::debug); continue; } @@ -1427,7 +1442,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { if($isHidden && $isUnpublished) continue; // this is a 'ready' page, we can ignore $changes = implode(', ', $p->getChanges()); - $this->message("Saved Repeater {$p->path} " . ($changes ? "($changes)" : ''), Notice::debug); + $this->message("Saved '$field' page: {$p->path} " . ($changes ? "($changes)" : ''), Notice::debug); if($isUnpublished && $isOn && $isProcessed && !$hasErrors) { // publish requested and allowed @@ -1435,7 +1450,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { } } else { - $this->message("Added New Repeater", Notice::debug); + $this->message("Added new '$field' page", Notice::debug); } // save the repeater page @@ -1492,7 +1507,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { // resursively delete the field parent and everything below it $this->wire('pages')->delete($parent, true); - $this->message("Deleted Repeater Parent $parentPath", Notice::debug); + $this->message("Deleted '$field' parent: $parentPath", Notice::debug); } // delete the template used by this field @@ -1512,7 +1527,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { $fieldgroup = $this->wire('fieldgroups')->get($templateName); if($fieldgroup) $this->wire('fieldgroups')->delete($fieldgroup); - $this->message("Deleted Repeater Template $templateName", Notice::debug); + $this->message("Deleted '$field' template: $templateName", Notice::debug); } return parent::___deleteField($field); @@ -1530,12 +1545,12 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { $result = parent::___deletePageField($page, $field); $this->deletePageField = $field->get('parent_id'); - $fieldParent = $this->wire('pages')->get($field->get('parent_id')); + $fieldParent = $this->wire('pages')->get((int) $field->get('parent_id')); // confirm that this field parent page is still part of the pages we manage if($fieldParent->parent_id == $this->repeatersRootPageID) { // locate the repeater page parent - $parent = $fieldParent->child('name=' . self::repeaterPageNamePrefix . $page->id); + $parent = $fieldParent->child('name=' . self::repeaterPageNamePrefix . $page->id . ', include=all'); if($parent->id) { // remove system status from repeater page parent $parent->addStatus(Page::statusSystemOverride); diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module b/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module index edfc4a0a..6d11fcaa 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module +++ b/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module @@ -12,6 +12,7 @@ * @property int $repeaterMinItems * @property int $repeaterDepth * @property bool $accordionMode + * @property bool $singleMode * * @method string renderRepeaterLabel($label, $cnt, Page $page) * @@ -92,6 +93,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { $this->set('repeaterMinItems', 0); $this->set('repeaterDepth', 0); $this->set('accordionMode', false); + $this->set('singleMode', false); } /** @@ -283,7 +285,6 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { } $minItems = $this->repeaterMinItems; - $maxItems = $this->repeaterMaxItems; // if there are a minimum required number of items, set them up now if(!$itemID && $minItems > 0) { @@ -300,7 +301,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { $numVisible = 0; $numOpen = 0; $isPost = $this->wire('input')->requestMethod('POST'); - $isSingle = $minItems == 1 && $maxItems == 1; + $isSingle = $this->singleMode; // create field for each repeater iteration foreach($value as $key => $page) { @@ -434,6 +435,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { if(!$isOn) $wrap->addClass('InputfieldRepeaterOff'); } + $wrap->add($inputfields); $wrap->prepend($delete); $wrap->prepend($sort); if($depth) $wrap->prepend($depth); @@ -603,7 +605,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { if($min > 0) { $this->addClass('InputfieldRepeaterMin', 'wrapClass'); } - if($min === 1 && $max === 1) { + if($this->singleMode) { $this->addClass('InputfieldRepeaterSingle', 'wrapClass'); } else if($this->repeaterDepth > 0) { @@ -645,6 +647,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { */ protected function renderFooter($noAjaxAdd) { // a hidden checkbox with link that we use to identify when items have been added + if($this->singleMode) return ''; $out = "

" . "" . // for noAjaxAdd @@ -727,7 +730,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { */ public function ___processInput(WireInputData $input) { - $isSingle = $this->repeaterMinItems == 1 && $this->repeaterMaxItems == 1; + $isSingle = $this->singleMode; /** @var PageArray $value */ $value = $this->attr('value'); diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/config.php b/wire/modules/Fieldtype/FieldtypeRepeater/config.php index dace2cac..b97ea896 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/config.php +++ b/wire/modules/Fieldtype/FieldtypeRepeater/config.php @@ -25,6 +25,15 @@ class FieldtypeRepeaterConfigHelper extends Wire { $this->field = $field; } + /** + * @return bool + * + */ + protected function isSingleMode() { + $schema = $this->field->type->getDatabaseSchema($this->field); + $singleMode = !isset($schema['count']); + return $singleMode; + } /** * Return configuration fields definable for each FieldtypePage * @@ -114,6 +123,10 @@ class FieldtypeRepeaterConfigHelper extends Wire { if($this->wire('config')->debug) $select->notes = "This repeater uses template '$template' and parent '{$parent->path}'"; $inputfields->add($select); + + if($this->isSingleMode()) return $inputfields; + + // all of the following fields are not applicable to single-page mode (i.e. FieldtypeFieldsetPage) // ------------------------------------------------- @@ -307,14 +320,14 @@ class FieldtypeRepeaterConfigHelper extends Wire { foreach($ids as $id) { if(!$f = $this->wire('fields')->get((int) $id)) continue; - if(!$fieldgroup->has($f)) $this->message(sprintf($this->_('Added Field "%1$s" to Repeater "%2$s"'), $f, $field)); + if(!$fieldgroup->has($f)) $this->message(sprintf($this->_('Added Field "%1$s" to "%2$s"'), $f, $field)); $fieldgroup->add($f); } foreach($fieldgroup as $f) { if(in_array($f->id, $ids)) continue; $fieldgroup->remove($f); - $this->message(sprintf($this->_('Removed Field "%1$s" from Repeater "%2$s"'), $f, $field)); + $this->message(sprintf($this->_('Removed Field "%1$s" from "%2$s"'), $f, $field)); } $fieldgroup->save();