PHPackages                             affinity4/slimphp-support - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. affinity4/slimphp-support

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

affinity4/slimphp-support
=========================

Add Laravel style facades, traits and helper functions to any SlimPHP app

0.10.1(2y ago)415MITPHPPHP ^8.1

Since Mar 6Pushed 2y ago1 watchersCompare

[ Source](https://github.com/affinity4/slimphp-support)[ Packagist](https://packagist.org/packages/affinity4/slimphp-support)[ RSS](/packages/affinity4-slimphp-support/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (7)Versions (15)Used By (0)

SlimPHP Support
===============

[](#slimphp-support)

Add Laravel style facades, traits and helper functions to any SlimPHP app

Installation
------------

[](#installation)

```
composer require affinity4/slimphp-support
```

Usage
-----

[](#usage)

### Setting up Facades in your Application

[](#setting-up-facades-in-your-application)

To use SlimPHP Facades, you first need to create your Slim app as normal, with either `Slim\App\AppFactory` or `DI\Container\Slim\Bridge`. Then you'll need to call `Affinity4\SlimSupport\Support\Facade::setFacadeApplication($app)`:

```
use Slim\Factory\AppFactory;
use Affinity4\SlimSupport\Support\Facade;

$app = AppFactory::createFromContainer();
Facade::setFacadeApplication($app);
```

You will now have access to all Facades, as well as the helper function (e.g. `response()`)

### App Facade

[](#app-facade)

Facade for `Slim\App`:

```
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Slim\Factory\AppFactory;
use Affinity4\SlimSupport\Support\Facade;

$app = AppFactory::createFromContainer();
Facade::setFacadeApplication($app);

App::get('/', function(RequestInterface $request, ResponseInterface $response) {
    // return ...
});

App::run();
```

### Container

[](#container)

```
use Affinity4\SlimSupport\Facades\Container;

Container::set('some-service', function () {
    return SomeService();
});

if (Container::has('some-service')) {
    $someService = Container::get('some-service');
}
```

### Response

[](#response)

#### JSON Response

[](#json-response)

```
use Affinity4\SlimSupport\Facades\Container;

App::get('/', function($request) {
    return Response::json(['test' => 'payload'])->get();
});
```

### Pipeline Facade

[](#pipeline-facade)

**NOTE: See [Pipeline Support Class](#pipeline-support-class) section for a detailed example.**

```
App::get('/', function ($request) {
    // 4. Define the pipeline
    $result = (new Pipeline(App::getContainer()))
        ->send($request)
        ->through([
            PrepareRequest::class,
            ValidateRequest::class,
            TransformRequest::class,
            SaveRequest::class,
        ])
        ->thenReturn();

    // 5. Respond with the processed data
    return response()->json(['result' => $result])->get();
});
```

Helper functions
----------------

[](#helper-functions)

### response()

[](#response-1)

#### Standard application/text Response

[](#standard-applicationtext-response)

```
App::get('/', function ($request) {
    return response('Hello World')->get();
});
```

#### Standard JSON Response

[](#standard-json-response)

```
App::get('/', function ($request) {
    return response()->json(['data' => 'payload'])->get();
});
```

### tap()

[](#tap)

```
return tap(new Psr7Response(), function ($response) {
    $response->getBody()->write('foo');
});
```

Traits
------

[](#traits)

### Tappable

[](#tappable)

```
use Affinity4\SlimSupport\Support\Traits\Tappable;

class TappableClass
{
    use Tappable;

    private $name;

    public static function make()
    {
        return new static;
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }
}

$name = TappableClass::make()->tap(function ($tappable) {
    $tappable->setName('MyName');
})->getName();

// Or, even though setName does not return this you can now just chain from it!
$name = TappableClass::make()->tap()->setName('MyName')->getName()
```

### Macroable

[](#macroable)

Macros allow you to add methods to classes dynamically (without having to modify their code).

Let's say you are tired of having to do this:

```
$app->get('/', function ($request, $response) {
    $response = new Response;
    $response->getBody()->write('Hello');

    return $response;
})
```

Instead you just want to call a write method directly from the `$response` instance. First, we need to extend the Response class so we can use the `Macroable` trait, but still have all of our base Response methods.

```
use GuzzleHttp\Psr7\Response;
use Affinity4\SlimSupport\Support\Traits\Macroable;

class MacroableResponse extends Response
{
    use Macroable;
}
```

Then we need to add `MacroableResponse` to our container, so we are always dealing with the same instance (not all instances will have the "macroed" methods).

```
use Affinity4\SlimSupport\Facades\Container;
// ... above code here

Container::set('response', function () {
    return new MacroableResponse();
});
```

Then we can get our `MacroableResponse` instance from the container however you want, and just call `write`!

```
App::get('/', function () {
   return Container::get('response')->write('Macro!');
});
```

### Conditionable

[](#conditionable)

Allows to conditionally chain functionality.

For example, let's imagine we have a standard PSR-11 Container, which has a the bare minimum PSR-11 compliant methods, `set`, `get` and `has`. The `set` method adds a service to the container, `get` returns the service and `has` checks an service is in the container.

We have a `Logger` we want to add to the container, but it requires a `FileDriver` to be in the container already, or else we need to also add the `FileDriver` class to the container first.

We might then have some bootstrapping logic like so:

```
$container = new Container;

if (!$container->has('FileDriver')) {
    $container->set('FileDriver', fn() => new FileDriver);
}

if (!$container->has('Logger')) {
    $container->set('Logger', function ($container) {
        $logger = new Logger;
        $logger->setDriver($container->get('FileDriver'));
        return $logger;
    });
}
```

However, if we extends our `Container` class and add the `Conditionable` trait, we can instead use the `unless` method to do this check with a fluent interface:

**NOTE: To check the opposite, there is also `when`.**

```
class ConditionableContainer extends Container
{
    use Conditionable;
}

$container = new ConditionableContainer;
$container
    ->unless(
        fn($container) => $container->has('FileDriver'),
        function ($container) {
            $container->set('FileDriver', fn() => new FileDriver);
        }
    )->unless(
        fn($container) => $container->has('Logger'),
        function ($container) {
            $container->set('Logger', function ($container) {
                $logger = new Logger;
                $logger->setDriver($container->get('FileDriver'));
                return $logger;
            });
        }
    );
```

You're probably thinking this is still quite bit verbose, so to clean this up you could create `invokable` ServiceFactory classes for all of your `$container->set` logic.\_\_

```
class FileDriverServiceFactory
{
    public function __invoke($container)
    {
        $container->set('FileDriver', fn() => new FileDriver);
    }
}

class LoggerServiceFactory
{
    public function __invoke($cotnainer)
    {
        $logger = new Logger;
        $logger->setDriver($container->get('FileDriver'));
        return $logger;
    }
}

$container = new ConditionableContainer;

// or, using unless, instead of when
$container
    ->unless(fn($container) => $container->has('FileDriver'), FileDriverServiceFactory($container))
    ->unless(fn($container) => $container->has('Logger'), LoggerServiceFactory($container));
```

### Dumpable

[](#dumpable)

Adds `dump` and `dd` methods to any class

```
class Collection
{
    use Dumpable;

    public function __constructor(
        protected array $collection = []
    ) {}
}

$collection = new Collection([
    "one" => 1,
    "two" => 2
]);

// Debug the collection...
$collection->dump();
// Or
$collection->dd();
```

Would result in:

```
DumpableCollection {#69 ▼
  #collection: array:1 [▼
    "one" => 1,
    "two" => 2
  ]
}
```

**NOTE: You can also pass `...$args` to the dd and dump methods as normal if you want to append additional dump data.**

### ForwardsCalls

[](#forwardscalls)

Proxy calls to missing methods in current class, to another target class. Useful when you cannot inherit or modify a class but you want to add some functionality to it (other than overloading any of it's methods of course).

Here's an example where we have a base `App` class, but it is a final class so we cannot inherit it. So instead, we create an `AppProxy` class which allows us to say that "any method that gets called on `AppProxy` which doesn't exist in `AppProxy`, we use `App` instead"

```
class AppProxy
{
    use ForwardsCalls;

    public function __call($method, $parameters)
    {
        return $this->forwardCallTo(new App, $method, $parameters);
    }

    public function addSomeServiceDirectlyToContainer()
    {
        $this->getContainer()->set('some-service', function ($container) {
            return new SomeService($container->get('some-dependency-already-in-container'));
        });
    }
}

final class App
{
    public function __construct(
        protected ContainerInterface $container
    ) {}

    public function getContainer()
    {
        return $this->container;
    }
}
```

Then we can use `getContainer` (or any other public methods/properties) from `App` by calling out `AppProxy`

```
$appProxy = new AppProxy;
$app->addSomeServiceDirectlyToContainer();
$container = $appProxy->getContainer();
dd($congainer->get('some-service'));
/*
SomeService {# 46
    # some_service_already_in_container: someServiceAlreadyInContainer {# 30 }
    ...
}
*/
```

Pipeline Support class
----------------------

[](#pipeline-support-class)

Pipelines allow for a middleware-like interface to chain processing of tasks.

A pipeline processes each task, passed the returned value to the next process in the chain.

They are useful for multi-step data processing, http middleware, database querying and validation tasks.

Here's an example of how to use it to validation, filter, transform and save an incoming get request.

```
// 1. Prepare the request
class PrepareRequest
{
    public function handle($request, $next)
    {
        $uri = $request->getUri();
        $query = $uri->getQuery(); // Get the query string (e.g., "param1=value1&param2=value2")
        parse_str($query, $queryParams); // Parse the query string into an array

        return $next($queryParams);
    }
}

// 2. Validate the request
class ValidateRequest
{
    public function handle($data, $next)
    {
        // Validate parameters
        // (e.g. check if 'email' and 'password' exist, validate 'email' and 'password' etc)

        // If invalid then $data['valid'] = false, else $data['valid'] = true;

        return $next($data);
    }
}

// 2. Transform the request
class TransformRequest
{
    public function handle($data, $next)
    {
        $data['password'] = bcrypt($data['password']);

        return $next($data);
    }
}

// 3. Save the data, or log errors
class SaveRequest
{
    public function handle($data, $next)
    {
        if (!$data['valid']) {
            // Log errors...

            return $next($data);
        }

        $data['saved'] = true;

        return $next($data);
    }
}

App::get('/', function ($request) {
    // 4. Define the pipeline
    $result = (new Pipeline(App::getContainer()))
        ->send($request)
        ->through([
            PrepareRequest::class,
            ValidateRequest::class,
            TransformRequest::class,
            SaveRequest::class,
        ])
        ->thenReturn();

    // 5. Respond with the processed data
    return response()->json(['result' => $result])->get();
});
```

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
---

[](#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.

```
$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();
});
```

###  Health Score

23

—

LowBetter than 27% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity10

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

14

Last Release

796d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/6eda764179ed00e7d50099142dc56015a0a5a44542d6df9bb80fe087418efceb?d=identicon)[Affinity4](/maintainers/Affinity4)

---

Top Contributors

[![lukewatts](https://avatars.githubusercontent.com/u/4622166?v=4)](https://github.com/lukewatts "lukewatts (26 commits)")

---

Tags

facadeshelpersphpslim-frameworkslim4traits

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/affinity4-slimphp-support/health.svg)

```
[![Health](https://phpackages.com/badges/affinity4-slimphp-support/health.svg)](https://phpackages.com/packages/affinity4-slimphp-support)
```

###  Alternatives

[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

728272.9k20](/packages/civicrm-civicrm-core)[cognesy/instructor-php

The complete AI toolkit for PHP: unified LLM API, structured outputs, agents, and coding agent control

310107.9k1](/packages/cognesy-instructor-php)[bakame/laravel-domain-parser

Laravel package to integrate PHP Domain parser.

26534.8k4](/packages/bakame-laravel-domain-parser)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

245.2k](/packages/aedart-athenaeum)[shopware/app-php-sdk

Shopware App SDK for PHP

1577.8k1](/packages/shopware-app-php-sdk)[azuracast/nowplaying

A lightweight PHP adapter for viewing the current now playing data in Icecast and SHOUTcast 1/2. A part of the AzuraCast software suite.

3044.5k1](/packages/azuracast-nowplaying)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
