Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.x] Conditional extender instantiation #3898

Merged
merged 12 commits into from
Oct 18, 2023
48 changes: 41 additions & 7 deletions framework/core/src/Extend/Conditional.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,49 @@
use Flarum\Extension\ExtensionManager;
use Illuminate\Contracts\Container\Container;

/**
* The Conditional extender allows developers to conditionally apply other extenders
* based on either boolean values or results from callable functions.
*
* This is useful for applying extenders only if certain conditions are met,
* such as the presence of an enabled extension or a specific configuration setting.
*/
class Conditional implements ExtenderInterface
{
/**
* @var array<array{condition: bool|callable, extenders: ExtenderInterface[]}>
* An array of conditions and their associated extenders.
*
* Each entry should have:
* - 'condition': a boolean or callable that should return a boolean.
* - 'extenders': an array of extenders, a callable returning an array of extenders, or an invokable class string.
*
* @var array<array{condition: bool|callable, extenders: ExtenderInterface[]|callable|string}>
*/
protected $conditions = [];

/**
* @param ExtenderInterface[] $extenders
* Apply extenders only if a specific extension is enabled.
*
* @param string $extensionId The ID of the extension.
* @param ExtenderInterface[]|callable|string $extenders An array of extenders, a callable returning an array of extenders, or an invokable class string.
* @return self
*/
public function whenExtensionEnabled(string $extensionId, array $extenders): self
public function whenExtensionEnabled(string $extensionId, $extenders): self
{
return $this->when(function (ExtensionManager $extensions) use ($extensionId) {
return $extensions->isEnabled($extensionId);
}, $extenders);
}

/**
* @param bool|callable $condition
* @param ExtenderInterface[] $extenders
* Apply extenders based on a condition.
*
* @param bool|callable $condition A boolean or callable that should return a boolean.
* If this evaluates to true, the extenders will be applied.
* @param ExtenderInterface[]|callable|string $extenders An array of extenders, a callable returning an array of extenders, or an invokable class string.
* @return self
*/
public function when($condition, array $extenders): self
public function when($condition, $extenders): self
{
$this->conditions[] = [
'condition' => $condition,
Expand All @@ -44,6 +65,13 @@ public function when($condition, array $extenders): self
return $this;
}

/**
* Iterates over the conditions and applies the associated extenders if the conditions are met.
*
* @param Container $container
* @param Extension|null $extension
* @return void
*/
public function extend(Container $container, Extension $extension = null)
{
foreach ($this->conditions as $condition) {
Expand All @@ -52,7 +80,13 @@ public function extend(Container $container, Extension $extension = null)
}

if ($condition['condition']) {
foreach ($condition['extenders'] as $extender) {
$extenders = $condition['extenders'];

if (is_string($extenders) || is_callable($extenders)) {
$extenders = $container->call($extenders);
}

foreach ($extenders as $extender) {
$extender->extend($container, $extension);
}
}
Expand Down
136 changes: 136 additions & 0 deletions framework/core/tests/integration/extenders/ConditionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,140 @@ public function conditional_injects_dependencies_to_condition_callable()

$this->app();
}

/** @test */
public function conditional_does_not_instantiate_extender_if_condition_is_false_using_callable()
{
$this->extend(
(new Extend\Conditional())
->when(false, TestExtender::class)
);

$this->app();

$response = $this->send(
$this->request('GET', '/api', [
'authenticatedAs' => 1,
])
);

$payload = json_decode($response->getBody()->getContents(), true);

$this->assertArrayNotHasKey('customConditionalAttribute', $payload['data']['attributes']);
}

/** @test */
public function conditional_does_instantiate_extender_if_condition_is_true_using_callable()
{
$this->extend(
(new Extend\Conditional())
->when(true, TestExtender::class)
);

$this->app();

$response = $this->send(
$this->request('GET', '/api', [
'authenticatedAs' => 1,
])
);

$payload = json_decode($response->getBody()->getContents(), true);

$this->assertArrayHasKey('customConditionalAttribute', $payload['data']['attributes']);
}

/** @test */
public function conditional_does_not_instantiate_extender_if_condition_is_false_using_callback()
{
$this->extend(
(new Extend\Conditional())
->when(false, function (): array {
return [
(new Extend\ApiSerializer(ForumSerializer::class))
->attributes(function () {
return [
'customConditionalAttribute' => true
];
})
];
})
);

$this->app();

$response = $this->send(
$this->request('GET', '/api', [
'authenticatedAs' => 1,
])
);

$payload = json_decode($response->getBody()->getContents(), true);

$this->assertArrayNotHasKey('customConditionalAttribute', $payload['data']['attributes']);
}

/** @test */
public function conditional_does_instantiate_extender_if_condition_is_true_using_callback()
{
$this->extend(
(new Extend\Conditional())
->when(true, function (): array {
return [
(new Extend\ApiSerializer(ForumSerializer::class))
->attributes(function () {
return [
'customConditionalAttribute' => true
];
})
];
})
);

$this->app();

$response = $this->send(
$this->request('GET', '/api', [
'authenticatedAs' => 1,
])
);

$payload = json_decode($response->getBody()->getContents(), true);

$this->assertArrayHasKey('customConditionalAttribute', $payload['data']['attributes']);
}

/** @test */
public function conditional_does_not_work_if_extension_is_disabled()
{
$this->extend(
(new Extend\Conditional())
->whenExtensionEnabled('dummy-extension-id', TestExtender::class)
);

$response = $this->send(
$this->request('GET', '/api', [
'authenticatedAs' => 1,
])
);

$payload = json_decode($response->getBody()->getContents(), true);

$this->assertArrayNotHasKey('customConditionalAttribute', $payload['data']['attributes']);
}
}

class TestExtender
{
public function __invoke(): array
{
return [
(new Extend\ApiSerializer(ForumSerializer::class))
->attributes(function () {
return [
'customConditionalAttribute' => true
];
})
];
}
}
Loading