api4: add sql function IFNULL
authorNoah Miller <nm@lemnisc.us>
Mon, 22 May 2023 23:57:01 +0000 (16:57 -0700)
committerNoah Miller <nm@lemnisc.us>
Mon, 22 May 2023 23:57:01 +0000 (16:57 -0700)
Civi/Api4/Query/SqlFunctionIFNULL.php [new file with mode: 0644]
tests/phpunit/api/v4/Action/SqlFunctionTest.php

diff --git a/Civi/Api4/Query/SqlFunctionIFNULL.php b/Civi/Api4/Query/SqlFunctionIFNULL.php
new file mode 100644 (file)
index 0000000..5714dd4
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Query;
+
+/**
+ * Sql function
+ */
+class SqlFunctionIFNULL extends SqlFunction {
+
+  protected static $category = self::CATEGORY_COMPARISON;
+
+  protected static $dataType = 'String';
+
+  protected static function params(): array {
+    return [
+      [
+        'optional' => FALSE,
+        'must_be' => ['SqlEquation', 'SqlField', 'SqlFunction'],
+        'label' => ts('Primary Value'),
+      ],
+      [
+        'optional' => FALSE,
+        'must_be' => ['SqlField', 'SqlFunction', 'SqlString', 'SqlNumber', 'SqlNull'],
+        'label' => ts('Fallback value'),
+      ],
+    ];
+  }
+
+  /**
+   * @return string
+   */
+  public static function getTitle(): string {
+    return ts('If Null');
+  }
+
+  /**
+   * @return string
+   */
+  public static function getDescription(): string {
+    return ts('The primary value, or if it is null, the fallback value.');
+  }
+
+}
index c7d570f5f8d7a2e40e7ab78f860fcf3303ea88be..329c4fefc75e779e528350aa7686884ebcfcd91b 100644 (file)
@@ -168,12 +168,13 @@ class SqlFunctionTest extends Api4TestBase implements TransactionalInterface {
     $result = Activity::get(FALSE)
       ->addWhere('id', 'IN', $aids)
       ->addSelect('IF(is_deleted, "Trash", "No Trash") AS trashed')
-      ->addSelect('NULLIF(subject, location) AS subject_is_location')
-      ->addSelect('NULLIF(duration, 456) AS duration_not_456')
-      ->addSelect('COALESCE(duration, location) AS duration_or_location')
-      ->addSelect('GREATEST(duration, 0200) AS duration_or_200')
-      ->addSelect('LEAST(duration, 300) AS 300_or_duration')
+      ->addSelect('NULLIF(subject, location) AS nullif_subject_is_location')
+      ->addSelect('NULLIF(duration, 456) AS nullif_duration_is_456')
+      ->addSelect('COALESCE(duration, location) AS coalesce_duration_location')
+      ->addSelect('GREATEST(duration, 0200) AS greatest_of_duration_or_200')
+      ->addSelect('LEAST(duration, 300) AS least_of_duration_and_300')
       ->addSelect('ISNULL(duration) AS duration_isnull')
+      ->addSelect('IFNULL(duration, 2) AS ifnull_duration_2')
       ->addOrderBy('id')
       ->execute()->indexBy('id');
 
@@ -181,19 +182,34 @@ class SqlFunctionTest extends Api4TestBase implements TransactionalInterface {
     $this->assertEquals('No Trash', $result[$aids[0]]['trashed']);
     $this->assertEquals('Trash', $result[$aids[1]]['trashed']);
     $this->assertEquals('No Trash', $result[$aids[2]]['trashed']);
-    $this->assertEquals(NULL, $result[$aids[0]]['subject_is_location']);
-    $this->assertEquals('xyz', $result[$aids[1]]['subject_is_location']);
-    $this->assertEquals('def', $result[$aids[2]]['subject_is_location']);
-    $this->assertEquals(123, $result[$aids[0]]['duration_not_456']);
-    $this->assertEquals(NULL, $result[$aids[1]]['duration_not_456']);
-    $this->assertEquals(NULL, $result[$aids[2]]['duration_not_456']);
-    $this->assertEquals('123', $result[$aids[0]]['duration_or_location']);
-    $this->assertEquals('abc', $result[$aids[1]]['duration_or_location']);
-    $this->assertEquals(123, $result[$aids[0]]['300_or_duration']);
-    $this->assertEquals(300, $result[$aids[2]]['300_or_duration']);
+
+    $this->assertEquals(NULL, $result[$aids[0]]['nullif_subject_is_location']);
+    $this->assertEquals('xyz', $result[$aids[1]]['nullif_subject_is_location']);
+    $this->assertEquals('def', $result[$aids[2]]['nullif_subject_is_location']);
+
+    $this->assertEquals(123, $result[$aids[0]]['nullif_duration_is_456']);
+    $this->assertEquals(NULL, $result[$aids[1]]['nullif_duration_is_456']);
+    $this->assertEquals(NULL, $result[$aids[2]]['nullif_duration_is_456']);
+
+    $this->assertEquals('123', $result[$aids[0]]['coalesce_duration_location']);
+    $this->assertEquals('abc', $result[$aids[1]]['coalesce_duration_location']);
+    $this->assertEquals('456', $result[$aids[2]]['coalesce_duration_location']);
+
+    $this->assertEquals(200, $result[$aids[0]]['greatest_of_duration_or_200']);
+    $this->assertEquals(NULL, $result[$aids[1]]['greatest_of_duration_or_200']);
+    $this->assertEquals(456, $result[$aids[2]]['greatest_of_duration_or_200']);
+
+    $this->assertEquals(123, $result[$aids[0]]['least_of_duration_and_300']);
+    $this->assertEquals(NULL, $result[$aids[1]]['least_of_duration_and_300']);
+    $this->assertEquals(300, $result[$aids[2]]['least_of_duration_and_300']);
+
     $this->assertEquals(FALSE, $result[$aids[0]]['duration_isnull']);
     $this->assertEquals(TRUE, $result[$aids[1]]['duration_isnull']);
     $this->assertEquals(FALSE, $result[$aids[2]]['duration_isnull']);
+
+    $this->assertEquals(123, $result[$aids[0]]['ifnull_duration_2']);
+    $this->assertEquals(2, $result[$aids[1]]['ifnull_duration_2']);
+    $this->assertEquals(456, $result[$aids[2]]['ifnull_duration_2']);
   }
 
   public function testStringFunctions() {