dev/core#2117 - Resources - Allow each snippet to have `aliases`
authorTim Otten <totten@civicrm.org>
Wed, 14 Oct 2020 01:24:47 +0000 (18:24 -0700)
committerTim Otten <totten@civicrm.org>
Wed, 14 Oct 2020 01:26:55 +0000 (18:26 -0700)
__Before__: Every snippet/resource has a singular name. If the name ever changes,
it becomes a backward-compatibility break.

__After__: Every snippet/resource has a singular name, and it may optionally
have a list of aliases.  If the name ever changes, you can leave an alias
for backward-compatibility.

CRM/Core/Resources/CollectionInterface.php
CRM/Core/Resources/CollectionTrait.php
tests/phpunit/CRM/Core/Resources/CollectionTestTrait.php

index e3d1433b710530111921061d50d7e8fb5271626e..a81db1cfacb32e45f4eed2ee9c9ba7a265e3cac1 100644 (file)
@@ -50,6 +50,7 @@
  *
  *   - type: string (markup, template, callback, script, scriptFile, scriptUrl, jquery, style, styleFile, styleUrl)
  *   - name: string, symbolic identifier for this resource
+ *   - aliases: string[], list of alternative names for this resource
  *   - weight: int, default=1. Lower weights come before higher weights.
  *     (If two resources have the same weight, then a secondary ordering will be
  *     used to ensure reproducibility. However, the secondary ordering is
index ec7ef1845356c3c4f95245ae17cfbfe9a65cf0f3..38abdea67979d8a11f1c54f990b0290a4348833b 100644 (file)
@@ -122,6 +122,10 @@ trait CRM_Core_Resources_CollectionTrait {
       $snippet['styleFileUrls'] = $theme->resolveUrls($theme->getActiveThemeKey(), $ext, $file);
     }
 
+    if (isset($snippet['aliases']) && !is_array($snippet['aliases'])) {
+      $snippet['aliases'] = [$snippet['aliases']];
+    }
+
     $this->snippets[$snippet['name']] = $snippet;
     $this->isSorted = FALSE;
     return $snippet;
@@ -149,8 +153,15 @@ trait CRM_Core_Resources_CollectionTrait {
    * @see CRM_Core_Resources_CollectionInterface::update()
    */
   public function update($name, $snippet) {
-    $this->snippets[$name] = array_merge($this->snippets[$name], $snippet);
-    $this->isSorted = FALSE;
+    foreach ($this->resolveName($name) as $realName) {
+      $this->snippets[$realName] = array_merge($this->snippets[$realName], $snippet);
+      $this->isSorted = FALSE;
+      return $this;
+    }
+
+    Civi::log()->warning('Failed to update resource by name ({name})', [
+      'name' => $name,
+    ]);
     return $this;
   }
 
@@ -174,7 +185,12 @@ trait CRM_Core_Resources_CollectionTrait {
    * @see CRM_Core_Resources_CollectionInterface::get()
    */
   public function &get($name) {
-    return $this->snippets[$name];
+    foreach ($this->resolveName($name) as $realName) {
+      return $this->snippets[$realName];
+    }
+
+    $null = NULL;
+    return $null;
   }
 
   /**
@@ -290,6 +306,24 @@ trait CRM_Core_Resources_CollectionTrait {
     return $this;
   }
 
+  /**
+   * @param string $name
+   *   Name or alias.
+   * return array
+   *   List of real names.
+   */
+  protected function resolveName($name) {
+    if (isset($this->snippets[$name])) {
+      return [$name];
+    }
+    foreach ($this->snippets as $snippetName => $snippet) {
+      if (isset($snippet['aliases']) && in_array($name, $snippet['aliases'])) {
+        return [$snippetName];
+      }
+    }
+    return [];
+  }
+
   /**
    * @param $a
    * @param $b
index 3b1a069cbb420135037fea3ff800877c0b9732c3..63fad9d1986e3ca40a99ad1d3a5cc9f785f93ddf 100644 (file)
@@ -222,6 +222,45 @@ trait CRM_Core_Resources_CollectionTestTrait {
     $this->assertEquals(1, $count, 'Expect one registered snippet');
   }
 
+  /**
+   * Create a few resources with aliases. Use a mix of reads+writes on both the
+   * canonical names and aliased names.
+   */
+  public function testAliases() {
+    $b = $this->createEmptyCollection();
+    $b->add([
+      'styleUrl' => 'https://example.com/foo.css',
+      'name' => 'foo',
+      'aliases' => ['bar', 'borg'],
+    ]);
+    $b->add([
+      'scriptUrl' => 'https://example.com/whiz.js',
+      'name' => 'whiz',
+      'aliases' => 'bang',
+    ]);
+
+    $this->assertEquals('foo', $b->get('foo')['name']);
+    $this->assertEquals('foo', $b->get('bar')['name']);
+    $this->assertEquals('foo', $b->get('borg')['name']);
+    $this->assertEquals('whiz', $b->get('whiz')['name']);
+    $this->assertEquals('whiz', $b->get('bang')['name']);
+    $this->assertEquals(NULL, $b->get('snafu'));
+
+    // Go back+forth, updating with one name then reading with the other.
+
+    $b->get('borg')['borgify'] = TRUE;
+    $this->assertEquals(TRUE, $b->get('foo')['borgify']);
+
+    $b->get('foo')['d'] = 'ie';
+    $this->assertEquals('ie', $b->get('borg')['d']);
+
+    $b->update('bang', ['b52' => 'love shack']);
+    $this->assertEquals('love shack', $b->get('whiz')['b52']);
+
+    $b->update('whiz', ['golly' => 'gee']);
+    $this->assertEquals('gee', $b->get('bang')['golly']);
+  }
+
   /**
    * Add some items to a bundle - then clear() all of them.
    */