APIv4 - Use empty string instead of 'null' to pass null values to the database
authorColeman Watts <coleman@civicrm.org>
Thu, 20 Jan 2022 21:34:48 +0000 (16:34 -0500)
committerColeman Watts <coleman@civicrm.org>
Thu, 20 Jan 2022 21:34:48 +0000 (16:34 -0500)
`CRM_Core_DAO::copyValues()` already converts '' to 'null', and passing the string 'null'
into `BAO::writeRecords` or `BAO::create` functions prematurely can introduce subtle bugs
because the string 'null' is `!empty()` whereas '' is `empty()`.

Civi/Api4/Generic/Traits/DAOActionTrait.php
Civi/Api4/Utils/FormattingUtil.php

index 201f4c2ab98ac56c2ee580d6d2c946bacbe932cb..b44c7d1fda540ff65465a372d16174d59ecc0f9d 100644 (file)
@@ -60,8 +60,8 @@ trait DAOActionTrait {
     else {
       $inputFields = array_keys($input);
       // Convert 'null' input to true null
-      foreach ($input as $key => $val) {
-        if ($val === 'null') {
+      foreach ($inputFields as $key) {
+        if (($bao->$key ?? NULL) === 'null') {
           $bao->$key = NULL;
         }
       }
index f537e73b5e8b91406dada370c348dcdc7d594394..6d4af31ed144ed0059a06b1bf109e99d93394f0f 100644 (file)
@@ -56,7 +56,8 @@ class FormattingUtil {
       /*
        * Because of the wacky way that database values are saved we need to format
        * some of the values here. In this strange world the string 'null' is used to
-       * unset values. Hence if we encounter true null we change it to string 'null'.
+       * unset values. If we encounter true null at this layer we change it to an empty string
+       * and it will be converted to 'null' by CRM_Core_DAO::copyValues.
        *
        * If we encounter the string 'null' then we assume the user actually wants to
        * set the value to string null. However since the string null is reserved for
@@ -66,7 +67,7 @@ class FormattingUtil {
        * 'Null'.
        */
       elseif (array_key_exists($name, $params) && $params[$name] === NULL) {
-        $params[$name] = 'null';
+        $params[$name] = '';
       }
     }