From e2c53b4853566bd96ef52a14ba853a6f12014b2a Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Fri, 15 Nov 2024 11:38:34 +0100 Subject: [PATCH 01/21] feat: add setPageName() method --- RockMigrations.module.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index 98eabc5..ec216ba 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -4479,6 +4479,32 @@ public function setOutputLevel($level) $this->outputLevel = $level; } + /** + * Set name of a page + * Can also set name of pages having the system flag + */ + public function setPageName( + Page|string|int $page, + string $name, + $removeSystemFlag = false, + ): void { + $page = $this->getPage($page); + if (!$removeSystemFlag) { + $page->setAndSave('name', $name); + return; + } + + // set new name and restore old status + $oldStatus = $page->status; + $page->addStatus(Page::statusSystemOverride); + $page->status = 1; + $page->name = $name; + $page->status = $oldStatus; + + // save changes + $page->save(); + } + /** * Set page name from field of template * From 771fd471782fb94a2217c3100e540044a7842ea9 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Fri, 15 Nov 2024 11:38:34 +0100 Subject: [PATCH 02/21] feat: add copy/moveRepeaterItems() --- RockMigrations.module.php | 90 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index ec216ba..867c5da 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -729,6 +729,71 @@ public function columnWidth(InputfieldWrapper $form, array $fields) } } + /** + * Thx Adrian + * https://github.com/adrianbj/ProcessAdminActions/blob/master/actions/CopyRepeaterItemsToOtherPage.action.php + * + * Usage of newFieldnames: + * [ + * 'oldFieldName' => 'newFieldName', + * ] + */ + public function copyRepeaterItems( + Page|string|int $sourcePage, + Field|string|int $sourceField, + Field|string|int $targetField, + Page|string|int $targetPage = null, + bool $resetTarget = false, + array $newFieldnames = [], + ): void { + $sourcePage = $this->getPage($sourcePage); + $sourcePage->of(false); + $targetPage = $targetPage ? $this->getPage($targetPage) : $sourcePage; + $targetPage->of(false); + $sourceField = $this->getField($sourceField); + $targetField = $this->getField($targetField); + $sourceItems = $sourcePage->get($sourceField->name); + $targetItems = $targetPage->get($targetField->name); + + // reset target before copy? + if ($resetTarget) { + $targetItems->removeAll(); + $targetPage->save($targetField->name); + } + + // copy items + foreach ($sourceItems as $item) { + // create new item + $clone = $targetItems->getNew(); + $clone->save(); + + // populate all fields + foreach ($sourceField->fields as $fieldname) { + $targetName = $fieldname; + + // move content to the same field or to another one? + if (in_array($fieldname, array_keys($newFieldnames))) { + $targetName = $newFieldnames[$fieldname]; + } + + // set new value + $clone->set($targetName, $item->getUnformatted($fieldname)); + } + + // copy files? + if (PagefilesManager::hasFiles($item)) { + $clone->filesManager->init($clone); + $item->filesManager->copyFiles($clone->filesManager->path()); + } + + // save changes + $clone->save(); + } + + // save page + $targetPage->setAndSave($targetField->name, $targetItems); + } + private function createConstantTraits(): void { // only do this if debug mode is enabled @@ -3467,6 +3532,31 @@ public function movePageBefore($page, $reference) $this->wire->pages->sort($page, $ref->sort); } + public function moveRepeaterItems( + Page|string|int $sourcePage, + Field|string|int $sourceField, + Field|string|int $targetField, + Page|string|int $targetPage = null, + bool $resetTarget = false, + array $newFieldnames = [], + ): void { + $this->copyRepeaterItems( + $sourcePage, + $sourceField, + $targetField, + $targetPage, + $resetTarget, + $newFieldnames, + ); + + // reset source field + $sourcePage = $this->getPage($sourcePage); + $sourceField = $this->getField($sourceField); + $oldItems = $sourcePage->getUnformatted($sourceField->name); + $oldItems->removeAll(); + $sourcePage->setAndSave($sourceField->name, $oldItems); + } + /** * Set no migrate flag */ From fa76493353dc7a9c15fabc48f3f43ab72eed2312 Mon Sep 17 00:00:00 2001 From: gebeer Date: Fri, 15 Nov 2024 11:38:34 +0100 Subject: [PATCH 03/21] fix: error when no matrixItems passed in $options --- RockMigrations.module.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index 867c5da..9042ce2 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -6268,7 +6268,7 @@ private function macros(): WireArray */ public function createRepeaterMatrixField(string $name, array $options, bool $wipe = false) { - $items = array_key_exists('matrixItems', $options) ? $options['matrixItems'] : null; + $items = array_key_exists('matrixItems', $options) ? $options['matrixItems'] : []; if ($items) unset($options['matrixItems']); // create field $field = $this->createField($name, 'FieldtypeRepeaterMatrix', $options); From ad59425831c74e32034035afa9d2276f398cc2be Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Fri, 15 Nov 2024 15:47:02 +0100 Subject: [PATCH 04/21] fix: issue with iterating fields of repeater --- RockMigrations.module.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index 9042ce2..fb3cbb8 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -768,7 +768,8 @@ public function copyRepeaterItems( $clone->save(); // populate all fields - foreach ($sourceField->fields as $fieldname) { + foreach ($item->fields as $field) { + $fieldname = $field->name; $targetName = $fieldname; // move content to the same field or to another one? From 7195a0fa106ccbc0590c420cee2e5208db33fa30 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Fri, 15 Nov 2024 19:19:22 +0100 Subject: [PATCH 05/21] fix: do not migrate dotfiles --- RockMigrations.module.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index fb3cbb8..539ff8e 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -835,6 +835,7 @@ private function createConstantTraits(): void foreach ($files as $file) { $type = $this->getConfigFileType($file, true); $shortName = $this->getConfigFileName($file, true); + if (str_starts_with($shortName, '.')) continue; $longName = $this->getConfigFileName($file); $content .= " const {$type}_$shortName = '$longName';\n"; } @@ -4081,6 +4082,13 @@ private function runConfigFile(string $file, $firstRun = false): void $url = $this->toUrl($file); $this->log($url); + // skip dotfiles + $shortName = $this->getConfigFileName($file, true); + if (str_starts_with($shortName, '.')) { + $this->log("Skipping dotfile $shortName"); + return; + } + $config = $this->getConfigFileArray($file); $name = $this->getConfigFileName($file); $type = $this->getConfigFileType($file); @@ -4102,8 +4110,12 @@ private function runConfigFile(string $file, $firstRun = false): void $this->log(" Tag: $tag"); switch ($type) { case 'fields': - $this->createField($name, $config); - $this->setFieldData($name, ['tags' => $tag]); + // get fieldname either from config "name" property or from filename + $fieldname = array_key_exists('name', $config) + ? $config['name'] + : $name; + $this->createField($fieldname, $config); + $this->setFieldData($fieldname, ['tags' => $tag]); break; case 'templates': $this->createTemplate($name, $config); From a89fa4d633316598e9e26bd722fee6f4ceb1ae17 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Fri, 15 Nov 2024 20:23:31 +0100 Subject: [PATCH 06/21] fix: allow priorities > 1000 --- RockMigrations.module.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index 539ff8e..e5d80ed 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -5593,8 +5593,6 @@ public function watch($what, $migrate = true, $options = []) { if (!$this->watchEnabled()) return; - if ($migrate > 1000) throw new WireException("Migrate priority must not be higher than 1000: $migrate"); - // if what is an array we watch all files in the array if (is_array($what)) { foreach ($what as $file) $this->watch($file, $migrate, $options); From 47301fa724fde85aae04513d24fc9da28e223271 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Fri, 15 Nov 2024 21:19:30 +0100 Subject: [PATCH 07/21] fix: run watchlist migrations in correct order --- RockMigrations.module.php | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index e5d80ed..1a41577 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -3402,29 +3402,29 @@ private function migrateWatchfiles($force = false) $this->log($list); } - // first we migrate all config files - // in the first step we only create all fields/templates/etc - // in the second step we migrate the data - $this->log("### Migrate config files (priority #1000) ###"); - $configFiles = $list['#1000'] ?? []; - $this->indent(2); - $this->log("--- create PHP constant traits ---"); - $this->createConstantTraits(); - $this->log("--- create objects ---"); - foreach ($configFiles as $file) $this->runConfigFile($file, true); - $this->log("--- migrate data ---"); - foreach ($configFiles as $file) $this->runConfigFile($file); - $this->indent(0); - // now we migrate all other files foreach ($list as $prio => $items) { - // dont run config files again - if ($prio === '#1000') continue; - if ($this->isCLI()) $this->log(""); - $this->indent = 0; $this->log("### Migrate items with priority $prio ###"); + // prio 1000 is reserved for config migrations + // in the first step we only create all fields/templates/etc + // in the second step we migrate the data + if ($prio === '#1000') { + $this->log("### Config File Migrations ###"); + $this->indent(2); + $this->log("--- create PHP constant traits ---"); + $this->createConstantTraits(); + $this->log("--- create objects ---"); + foreach ($items as $file) $this->runConfigFile($file, true); + $this->log("--- migrate data ---"); + foreach ($items as $file) $this->runConfigFile($file); + $this->indent(-2); + + // skip everything below + continue; + } + foreach ($items as $path) { $file = $this->watchlist->get("path=$path"); if ($file) $this->migrateWatchfile($file); From ccce68a2d5f87ae7ad37007fa003c0bdaceba2f6 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Sat, 16 Nov 2024 09:31:06 +0100 Subject: [PATCH 08/21] fix: prevent errors when page is nullpage in deletepage --- RockMigrations.module.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index 1a41577..fa01009 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -1386,6 +1386,9 @@ public function deletePage($page, $quiet = false) { if (!$page = $this->getPage($page, $quiet)) return; + // nullpage? exit early + if (!$page->id) return; + // temporarily disable filesOnDemand feature // this prevents PW from downloading files that are deleted from a local dev // system but only exist on the live system From ffc3f0fa16863c341015e74df301c13932ee8407 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Sat, 16 Nov 2024 09:51:04 +0100 Subject: [PATCH 09/21] break lines --- RockMigrations.module.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index fa01009..63581d3 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -2144,8 +2144,10 @@ public function getPermission($data, $quiet = false) /** * Get repeater template for given field */ - public function getRepeaterTemplate(Field|string|int $field, $quiet = false): Template|false - { + public function getRepeaterTemplate( + Field|string|int $field, + $quiet = false, + ): Template|false { $field = $this->getField($field, $quiet); if ($field) return $field->type->getRepeaterTemplate($field); return false; From 3993f2f31daa8d4d43e1df22f47751a138e3e58b Mon Sep 17 00:00:00 2001 From: gebeer Date: Sat, 16 Nov 2024 16:23:27 +0700 Subject: [PATCH 10/21] fix: custom fieldtype returns wrong class after creation --- RockMigrations.module.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index 63581d3..9af174e 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -888,7 +888,27 @@ public function createField($name, $type = 'text', $options = []) // create the new field $_name = $this->wire->sanitizer->fieldName($name); if ($_name !== $name) throw new WireException("Invalid fieldname ($name)!"); - $field = $this->wire(new Field()); + + // field does not exist + if (!$field) { + // get type + $type = $this->getFieldtype($type); + if (!$type) return; // logging above + + // create the new field + $_name = $this->wire->sanitizer->fieldName($name); + if ($_name !== $name) throw new WireException("Invalid fieldname ($name)!"); + + // get fieldClass as string if implemented for that FieldType + /** @var string $fieldClass */ + $fieldClass = $type->getFieldClass(); + $fieldClass = "ProcessWire\\$fieldClass"; + if(class_exists($fieldClass)) { + // use the specific class to create new field if it exists + $field = $this->wire(new $fieldClass()); + } else { + $field = $this->wire(new Field()); + } $field->type = $type; $field->name = $_name; $field->label = $_name; // set label (mandatory since ~3.0.172) From 26337043e785d5a6a2a5f0be236eeee85b3aeaa9 Mon Sep 17 00:00:00 2001 From: gebeer Date: Sat, 16 Nov 2024 16:26:20 +0700 Subject: [PATCH 11/21] fix: wrong copy paste --- RockMigrations.module.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index 9af174e..eb0d7b9 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -879,16 +879,6 @@ public function createField($name, $type = 'text', $options = []) } $field = $this->getField($name, true); - // field does not exist - if (!$field) { - // get type - $type = $this->getFieldtype($type); - if (!$type) return; // logging above - - // create the new field - $_name = $this->wire->sanitizer->fieldName($name); - if ($_name !== $name) throw new WireException("Invalid fieldname ($name)!"); - // field does not exist if (!$field) { // get type From fc8be28b407decc0a0072fa1b9d0b2885d03bbd4 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Sat, 16 Nov 2024 11:08:51 +0100 Subject: [PATCH 12/21] fix: add early exit in createTemplateFromClassfile if tpl constant is not set --- RockMigrations.module.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index eb0d7b9..b1abc5f 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -893,7 +893,7 @@ public function createField($name, $type = 'text', $options = []) /** @var string $fieldClass */ $fieldClass = $type->getFieldClass(); $fieldClass = "ProcessWire\\$fieldClass"; - if(class_exists($fieldClass)) { + if (class_exists($fieldClass)) { // use the specific class to create new field if it exists $field = $this->wire(new $fieldClass()); } else { @@ -1232,6 +1232,10 @@ private function createTemplateFromClassfile( $classname = "\\$namespace\\$name"; $tmp = new $classname(); + // if $tmp::tpl is not defined we exit early + // this is to allow config migrations to create templates + if (!defined("{$classname}::tpl")) return; + try { // if the template already exists we exit early $tpl = $this->getTemplate($tmp::tpl, true); @@ -1246,7 +1250,7 @@ private function createTemplateFromClassfile( return $tpl; } catch (\Throwable $th) { - throw new WireException("Error setting up template - you must add the tpl constant to $classname"); + throw new WireException("Error setting up template for class $classname"); } } From 50869c9b32540c635fe5c533418476f6011fb9eb Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Sat, 16 Nov 2024 15:21:35 +0100 Subject: [PATCH 13/21] feat: add last run logfile --- RockMigrations.module.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index b1abc5f..ca21618 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -78,6 +78,8 @@ class RockMigrations extends WireData implements Module, ConfigurableModule **/ private $lastrun; + private $lastRunLogfile; + private $migrateAll = false; private $migrated = []; @@ -119,6 +121,7 @@ public function init() $config = $this->wire->config; $this->wire('rockmigrations', $this); + $this->lastRunLogfile = wire()->config->paths->logs . 'rockmigrations-lastrun.txt'; $this->installModule('MagicPages'); if ($config->debug) $this->setOutputLevel(self::outputLevelVerbose); @@ -2913,18 +2916,22 @@ public function lockPageName($form) */ public function log($msg, $throwException = true) { - $trace = $this->getTrace($msg); - $file = $trace['file']; - $line = $trace['line']; - $filename = pathinfo($file, PATHINFO_FILENAME); - $traceStr = "$filename:$line"; - // convert message to a string // this makes it possible to log a Debug::backtrace for example // which can be handy for debugging $msg = $this->str($msg); $msg = str_repeat(" ", $this->indent) . $msg; + // write raw message to log file + wire()->files->filePutContents($this->lastRunLogfile, $msg, FILE_APPEND); + + // enrich message with trace information + $trace = $this->getTrace($msg); + $file = $trace['file']; + $line = $trace['line']; + $filename = pathinfo($file, PATHINFO_FILENAME); + $traceStr = "$filename:$line"; + if ($this->isVerbose()) { try { $url = TracyDebugger::createEditorLink($file, $line, $traceStr); @@ -3402,7 +3409,10 @@ private function migrateWatchfiles($force = false) $this->ismigrating = true; // logging + // reset logs $this->wire->log->delete($this->className); + if (is_file($this->lastRunLogfile)) wire()->files->unlink($this->lastRunLogfile); + // start new log if (!$cli) { $this->log('-------------------------------------'); foreach ($changed as $file) $this->log("Detected change in $file"); From 2f3ccaa98f69e50b9da4aeacfad95e0216a51586 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Sat, 16 Nov 2024 17:43:15 +0100 Subject: [PATCH 14/21] feat: add option to prevent migrate from running --- classes/Deployment.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/classes/Deployment.php b/classes/Deployment.php index 2713cf2..a0646eb 100644 --- a/classes/Deployment.php +++ b/classes/Deployment.php @@ -71,8 +71,10 @@ public function __construct($argv = null) /** * Run default actions */ - public function run($keep = null) - { + public function run( + $keep = null, + $migrate = true, + ) { // this is for the auto-detect php feature if (defined('GET-PHP')) { echo $this->php() . "\n"; @@ -107,9 +109,11 @@ public function run($keep = null) $this->cleanupDB(); $this->trigger("cleanupDB", "after"); - $this->trigger("migrate", "before"); - $this->migrate(); - $this->trigger("migrate", "after"); + if ($migrate) { + $this->trigger("migrate", "before"); + $this->migrate(); + $this->trigger("migrate", "after"); + } $this->trigger("addRobots", "before"); $this->addRobots(); From 39775ccdf435c7198261a709de60d662b874d4bb Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Sat, 16 Nov 2024 22:12:08 +0100 Subject: [PATCH 15/21] docs: update config migration docs --- docs/config-migrations/readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/config-migrations/readme.md b/docs/config-migrations/readme.md index 225d0ff..e50285f 100644 --- a/docs/config-migrations/readme.md +++ b/docs/config-migrations/readme.md @@ -97,7 +97,7 @@ Let's say we have a module called `MyModule` and we create a field called `myfie To do so, we need to do two things: - Create the file `site/modules/MyModule/RockMigrations/RockMigrationsConstants.php` and do a modules refresh to populate it -- Add the `RockMigrationsConstants` trait to the module +- Let your module use the `RockMigrationsConstants` trait The trait file in `/site/modules/MyModule/RockMigrations/RockMigrationsConstants.php` could look like this: @@ -120,6 +120,7 @@ This file will be auto-generated by RockMigrations and should not be modified. W The code for the `MyModule.module.php` file could look like this: ```php +require_once __DIR__ . '/RockMigrationsConstants.php'; class MyModule extends WireData implements Module, ConfigurableModule { use \MyModule\RockMigrationsConstants; From b0cbd8cdf9514abc951f2c93e1d4fb9bd5d55ad6 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Sun, 17 Nov 2024 11:28:27 +0100 Subject: [PATCH 16/21] feat: add dedicated method runConfigMigrations() --- RockMigrations.module.php | 60 ++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index ca21618..0e5c792 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -803,6 +803,8 @@ private function createConstantTraits(): void // only do this if debug mode is enabled if (!wire()->config->debug) return; + $this->log("--- create PHP constant traits ---"); + // get files from watchlist $list = $this->sortedWatchlist(); @@ -1938,6 +1940,18 @@ private function getConfigFileName(string $file, $noPrefix = false): string return $prefix . str_replace('.php', '', basename($file)); } + /** + * Get all config files in given path + * NOTE: This will return all files, even if the module is not installed! + */ + public function getConfigFiles(string $path): array + { + return wire()->files->find($path, [ + 'recursive' => true, + 'extensions' => ['php'], + ]); + } + private function getConfigFileTag(string $file): string { $url = $this->toUrl($file); @@ -3165,7 +3179,8 @@ public function migrateAfterModuleInstall(HookEvent $event) */ public function migrateModule(Module $module): void { - $this->log("----- Migrate Module $module -----"); + $this->log("----- Migrate Module $module (DEPRECATED) -----"); + $this->log("Please use Config Migrations instead!"); $path = $this->pageClassPath($module); // if the module ships with custom pageclasses @@ -3440,18 +3455,8 @@ private function migrateWatchfiles($force = false) // in the first step we only create all fields/templates/etc // in the second step we migrate the data if ($prio === '#1000') { - $this->log("### Config File Migrations ###"); - $this->indent(2); - $this->log("--- create PHP constant traits ---"); - $this->createConstantTraits(); - $this->log("--- create objects ---"); - foreach ($items as $file) $this->runConfigFile($file, true); - $this->log("--- migrate data ---"); - foreach ($items as $file) $this->runConfigFile($file); - $this->indent(-2); - - // skip everything below - continue; + $this->runConfigMigrations($items); + continue; // skip everything below } foreach ($items as $path) { @@ -4172,6 +4177,35 @@ private function runConfigFile(string $file, $firstRun = false): void } } + /** + * Run config migrations of given files + * + * NOTE: This will always run migrations even when the module is not + * (yet) installed, so this method can be used on module installation to + * create all needed assets. + */ + public function runConfigMigrations(array|string $items): void + { + // was a path provided? + if (is_string($items)) { + if (is_dir($items)) $items = $this->getConfigFiles($items); + elseif (is_file($items)) $items = [$items]; + else { + $this->log("Invalid option for runConfigMigrations(): $items"); + return; + } + } + + $this->log("### Running Config Migrations ###"); + $this->indent(2); + $this->createConstantTraits(); + $this->log("--- first run: create assets ---"); + foreach ($items as $file) $this->runConfigFile($file, true); + $this->log("--- second run: migrate data ---"); + foreach ($items as $file) $this->runConfigFile($file); + $this->indent(-2); + } + /** * Run migrations from file */ From ed4a57d1edacc617b5e21ae90f0649c781d7e6f7 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Sun, 17 Nov 2024 18:09:12 +0100 Subject: [PATCH 17/21] remove deprecation note --- RockMigrations.module.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index 0e5c792..15eb923 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -3179,8 +3179,7 @@ public function migrateAfterModuleInstall(HookEvent $event) */ public function migrateModule(Module $module): void { - $this->log("----- Migrate Module $module (DEPRECATED) -----"); - $this->log("Please use Config Migrations instead!"); + $this->log("----- Migrate Module $module -----"); $path = $this->pageClassPath($module); // if the module ships with custom pageclasses From 9f07b3f114cb3970efe333e18395d507d6c82189 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Sun, 24 Nov 2024 14:03:32 +0100 Subject: [PATCH 18/21] fix: support hyphens in template names --- RockMigrations.module.php | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index 15eb923..4371036 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -839,7 +839,7 @@ private function createConstantTraits(): void $content .= "\ntrait RockMigrationsConstants\n{\n"; foreach ($files as $file) { $type = $this->getConfigFileType($file, true); - $shortName = $this->getConfigFileName($file, true); + $shortName = $this->getConfigFileName($file, true, true); if (str_starts_with($shortName, '.')) continue; $longName = $this->getConfigFileName($file); $content .= " const {$type}_$shortName = '$longName';\n"; @@ -1924,20 +1924,28 @@ private function getConfigFileArray(string $file): array } /** - * Get the name of property to migrate from config file - * * Adds a prefix if the file is located in a module folder to avoid * name clashes with fields from other modules. * + * @param string $file + * @param bool $noPrefix if true Do not add prefix + * @param bool $replaceHyphens if true Replace hyphens with underscores for valid constant names + * * Example: Returns "rockcommerce_foo" for file * /site/modules/RockCommerce/RockMigrations/fields/foo.php */ - private function getConfigFileName(string $file, $noPrefix = false): string - { + private function getConfigFileName( + string $file, + $noPrefix = false, + $replaceHyphens = false, + ): string { $prefix = ''; + $basename = basename($file); if (!$noPrefix) $prefix = $this->getConfigFileTag($file); if ($prefix) $prefix = strtolower($prefix) . "_"; - return $prefix . str_replace('.php', '', basename($file)); + // hyphens are not allowed in constant names + if ($replaceHyphens) $basename = str_replace('-', '_', $basename); + return $prefix . str_replace('.php', '', $basename); } /** From 4e776f5185ce19866fd853c87a9acec47f4e7485 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Wed, 27 Nov 2024 09:24:20 +0100 Subject: [PATCH 19/21] docs: add note about php82 --- docs/config-migrations/readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/config-migrations/readme.md b/docs/config-migrations/readme.md index e50285f..cc47e4a 100644 --- a/docs/config-migrations/readme.md +++ b/docs/config-migrations/readme.md @@ -88,6 +88,8 @@ I decided to use PHP files rather than YAML or JSON, because in PHP files we get ### Class Constant Traits +
This feature requires at least PHP8.2 to work.
+ As you might have noticed I don't like to type long field or template names as strings like `rockcommerce_mylongfieldname`. Instead I use class constants, which have two benefits: First, I can't make typos and second, I get autocompletion again. The idea is that all fields that are created from within a module are available as a class constant from that module. The easiest way to explain this is by example. From 3ebac1793d61db6c815b70aa676f2de62000ee17 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Sat, 30 Nov 2024 12:57:30 +0100 Subject: [PATCH 20/21] update tracy config stub --- snippets/ProcessWire/config-tracy.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/snippets/ProcessWire/config-tracy.txt b/snippets/ProcessWire/config-tracy.txt index 257ff5c..70f8f2c 100644 --- a/snippets/ProcessWire/config-tracy.txt +++ b/snippets/ProcessWire/config-tracy.txt @@ -7,4 +7,5 @@ 'localRootPath' => getenv("DDEV_APPROOT"), 'numLogEntries' => 100, // for RockMigrations + // 'editor' => 'cursor://file/%file:%line', ]; From a34bb2abb424a5a198e41178c37bccd4112a9349 Mon Sep 17 00:00:00 2001 From: BernhardBaumrock Date: Sat, 30 Nov 2024 15:37:20 +0100 Subject: [PATCH 21/21] feat: add createTrait option in runConfigMigrations This makes it possible to add optional config migrations for modules (like RockCommerce variations) --- RockMigrations.module.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/RockMigrations.module.php b/RockMigrations.module.php index 4371036..93236bb 100644 --- a/RockMigrations.module.php +++ b/RockMigrations.module.php @@ -4191,8 +4191,10 @@ private function runConfigFile(string $file, $firstRun = false): void * (yet) installed, so this method can be used on module installation to * create all needed assets. */ - public function runConfigMigrations(array|string $items): void - { + public function runConfigMigrations( + array|string $items, + bool $createTrait = true, + ): void { // was a path provided? if (is_string($items)) { if (is_dir($items)) $items = $this->getConfigFiles($items); @@ -4205,7 +4207,7 @@ public function runConfigMigrations(array|string $items): void $this->log("### Running Config Migrations ###"); $this->indent(2); - $this->createConstantTraits(); + if ($createTrait) $this->createConstantTraits(); $this->log("--- first run: create assets ---"); foreach ($items as $file) $this->runConfigFile($file, true); $this->log("--- second run: migrate data ---");