Skip to content

Commit 7a45fba

Browse files
committed
Merge branch 'add-chainable-with-each'
2 parents 3be3509 + 16fe08f commit 7a45fba

File tree

4 files changed

+156
-12
lines changed

4 files changed

+156
-12
lines changed

src/Toolkit/Contract/Core/Chainable.php

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,34 @@ interface Chainable
1111
* Move to the next method in the chain after applying a callback to the
1212
* object
1313
*
14-
* @param callable($this): $this $callback
15-
* @return $this
14+
* @param callable(static): static $callback
15+
* @return static
1616
*/
1717
public function apply(callable $callback);
1818

1919
/**
2020
* Move to the next method in the chain after applying a conditional
2121
* callback to the object
2222
*
23-
* @param (callable($this): bool)|bool $condition
24-
* @param (callable($this): $this)|null $then Called if `$condition`
23+
* @param (callable(static): bool)|bool $condition
24+
* @param (callable(static): static)|null $then Called if `$condition`
2525
* resolves to `true`.
26-
* @param (callable($this): $this)|null $else Called if `$condition`
26+
* @param (callable(static): static)|null $else Called if `$condition`
2727
* resolves to `false`.
28-
* @return $this
28+
* @return static
2929
*/
3030
public function if($condition, ?callable $then = null, ?callable $else = null);
31+
32+
/**
33+
* Move to the next method in the chain after applying a callback to the
34+
* object with each item in a list
35+
*
36+
* @template TKey
37+
* @template TValue
38+
*
39+
* @param iterable<TKey,TValue> $list
40+
* @param callable(static, TValue, TKey): static $callback
41+
* @return static
42+
*/
43+
public function withEach(iterable $list, callable $callback);
3144
}

src/Toolkit/Core/Concern/HasChainableMethods.php

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@
88
* Implements Chainable
99
*
1010
* @see Chainable
11+
*
12+
* @api
13+
*
14+
* @phpstan-require-implements Chainable
1115
*/
1216
trait HasChainableMethods
1317
{
1418
/**
15-
* @param callable($this): static $callback
19+
* @param callable(static): static $callback
1620
* @return static
1721
*/
1822
public function apply(callable $callback)
@@ -21,9 +25,9 @@ public function apply(callable $callback)
2125
}
2226

2327
/**
24-
* @param (callable($this): bool)|bool $condition
25-
* @param (callable($this): static)|null $then
26-
* @param (callable($this): static)|null $else
28+
* @param (callable(static): bool)|bool $condition
29+
* @param (callable(static): static)|null $then
30+
* @param (callable(static): static)|null $else
2731
* @return static
2832
*/
2933
public function if($condition, ?callable $then = null, ?callable $else = null)
@@ -36,4 +40,21 @@ public function if($condition, ?callable $then = null, ?callable $else = null)
3640
}
3741
return $then === null ? $this : $then($this);
3842
}
43+
44+
/**
45+
* @template TKey
46+
* @template TValue
47+
*
48+
* @param iterable<TKey,TValue> $list
49+
* @param callable(static, TValue, TKey): static $callback
50+
* @return static
51+
*/
52+
public function withEach(iterable $list, callable $callback)
53+
{
54+
$instance = $this;
55+
foreach ($list as $key => $value) {
56+
$instance = $callback($instance, $value, $key);
57+
}
58+
return $instance;
59+
}
3960
}

src/Toolkit/Core/Facade/App.php

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Salient\Tests\Core\Concern\HasChainableMethods;
4+
5+
use Salient\Contract\Core\Chainable;
6+
use Salient\Core\Concern\HasChainableMethods;
7+
use Salient\Tests\TestCase;
8+
9+
/**
10+
* @covers \Salient\Core\Concern\HasChainableMethods
11+
*/
12+
final class HasChainableMethodsTest extends TestCase
13+
{
14+
public function testApply(): void
15+
{
16+
$a = new MyChainable();
17+
$b = $a->apply(fn(MyChainable $chainable) => $chainable);
18+
$c = $b->apply(fn(MyChainable $chainable) => $chainable->next());
19+
20+
$this->assertSame($a, $b);
21+
$this->assertNotSame($b, $c);
22+
}
23+
24+
public function testIf(): void
25+
{
26+
$callback1 = fn(MyChainable $chainable) => $chainable->next();
27+
$callback2 = fn(MyChainable $chainable) => $chainable->next()->next();
28+
29+
$a = new MyChainable();
30+
$b = $a->if(true, null, $callback2);
31+
$c = $b->if(false, $callback1, null);
32+
$d = $c->if(true, $callback1, $callback2);
33+
$e = $d->if(false, $callback1, $callback2);
34+
$f = $e->if(fn() => true, null, $callback2);
35+
$g = $f->if(fn() => false, $callback1, null);
36+
$h = $g->if(fn() => true, $callback1, $callback2);
37+
$i = $h->if(fn() => false, $callback1, $callback2);
38+
39+
$this->assertSame($a, $b);
40+
$this->assertSame($a, $c);
41+
$this->assertNotSame($c, $d);
42+
$this->assertSame(1, $d->id());
43+
$this->assertNotSame($d, $e);
44+
$this->assertSame(3, $e->id());
45+
$this->assertSame($e, $f);
46+
$this->assertSame($e, $g);
47+
$this->assertNotSame($g, $h);
48+
$this->assertSame(4, $h->id());
49+
$this->assertNotSame($h, $i);
50+
$this->assertSame(6, $i->id());
51+
}
52+
53+
public function testWithEach(): void
54+
{
55+
$callback = fn(MyChainable $chainable, $value, $key) =>
56+
$chainable->next()->record([$key, $value]);
57+
58+
$a = new MyChainable();
59+
$b = $a->withEach([], $callback);
60+
$c = $b->withEach([1, 'foo' => 2, 'BAR' => 3], $callback);
61+
62+
$this->assertSame($a, $b);
63+
$this->assertNotSame($b, $c);
64+
$this->assertSame(3, $c->id());
65+
$this->assertSame([[0, 1], ['foo', 2], ['BAR', 3]], $c->entries());
66+
}
67+
}
68+
69+
class MyChainable implements Chainable
70+
{
71+
use HasChainableMethods;
72+
73+
private int $Id = 0;
74+
/** @var mixed[] */
75+
private array $Entries = [];
76+
77+
public function id(): int
78+
{
79+
return $this->Id;
80+
}
81+
82+
/**
83+
* @return mixed[]
84+
*/
85+
public function entries(): array
86+
{
87+
return $this->Entries;
88+
}
89+
90+
/**
91+
* @param mixed $entry
92+
* @return $this
93+
*/
94+
public function record($entry)
95+
{
96+
$this->Entries[] = $entry;
97+
return $this;
98+
}
99+
100+
/**
101+
* @return static
102+
*/
103+
public function next()
104+
{
105+
$instance = clone $this;
106+
$instance->Id++;
107+
return $instance;
108+
}
109+
}

0 commit comments

Comments
 (0)