From be5e477379d7c87a018ccf4fdb085f547a8c32d0 Mon Sep 17 00:00:00 2001 From: netzknecht <38781482+netzknecht@users.noreply.github.com> Date: Fri, 27 Oct 2023 11:10:17 +0200 Subject: [PATCH] Bugfix for issue #46247, #28029 and related https://github.com/laravel/framework/issues/46247 https://github.com/laravel/framework/pull/28029 --- .../Eloquent/Concerns/HasAttributes.php | 14 +++++--- src/Illuminate/Support/helpers.php | 33 +++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index d4f86c52c2be..b3e22ea93f0c 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -2071,8 +2071,7 @@ public function originalIsEquivalent($key) return $this->fromDateTime($attribute) === $this->fromDateTime($original); } elseif ($this->hasCast($key, ['object', 'collection'])) { - return $this->fromJson($attribute) === - $this->fromJson($original); + return $this->diffJson($attribute, $original); } elseif ($this->hasCast($key, ['real', 'float', 'double'])) { if ($original === null) { return false; @@ -2083,11 +2082,13 @@ public function originalIsEquivalent($key) return $this->castAttribute($key, $attribute) === $this->castAttribute($key, $original); } elseif ($this->isClassCastable($key) && Str::startsWith($this->getCasts()[$key], [AsArrayObject::class, AsCollection::class])) { - return $this->fromJson($attribute) === $this->fromJson($original); + return $this->diffJson($attribute, $original); } elseif ($this->isClassCastable($key) && Str::startsWith($this->getCasts()[$key], [AsEnumArrayObject::class, AsEnumCollection::class])) { - return $this->fromJson($attribute) === $this->fromJson($original); + return $this->diffJson($attribute, $original); } elseif ($this->isClassCastable($key) && $original !== null && Str::startsWith($this->getCasts()[$key], [AsEncryptedArrayObject::class, AsEncryptedCollection::class])) { return $this->fromEncryptedString($attribute) === $this->fromEncryptedString($original); + } elseif ($this->isClassCastable($key) && $this->resolveCasterClass($key) instanceof Json) { + return $this->diffJson($attribute, $original); } return is_numeric($attribute) && is_numeric($original) @@ -2254,4 +2255,9 @@ protected static function getAttributeMarkedMutatorMethods($class) return false; })->map->name->values()->all(); } + + private function diffJson($attribute, $original) + { + return !count(array_diff_assoc_recursive($this->fromJson($attribute), $this->fromJson($original))); + } } diff --git a/src/Illuminate/Support/helpers.php b/src/Illuminate/Support/helpers.php index 11348fa4f296..d8c53c188d03 100755 --- a/src/Illuminate/Support/helpers.php +++ b/src/Illuminate/Support/helpers.php @@ -432,3 +432,36 @@ function with($value, callable $callback = null) return is_null($callback) ? $value : $callback($value); } } + +if (!function_exists('array_diff_assoc_recursive')) { + /** + * Computes the difference between two arrays with additional index check recursively + * + * @link https://gist.github.com/tulik/30550e91c641c9a7564a407b691983ad + * + * @param mixed $array1 + * @param mixed $array2 + * @return array + */ + function array_diff_assoc_recursive($array1, $array2) + { + $difference = array(); + + foreach ($array1 as $key => $value) { + if (is_array($value)) { + if (!isset($array2[$key]) || !is_array($array2[$key])) { + $difference[$key] = $value; + } else { + $new_diff = array_diff_assoc_recursive($value, $array2[$key]); + if (!empty($new_diff)) { + $difference[$key] = $new_diff; + } + } + } elseif (!array_key_exists($key, $array2) || $array2[$key] !== $value) { + $difference[$key] = $value; + } + } + + return $difference; + } +} \ No newline at end of file