Skip to content

Commit

Permalink
Added Hub class to group pipelines
Browse files Browse the repository at this point in the history
  • Loading branch information
lukewatts committed Mar 8, 2024
1 parent da0c406 commit f19a944
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 1 deletion.
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,3 +371,62 @@ App::get('/', function ($request) {
```

This way our controller stays clean, and readable, and each responsibility is separated to it's own class to make maintainance easier in the long run. This would also make testing easier, as you could test the individual classes, and also the overall pipeline result, without needing to test the controller itself.

## Hub

A `Hub` class, is a way to store a similar group of pipelines so they can be retrieved and executed from the same object.

```php
$app = AppFactory::create();
$userWorkflows = new Hub($app->getContainer());

// By default register the user
$userWorkflows->defaults(function ($pipeline, $passable) {
return $pipeline->send($passable)
->through([
ValidateRequest::class,
RegisterUser::class,
SendRegistrationEmail::class
])
->thenReturn();
});

$userWorkflows->pipeline('user-requested-reset-password', function ($pipeline, $passable) {
return $pipeline->send($passable)
->through([
ValidateRequestData::class,
ValidateUser::class,
EmailResetPasswordLink::class
])
->thenReturn();
});

$userWorkflows->pipeline('user-enabled-2fa', function ($pipeline, $passable) {
return $pipeline->send($passable)
->through([
ValidateRequestData::class,
ValidateUser::class,
Handle2faSetup::class
])
->thenReturn();
});

// Then we can call them easily like so
App::post('/user/register', function($request) use ($userWorkflows) {
$result = $userWorkflows->pipe($request); // Since our default is our register pipe we only need the first arg

return response()->json(['data' => $result])->get();
});

App::post('/user/password-reset', function($request) use ($userWorkflows) {
$result = $userWorkflows->pipe($request, 'user-requested-password-reset');

return response()->json(['data' => $result])->get();
});

App::post('/user/enable-2fa', function($request) use ($userWorkflows) {
$result = $userWorkflows->pipe($request, 'user-enabled-2fa');

return response()->json(['data' => $result])->get();
});
```
13 changes: 13 additions & 0 deletions src/Facades/Pipeline.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,21 @@
use SlimFacades\Support\Facade;
use SlimFacades\Support\Pipeline as PipelineBase;

/**
* @method \SlimFacades\Support\Pipeline send(mixed $passable)
* @method \SlimFacades\Support\Pipeline through(array|mixed $pipes)
* @method \SlimFacades\Support\Pipeline pipe(array|mixed $pipes)
* @method \SlimFacades\Support\Pipeline via(string $method)
* @method \SlimFacades\Support\Pipeline then(\Closure $destination)
* @method mixed thenReturn()
* @method \SlimFacades\Support\PipelineBase getContainer()
* @method \SlimFacades\Support\PipelineBase setContainer(ContainerInterface $container)
*/
class Pipeline extends Facade
{
/**
* @inheritDoc
*/
public static function getFacadeRoot()
{
return new PipelineBase(static::$app->getContainer());
Expand Down
94 changes: 94 additions & 0 deletions src/Support/Hub.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace SlimFacades\Support;

use Psr\Container\ContainerInterface;
use SlimFacades\Support\Pipeline;

class Hub
{
/**
* The container implementation.
*
* @var \Psr\Container\ContainerInterface|null
*/
protected $container;

/**
* All of the available pipelines.
*
* @var array
*/
protected $pipelines = [];

/**
* Create a new Hub instance.
*
* @param \Psr\Container\ContainerInterface|null $container
* @return void
*/
public function __construct(ContainerInterface $container = null)
{
$this->container = $container;
}

/**
* Define the default named pipeline.
*
* @param \Closure $callback
* @return void
*/
public function defaults(\Closure $callback)
{
return $this->pipeline('default', $callback);
}

/**
* Define a new named pipeline.
*
* @param string $name
* @param \Closure $callback
* @return void
*/
public function pipeline($name, \Closure $callback)
{
$this->pipelines[$name] = $callback;
}

/**
* Send an object through one of the available pipelines.
*
* @param mixed $object
* @param string|null $pipeline
* @return mixed
*/
public function pipe($object, $pipeline = null)
{
$pipeline = $pipeline ?: 'default';

return call_user_func($this->pipelines[$pipeline], new Pipeline($this->container), $object);
}

/**
* Get the container instance used by the hub.
*
* @return \Psr\Container\ContainerInterface
*/
public function getContainer()
{
return $this->container;
}

/**
* Set the container instance used by the hub.
*
* @param \Psr\Container\ContainerInterface $container
* @return $this
*/
public function setContainer(ContainerInterface $container)
{
$this->container = $container;

return $this;
}
}
2 changes: 1 addition & 1 deletion tests/Facades/PipelineTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function tearDown(): void
}


public function testPipelineMethodsExist()
public function testIsInstanceOfPipeline()
{
$this->assertInstanceOf(PipelineContract::class, Pipeline::getFacadeRoot());
}
Expand Down
78 changes: 78 additions & 0 deletions tests/Support/HubTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php declare(strict_types=1);

namespace SlimFacades\Tests\Support;

use DI\Bridge\Slim\Bridge;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use SlimFacades\Support\Hub;

final class HubTest extends TestCase
{
protected ContainerInterface $container;

public function setUp(): void
{
$app = Bridge::create();
$this->container = $app->getContainer();
}

public function tearDown(): void
{
unset($this->container);
}

public function testGetContainer()
{
$hub = new Hub($this->container);
$this->assertInstanceOf(ContainerInterface::class, $hub->getContainer());
}

public function testHubReceivesDefault()
{
$hub = new Hub($this->container);
$hub->defaults(function ($pipeline, $passable) {
return $pipeline->send($passable)
->through(PipelineEmpty::class)
->thenReturn();
});

$this->assertTrue($hub->pipe(true));
}

public function testHubReceivesNamedPipe()
{
$hub = new Hub($this->container);

$hub->pipeline('test-pipeline', function ($pipeline, $passable) {
return $pipeline->send($passable)
->through(PipelineEmpty::class)
->thenReturn();
});

$hub->defaults(function ($pipeline, $passable) {
return $pipeline->send($passable)
->through(PipelineFoo::class)
->thenReturn();
});

$this->assertEquals('foo', $hub->pipe('foo', 'test-pipeline'));
$this->assertEquals('foo', $hub->pipe('bar'));
}
}

class PipelineEmpty
{
public function handle($piped, $next)
{
return $next($piped);
}
}

class PipelineFoo
{
public function handle($piped, $next)
{
return $next('foo');
}
}

0 comments on commit f19a944

Please sign in to comment.