diff --git a/src/Illuminate/Collections/Traits/EnumeratesValues.php b/src/Illuminate/Collections/Traits/EnumeratesValues.php index d2894529ed6e..5eeb4a9f4ab0 100644 --- a/src/Illuminate/Collections/Traits/EnumeratesValues.php +++ b/src/Illuminate/Collections/Traits/EnumeratesValues.php @@ -350,6 +350,36 @@ public function value($key, $default = null) return value($default); } + /** + * Get the value of a given key from the first item in the collection, + * even if the value is a falsy one like 0, false, or an empty string. + * + * This method avoids treating falsy values as null or non-existent. + * + * @param string|int $key The key to retrieve from the first matching item. + * @param mixed|null $default Default value if key not found. + * @return mixed + */ + public function valuePreservingFalsy($key, $default = null) + { + $item = $this->first(); + + if ($item === null) { + return value($default); + } + + if (is_array($item) && array_key_exists($key, $item)) { + return $item[$key]; + } + + if (is_object($item) && property_exists($item, $key)) { + return $item->{$key}; + } + + // Fallback to dot notation + return data_get($item, $key, $default); + } + /** * Ensure that every item in the collection is of the expected type. * diff --git a/tests/Support/ValuePreservingFalsyTest.php b/tests/Support/ValuePreservingFalsyTest.php new file mode 100644 index 000000000000..852af53e2b29 --- /dev/null +++ b/tests/Support/ValuePreservingFalsyTest.php @@ -0,0 +1,118 @@ + 'Tim', 'balance' => 0], + ['name' => 'John', 'balance' => 200], + ]); + + $this->assertSame(0, $collection->valuePreservingFalsy('balance')); + } + + // Test when the value is 0.0 (float) + public function test_float_zero_is_preserved() + { + $collection = collect([ + ['name' => 'Tim', 'balance' => 0.0], + ['name' => 'John', 'balance' => 200.5], + ]); + + $this->assertSame(0.0, $collection->valuePreservingFalsy('balance')); + } + + // Test when the value is false (boolean) + public function test_false_is_preserved() + { + $collection = collect([ + ['name' => 'John', 'vegetarian' => true], + ['name' => 'Tim', 'vegetarian' => false], + ]); + + $this->assertFalse($collection->where('name', 'Tim')->valuePreservingFalsy('vegetarian')); + } + + // Test when the value is an empty string + public function test_empty_string_is_preserved() + { + $collection = collect([ + ['name' => 'Tim', 'status' => ''], + ['name' => 'John', 'status' => 'active'], + ]); + + $this->assertSame('', $collection->valuePreservingFalsy('status')); + } + + // Test when the value is null + public function test_null_is_preserved() + { + $collection = collect([ + ['name' => 'Tim', 'age' => null], + ['name' => 'John', 'age' => 30], + ]); + + $this->assertNull($collection->valuePreservingFalsy('age')); + } + + // Test when the value is an empty array + public function test_empty_array_is_preserved() + { + $collection = collect([ + ['name' => 'Tim', 'tags' => []], + ['name' => 'John', 'tags' => ['admin']], + ]); + + $this->assertSame([], $collection->valuePreservingFalsy('tags')); + } + + // Test when the value is '0' (string zero) + public function test_string_zero_is_preserved() + { + $collection = collect([ + ['name' => 'Tim', 'balance' => '0'], + ['name' => 'John', 'balance' => '100'], + ]); + + $this->assertSame('0', $collection->valuePreservingFalsy('balance')); + } + + // Test when a missing key is provided + public function test_missing_key_returns_default() + { + $collection = collect([ + ['name' => 'Tim', 'balance' => 0], + ['name' => 'John', 'balance' => 200], + ]); + + $this->assertSame('default_value', $collection->valuePreservingFalsy('missing_key', 'default_value')); + } + + // Test when a falsy value in a subsequent item is returned (e.g. first item is falsy, second is not) + public function test_first_falsy_value_is_preserved() + { + $collection = collect([ + ['name' => 'Tim', 'status' => 0], + ['name' => 'John', 'status' => 'active'], + ]); + + $this->assertSame(0, $collection->valuePreservingFalsy('status')); + } + + // Test when a falsy value in a subsequent item is returned (string '0' case) + public function test_first_item_with_string_zero_is_preserved() + { + $collection = collect([ + ['name' => 'Tim', 'status' => '0'], + ['name' => 'John', 'status' => 'active'], + ]); + + $this->assertSame('0', $collection->valuePreservingFalsy('status')); + } +}