Skip to content

Commit d49e821

Browse files
authored
[9.x] Universal HigherOrderWhenProxy (#37632)
* [9.x] Universal HigherOrderWhenProxy * StyleCI * Replace by-reference variables with exceptions
1 parent 77ed2c7 commit d49e821

File tree

6 files changed

+165
-60
lines changed

6 files changed

+165
-60
lines changed

src/Illuminate/Collections/Enumerable.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,11 @@ public function filter(callable $callback = null);
271271
* Apply the callback if the value is truthy.
272272
*
273273
* @param bool $value
274-
* @param callable $callback
274+
* @param callable|null $callback
275275
* @param callable|null $default
276276
* @return static|mixed
277277
*/
278-
public function when($value, callable $callback, callable $default = null);
278+
public function when($value, callable $callback = null, callable $default = null);
279279

280280
/**
281281
* Apply the callback if the collection is empty.

src/Illuminate/Collections/HigherOrderWhenProxy.php

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,14 @@
22

33
namespace Illuminate\Support;
44

5-
/**
6-
* @mixin \Illuminate\Support\Enumerable
7-
*/
85
class HigherOrderWhenProxy
96
{
107
/**
11-
* The collection being operated on.
8+
* The target being conditionally operated on.
129
*
13-
* @var \Illuminate\Support\Enumerable
10+
* @var mixed
1411
*/
15-
protected $collection;
12+
protected $target;
1613

1714
/**
1815
* The condition for proxying.
@@ -24,31 +21,31 @@ class HigherOrderWhenProxy
2421
/**
2522
* Create a new proxy instance.
2623
*
27-
* @param \Illuminate\Support\Enumerable $collection
24+
* @param mixed $target
2825
* @param bool $condition
2926
* @return void
3027
*/
31-
public function __construct(Enumerable $collection, $condition)
28+
public function __construct($target, $condition)
3229
{
30+
$this->target = $target;
3331
$this->condition = $condition;
34-
$this->collection = $collection;
3532
}
3633

3734
/**
38-
* Proxy accessing an attribute onto the collection.
35+
* Proxy accessing an attribute onto the target.
3936
*
4037
* @param string $key
4138
* @return mixed
4239
*/
4340
public function __get($key)
4441
{
4542
return $this->condition
46-
? $this->collection->{$key}
47-
: $this->collection;
43+
? $this->target->{$key}
44+
: $this->target;
4845
}
4946

5047
/**
51-
* Proxy a method call onto the collection.
48+
* Proxy a method call on the target.
5249
*
5350
* @param string $method
5451
* @param array $parameters
@@ -57,7 +54,7 @@ public function __get($key)
5754
public function __call($method, $parameters)
5855
{
5956
return $this->condition
60-
? $this->collection->{$method}(...$parameters)
61-
: $this->collection;
57+
? $this->target->{$method}(...$parameters)
58+
: $this->target;
6259
}
6360
}

src/Illuminate/Collections/Traits/EnumeratesValues.php

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use Illuminate\Support\Collection;
1212
use Illuminate\Support\Enumerable;
1313
use Illuminate\Support\HigherOrderCollectionProxy;
14-
use Illuminate\Support\HigherOrderWhenProxy;
1514
use JsonSerializable;
1615
use Symfony\Component\VarDumper\VarDumper;
1716
use Traversable;
@@ -45,6 +44,8 @@
4544
*/
4645
trait EnumeratesValues
4746
{
47+
use Conditionable;
48+
4849
/**
4950
* The methods that can be proxied.
5051
*
@@ -453,29 +454,6 @@ public function sum($callback = null)
453454
}, 0);
454455
}
455456

456-
/**
457-
* Apply the callback if the value is truthy.
458-
*
459-
* @param bool|mixed $value
460-
* @param callable|null $callback
461-
* @param callable|null $default
462-
* @return static|mixed
463-
*/
464-
public function when($value, callable $callback = null, callable $default = null)
465-
{
466-
if (! $callback) {
467-
return new HigherOrderWhenProxy($this, $value);
468-
}
469-
470-
if ($value) {
471-
return $callback($this, $value);
472-
} elseif ($default) {
473-
return $default($this, $value);
474-
}
475-
476-
return $this;
477-
}
478-
479457
/**
480458
* Apply the callback if the collection is empty.
481459
*
@@ -500,19 +478,6 @@ public function whenNotEmpty(callable $callback, callable $default = null)
500478
return $this->when($this->isNotEmpty(), $callback, $default);
501479
}
502480

503-
/**
504-
* Apply the callback if the value is falsy.
505-
*
506-
* @param bool $value
507-
* @param callable $callback
508-
* @param callable|null $default
509-
* @return static|mixed
510-
*/
511-
public function unless($value, callable $callback, callable $default = null)
512-
{
513-
return $this->when(! $value, $callback, $default);
514-
}
515-
516481
/**
517482
* Apply the callback unless the collection is empty.
518483
*

src/Illuminate/Support/Traits/Conditionable.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,25 @@
22

33
namespace Illuminate\Support\Traits;
44

5+
use Illuminate\Support\HigherOrderWhenProxy;
6+
57
trait Conditionable
68
{
79
/**
810
* Apply the callback if the given "value" is truthy.
911
*
1012
* @param mixed $value
11-
* @param callable $callback
13+
* @param callable|null $callback
1214
* @param callable|null $default
1315
*
1416
* @return mixed
1517
*/
16-
public function when($value, $callback, $default = null)
18+
public function when($value, callable $callback = null, callable $default = null)
1719
{
20+
if (! $callback) {
21+
return new HigherOrderWhenProxy($this, $value);
22+
}
23+
1824
if ($value) {
1925
return $callback($this, $value) ?: $this;
2026
} elseif ($default) {
@@ -28,13 +34,17 @@ public function when($value, $callback, $default = null)
2834
* Apply the callback if the given "value" is falsy.
2935
*
3036
* @param mixed $value
31-
* @param callable $callback
37+
* @param callable|null $callback
3238
* @param callable|null $default
3339
*
3440
* @return mixed
3541
*/
36-
public function unless($value, $callback, $default = null)
42+
public function unless($value, callable $callback = null, callable $default = null)
3743
{
44+
if (! $callback) {
45+
return new HigherOrderWhenProxy($this, ! $value);
46+
}
47+
3848
if (! $value) {
3949
return $callback($this, $value) ?: $this;
4050
} elseif ($default) {

tests/Support/SupportCollectionTest.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4298,8 +4298,8 @@ public function testWhenEmpty($collection)
42984298
{
42994299
$data = new $collection(['michael', 'tom']);
43004300

4301-
$data = $data->whenEmpty(function ($collection) {
4302-
return $data->concat(['adam']);
4301+
$data = $data->whenEmpty(function () {
4302+
throw new Exception('whenEmpty() should not trigger on a collection with items');
43034303
});
43044304

43054305
$this->assertSame(['michael', 'tom'], $data->toArray());
@@ -4367,6 +4367,30 @@ public function testWhenNotEmptyDefault($collection)
43674367
$this->assertSame(['michael', 'tom', 'adam'], $data->toArray());
43684368
}
43694369

4370+
/**
4371+
* @dataProvider collectionClassProvider
4372+
*/
4373+
public function testHigherOrderWhenAndUnless($collection)
4374+
{
4375+
$data = new $collection(['michael', 'tom']);
4376+
4377+
$data = $data->when(true)->concat(['chris']);
4378+
4379+
$this->assertSame(['michael', 'tom', 'chris'], $data->toArray());
4380+
4381+
$data = $data->when(false)->concat(['adam']);
4382+
4383+
$this->assertSame(['michael', 'tom', 'chris'], $data->toArray());
4384+
4385+
$data = $data->unless(false)->concat(['adam']);
4386+
4387+
$this->assertSame(['michael', 'tom', 'chris', 'adam'], $data->toArray());
4388+
4389+
$data = $data->unless(true)->concat(['bogdan']);
4390+
4391+
$this->assertSame(['michael', 'tom', 'chris', 'adam'], $data->toArray());
4392+
}
4393+
43704394
/**
43714395
* @dataProvider collectionClassProvider
43724396
*/
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Support;
4+
5+
use Exception;
6+
use Illuminate\Support\Traits\Conditionable;
7+
use PHPUnit\Framework\TestCase;
8+
9+
class SupportConditionableTest extends TestCase
10+
{
11+
public function testWhenConditionCallback()
12+
{
13+
$object = (new CustomConditionableObject())
14+
->when(2, function ($object, $condition) {
15+
$object->on();
16+
$this->assertEquals(2, $condition);
17+
}, function () {
18+
throw new Exception('when() should not trigger default callback on a truthy value');
19+
});
20+
21+
$this->assertTrue($object->enabled);
22+
}
23+
24+
public function testWhenDefaultCallback()
25+
{
26+
$object = (new CustomConditionableObject())
27+
->when(null, function () {
28+
throw new Exception('when() should not trigger on a falsy value');
29+
}, function ($object, $condition) {
30+
$object->on();
31+
$this->assertNull($condition);
32+
});
33+
34+
$this->assertTrue($object->enabled);
35+
}
36+
37+
public function testUnlessConditionCallback()
38+
{
39+
$object = (new CustomConditionableObject())
40+
->unless(null, function ($object, $condition) {
41+
$object->on();
42+
$this->assertNull($condition);
43+
}, function () {
44+
throw new Exception('unless() should not trigger default callback on a falsy value');
45+
});
46+
47+
$this->assertTrue($object->enabled);
48+
}
49+
50+
public function testUnlessDefaultCallback()
51+
{
52+
$object = (new CustomConditionableObject())
53+
->unless(2, function () {
54+
throw new Exception('unless() should not trigger on a truthy value');
55+
}, function ($object, $condition) {
56+
$object->on();
57+
$this->assertEquals(2, $condition);
58+
});
59+
60+
$this->assertTrue($object->enabled);
61+
}
62+
63+
public function testWhenProxy()
64+
{
65+
$object = (new CustomConditionableObject())->when(true)->on();
66+
67+
$this->assertInstanceOf(CustomConditionableObject::class, $object);
68+
$this->assertTrue($object->enabled);
69+
70+
$object = (new CustomConditionableObject())->when(false)->on();
71+
72+
$this->assertInstanceOf(CustomConditionableObject::class, $object);
73+
$this->assertFalse($object->enabled);
74+
}
75+
76+
public function testUnlessProxy()
77+
{
78+
$object = (new CustomConditionableObject())->unless(false)->on();
79+
80+
$this->assertInstanceOf(CustomConditionableObject::class, $object);
81+
$this->assertTrue($object->enabled);
82+
83+
$object = (new CustomConditionableObject())->unless(true)->on();
84+
85+
$this->assertInstanceOf(CustomConditionableObject::class, $object);
86+
$this->assertFalse($object->enabled);
87+
}
88+
}
89+
90+
class CustomConditionableObject
91+
{
92+
use Conditionable;
93+
94+
public $enabled = false;
95+
96+
public function on()
97+
{
98+
$this->enabled = true;
99+
100+
return $this;
101+
}
102+
103+
public function off()
104+
{
105+
$this->enabled = false;
106+
107+
return $this;
108+
}
109+
}

0 commit comments

Comments
 (0)