Skip to content

Commit

Permalink
[7.x] Add Orchestra\Testbench\Support\FluentDecorator (#280)
Browse files Browse the repository at this point in the history
* [7.x] Add `Orchestra\Testbench\Support\FluentDecorator`

Signed-off-by: Mior Muhammad Zaki <crynobone@gmail.com>

* wip

Signed-off-by: Mior Muhammad Zaki <crynobone@gmail.com>

* wip

Signed-off-by: Mior Muhammad Zaki <crynobone@gmail.com>

* wip

Signed-off-by: Mior Muhammad Zaki <crynobone@gmail.com>

* wip

Signed-off-by: Mior Muhammad Zaki <crynobone@gmail.com>

* wip

Signed-off-by: Mior Muhammad Zaki <crynobone@gmail.com>

* wip

Signed-off-by: Mior Muhammad Zaki <crynobone@gmail.com>

---------

Signed-off-by: Mior Muhammad Zaki <crynobone@gmail.com>
  • Loading branch information
crynobone authored Dec 15, 2024
1 parent 393fad7 commit 61889d8
Show file tree
Hide file tree
Showing 3 changed files with 256 additions and 12 deletions.
43 changes: 31 additions & 12 deletions src/Foundation/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
namespace Orchestra\Testbench\Foundation;

use Illuminate\Support\Arr;
use Illuminate\Support\Fluent;
use Illuminate\Support\LazyCollection;
use Orchestra\Testbench\Contracts\Config as ConfigContract;
use Orchestra\Testbench\Support\FluentDecorator;
use Symfony\Component\Yaml\Yaml;

use function Orchestra\Testbench\join_paths;
Expand Down Expand Up @@ -100,7 +100,7 @@
* workbench?: TOptionalWorkbenchConfig|null
* }
*/
class Config extends Fluent implements ConfigContract
class Config extends FluentDecorator implements ConfigContract
{
/**
* All of the attributes set on the fluent instance.
Expand All @@ -109,7 +109,7 @@ class Config extends Fluent implements ConfigContract
*
* @phpstan-var TConfig
*/
protected $attributes = [
protected $defaultAttributes = [
'laravel' => null,
'env' => [],
'providers' => [],
Expand Down Expand Up @@ -185,6 +185,18 @@ class Config extends Fluent implements ConfigContract
*/
protected static $cachedConfiguration;

/**
* Construct a new Config instance.
*
* @param iterable<string, mixed> $attributes
*
* @phpstan-param TOptionalConfig $attributes
*/
public function __construct($attributes = [])
{
parent::__construct(array_replace($this->defaultAttributes, $attributes));
}

/**
* Load configuration from Yaml file.
*
Expand Down Expand Up @@ -256,7 +268,7 @@ public static function cacheFromYaml(string $workingPath, ?string $filename = 't
*/
public function addProviders(array $providers)
{
$this->attributes['providers'] = array_unique(array_merge($this->attributes['providers'], $providers));
$this->fluent['providers'] = array_unique(array_merge($this->fluent['providers'], $providers));

return $this;
}
Expand All @@ -270,11 +282,13 @@ public function addProviders(array $providers)
*/
public function getExtraAttributes(): array
{
$attributes = $this->fluent->getAttributes();

return [
'env' => Arr::get($this->attributes, 'env', []),
'bootstrappers' => Arr::get($this->attributes, 'bootstrappers', []),
'providers' => Arr::get($this->attributes, 'providers', []),
'dont-discover' => Arr::get($this->attributes, 'dont-discover', []),
'env' => Arr::get($attributes, 'env', []),
'bootstrappers' => Arr::get($attributes, 'bootstrappers', []),
'providers' => Arr::get($attributes, 'providers', []),
'dont-discover' => Arr::get($attributes, 'dont-discover', []),
];
}

Expand All @@ -287,10 +301,13 @@ public function getExtraAttributes(): array
*/
public function getPurgeAttributes(): array
{
return array_merge(
$config = array_merge(
$this->purgeConfig,
$this->attributes['purge'],
$this->fluent['purge'],
);

/** @var TPurgeConfig $config */
return $config;
}

/**
Expand All @@ -302,14 +319,16 @@ public function getPurgeAttributes(): array
*/
public function getWorkbenchAttributes(): array
{
$attributes = $this->fluent->getAttributes();

$config = array_merge(
$this->workbenchConfig,
$this->attributes['workbench'],
$attributes['workbench'],
);

$config['discovers'] = array_merge(
$this->workbenchDiscoversConfig,
Arr::get($this->attributes, 'workbench.discovers', [])
Arr::get($attributes, 'workbench.discovers', [])
);

/** @var TWorkbenchConfig $config */
Expand Down
169 changes: 169 additions & 0 deletions src/Support/FluentDecorator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<?php

namespace Orchestra\Testbench\Support;

use ArrayAccess;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Support\Fluent;
use Illuminate\Support\Traits\ForwardsCalls;
use JsonSerializable;

/**
* @template TKey of array-key
* @template TValue
*
* @implements \Illuminate\Contracts\Support\Arrayable<TKey, TValue>
* @implements \ArrayAccess<TKey, TValue>
*/
abstract class FluentDecorator implements Arrayable, ArrayAccess, Jsonable, JsonSerializable
{
use ForwardsCalls;

/**
* The Fluent instance.
*
* @var \Illuminate\Support\Fluent<TKey, TValue>
*/
protected Fluent $fluent;

/**
* Create a new fluent instance.
*
* @param iterable<TKey, TValue> $attributes
*/
public function __construct($attributes = [])
{
$this->fluent = new Fluent($attributes);
}

/**
* Convert the fluent instance to an array.
*
* @return array<TKey, TValue>
*/
public function toArray()
{
return $this->fluent->getAttributes();
}

/**
* Convert the object into something JSON serializable.
*
* @return array<TKey, TValue>
*/
public function jsonSerialize(): array
{
return $this->toArray();
}

/**
* Convert the fluent instance to JSON.
*
* @param int $options
* @return string
*/
public function toJson($options = 0)
{
return (string) json_encode($this->jsonSerialize(), $options);
}

/**
* Determine if the given offset exists.
*
* @param TKey $offset
*/
public function offsetExists($offset): bool
{
return $this->fluent->offsetExists($offset);
}

/**
* Get the value for a given offset.
*
* @param TKey $offset
* @return TValue|null
*/
public function offsetGet($offset): mixed
{
return $this->fluent->offsetGet($offset);
}

/**
* Set the value at the given offset.
*
* @param TKey $offset
* @param TValue $value
*/
public function offsetSet($offset, $value): void
{
$this->fluent->offsetSet($offset, $value);
}

/**
* Unset the value at the given offset.
*
* @param TKey $offset
*/
public function offsetUnset($offset): void
{
$this->fluent->offsetUnset($offset);
}

/**
* Handle dynamic calls to the fluent instance to set attributes.
*
* @param TKey $method
* @param array{0: ?TValue} $parameters
* @return $this
*/
public function __call($method, $parameters)
{
return $this->forwardDecoratedCallTo($this->fluent, $method, $parameters);
}

/**
* Dynamically retrieve the value of an attribute.
*
* @param TKey $key
* @return TValue|null
*/
public function __get($key)
{
return $this->fluent->get($key);
}

/**
* Dynamically set the value of an attribute.
*
* @param TKey $key
* @param TValue $value
* @return void
*/
public function __set($key, $value)
{
$this->fluent->offsetSet($key, $value);
}

/**
* Dynamically check if an attribute is set.
*
* @param TKey $key
* @return bool
*/
public function __isset($key)
{
return $this->fluent->offsetExists($key);
}

/**
* Dynamically unset an attribute.
*
* @param TKey $key
* @return void
*/
public function __unset($key)
{
$this->fluent->offsetUnset($key);
}
}
56 changes: 56 additions & 0 deletions tests/Support/FluentDecoratorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Orchestra\Testbench\Tests\Support;

use Illuminate\Foundation\Application;
use Orchestra\Testbench\Support\FluentDecorator;
use PHPUnit\Framework\TestCase;

class FluentDecoratorTest extends TestCase
{
/** @test */
public function it_can_be_utilise_fluent_features()
{
$fluent = new class($attributes = ['testbench' => true, 'class' => __CLASS__]) extends FluentDecorator
{
// ...
};

$this->assertTrue(isset($fluent['testbench']));
$this->assertTrue(isset($fluent['class']));
$this->assertFalse(isset($fluent['workbench']));

$this->assertTrue($fluent['testbench']);
$this->assertNull($fluent['workbench']);

$this->assertSame($attributes, $fluent->getAttributes());
$this->assertSame($attributes, $fluent->toArray());
$this->assertSame(json_encode($attributes), $fluent->toJson());
$this->assertSame($attributes, $fluent->jsonSerialize());

$this->assertFalse(isset($fluent['laravel']));
$this->assertNull($fluent['laravel']);

$this->assertInstanceOf(FluentDecorator::class, $fluent->laravel(Application::VERSION));

$this->assertTrue(isset($fluent['laravel']));
$this->assertSame(Application::VERSION, $fluent['laravel']);

unset($fluent['class']);

$this->assertFalse(isset($fluent['class']));
$this->assertFalse(isset($fluent->class));
$this->assertNull($fluent['class']);
$this->assertNull($fluent->class);

$this->assertFalse(isset($fluent->file));

$fluent->file = __FILE__;

$this->assertTrue(isset($fluent->file));

unset($fluent->file);

$this->assertFalse(isset($fluent->file));
}
}

0 comments on commit 61889d8

Please sign in to comment.