PHPackages                             deefour/interactor - 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. deefour/interactor

ActiveLibrary

deefour/interactor
==================

Simple Service Objects

2.0.0(9y ago)3648.1k↓48.6%4[1 PRs](https://github.com/deefour/interactor/pulls)MITPHPPHP &gt;=5.6.0

Since Oct 2Pushed 8y ago5 watchersCompare

[ Source](https://github.com/deefour/interactor)[ Packagist](https://packagist.org/packages/deefour/interactor)[ Docs](https://github.com/deefour/interactor)[ RSS](/packages/deefour-interactor/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (3)Versions (26)Used By (0)

Interactor
==========

[](#interactor)

[![Build Status](https://camo.githubusercontent.com/f53a3bf9520d8bf9943e5d0c91f955ca8ae8e8f19729826ac7ec0089817571d6/68747470733a2f2f7472617669732d63692e6f72672f646565666f75722f696e7465726163746f722e737667)](https://travis-ci.org/deefour/interactor)[![Total Downloads](https://camo.githubusercontent.com/837b492eb71f9263f88e93c4c458e2a7e3df846d76108e41c9903d4c85849869/68747470733a2f2f706f7365722e707567782e6f72672f646565666f75722f696e7465726163746f722f642f746f74616c2e737667)](https://packagist.org/packages/deefour/interactor)[![Latest Stable Version](https://camo.githubusercontent.com/80b4da25b0dc7684cc91845f0c6400cece7db2f75f01566adc1d930e76cc4757/68747470733a2f2f706f7365722e707567782e6f72672f646565666f75722f696e7465726163746f722f762f737461626c652e737667)](https://packagist.org/packages/deefour/interactor)[![License](https://camo.githubusercontent.com/94fcf43d5c37aa7a8033bd9b96489ce04839071d3b8652a69c07e2f1f3b39e9f/68747470733a2f2f706f7365722e707567782e6f72672f646565666f75722f696e7465726163746f722f6c6963656e73652e737667)](https://packagist.org/packages/deefour/interactor)

Simple PHP Service Objects. Inspired by [collectiveidea/**interactor**](https://github.com/collectiveidea/interactor).

Getting Started
---------------

[](#getting-started)

Run the following to add Interactor to your project's `composer.json`. See [Packagist](https://packagist.org/packages/deefour/interactor) for specific versions.

```
composer require deefour/interactor
```

**`>=PHP5.6.0` is required.**

What is an Interactor
---------------------

[](#what-is-an-interactor)

An interactor is a simple, single-purpose object.

Interactors are used to encapsulate your application's [business logic](http://en.wikipedia.org/wiki/Business_logic). Each interactor represents one thing that your application does.

An interactor

1. Extends `Deefour\Interactor\Interactor`
2. Implements a `call()` method

As a simple example, the below interactor creates a new `Car`.

```
use Deefour\Interactor\Interactor;

class CreateCar extends Interactor
{
    public function call()
    {
        $c = $this->context();

        $c->car = new Car([ 'make' => $c->make, 'model' => $c->model ]);

        if ( ! $c->car->save()) {
            $this->fail('Creating the car failed!');
        }
    }
}
```

Context
-------

[](#context)

An interactor runs based on a given context. The context contains the information the interactor needs to do its work. An interactor may affect its passed context, providing data from within the interactor back to the caller.

All contexts extend the `Deefour\Transformer\MutableTransformer` from the [`deefour/transformer`](https://github.com/deefour/transformer) package. The `MutableTransformer` provides conveient access and mutation of the underlying data, including but not limited to implementations of `ArrayAccess` and `JsonSerializable`.

### Accessing the Context

[](#accessing-the-context)

An interactor's context can be accessed via the `context()` method.

```
$this->context();
$this->context()->make; //=> 'Honda'
```

### Modifying the the Context

[](#modifying-the-the-context)

An interactor can add or modify the context.

```
$this->context()->car = new Car;
```

This can be very useful to provide data back to the caller.

### Permitted Attributes

[](#permitted-attributes)

Performing safe mass assignment is easy thanks to the `MutableTransformer`'s `only()` method.

```
$car       = new Car;
$permitted = $this->context()->only($this->car->getFillable());

$car->fill($permitted);

$car->save();
```

### Specific Context Requirements

[](#specific-context-requirements)

The default context constructor expects a single array of attributes as key/value pairs.

```
public function __construct(array $attributes = [])
{
    $this->attributes = $attributes;
}
```

An interactor often requires specific data from the provided context. For example, a `CreateCar` interactor might expect to assign an owner to the `Car` it creates. A context class should be created specifically for this Interactor requiring a user be provided during instantiation.

```
use Deefour\Interactor\Context;

class CarContext extends Context
{
    /**
     * The owner of the vehicle.
     *
     * @var User
     */
    public $user;

    /**
     * Constructor.
     *
     * @param User  $user
     * @param array $attributes
     */
    public function __construct(User $user, array $attributes = [])
    {
        $this->user = $user;

        parent::__construct($attributes);
    }
}
```

The `CreateCar` interactor should expect an instance of this new `CarContext` during instantiation

```
public function __construct(CarContext $context)
{
    parent::__construct($context);
}
```

### The Context Factory

[](#the-context-factory)

While instantiating contexts manually is the norm, the `ContextFactory` is a useful alternative in certain circumstances. Pass a fully qualified name of the context to be instantiated along with a set of attributes/parameters to the `create()` method.

```
use App\User;
use Deefour\Interactor\ContextFactory;

$user       = User::find(34);
$attributes = [ 'make' => 'Honda', 'model' => 'Accord' ];

$context = ContextFactory::create(CarContext::class, compact('user', 'attributes'));

$context->user->id; //=> 34
$context->make;     //=> Honda
```

Explicitly specifying an `'attributes'` parameter isn't necessary. Any keys in the array of source data passed to the factory that do not match the name of a parameter on the constructor will be pushed into an `$attributes` parameter. If you provide an `'attributes'` parameter manually in addition to extra data, the extra data will be merged into the `$attributes` array.

> **Note:** Taking advantage of this requires an `$attributes` parameter be available on the constructor of the context class being instantiated through the factory.

```
use Deefour\Interactor\ContextFactory;

$user = User::find(34);
$data = [ 'make' => 'Honda', 'model' => 'Accord' ];

$context = ContextFactory::create(CarContext::class, array_merge(compact('user'), $data));

$context->make;  //=> Toyota
$context->model; //=> Accord
$context->foo;   //=> bar
```

Status
------

[](#status)

The state of an interactor is considered passing or failing. A context holds the current state in a `$status` proprerty. This library provides a `Success` and `Error` status. Contexts are given a `Success` status by default.

### Failing the Context

[](#failing-the-context)

An interactor can be considered a failure when something goes wrong during it's execution. This is done by marking the context as having failed.

```
$this->context()->fail();
```

A message can be provided to explain the reason for the failure.

```
$this->context()->fail('Some explicit error message here');
```

**Failing a context causes a `Deefour\Interactor\Exception\Failure` exception to be thrown.**

If an exception is provided it will be thrown after copying it's message over to the `Error` status set on the context.

```
try {
    $this->context()->fail(new CarCreationException('Invalid make/model combination'));
} catch (CarCreationException $e) {
    (string)$this->context()->status(); //=> Invalid make/model combination
}
```

### Checking the Status

[](#checking-the-status)

You can ask if the state is currently successful/passing.

```
$c = $this->context();

$c->ok();     //=> true
$c->status(); //=> Deefour\Interactor\Status\Success

try {
    $c->fail('Oops!');
} catch (\Deefour\Interactor\Exception\Failure $e) {
    $c->ok();             //=> false
    $c->status();         //=> Deefour\Interactor\Status\Error
    (string)$c->status(); //=> Oops!
}
```

Usage
-----

[](#usage)

Within a controller, implementing the car creation through the `CreateCar` interactor might look like this.

```
public function create(CreateRequest $request)
{
    $context = new CarContext($request->user(), $request->only('make', 'model'));

    (new CreateCar($context))->call();

    if ($context->ok()) {
        echo 'Wow! Nice new ' . $context->car->make;
    } else {
        echo 'ERROR: ' . $context->status()->error();
    }
}
```

### Dispatching Interactors

[](#dispatching-interactors)

A `Deefour\Interactor\DispatchesInteractors` trait can be included in any class to reduce the creation and execution of an interactor to a single method call. In the example below, this trait will

1. Resolve a new `CarContext` instance using the provided `User`, `'make'`, and `'model'`
2. Instantiate a new `CreateCar` instance with the newly created `CarContext`
3. Execute the `call()` method on the interactor
4. Return the context

```
namespace App\Controllers;

use App\Interactors\CreateCar;
use App\Contexts\CarContext;
use Deefour\Interactor\DispatchesInteractors;

class CarController extends BaseController
{
    use DispatchesInteractors;

    /**
     * Create a new car.
     *
     * @param  Request $request
     * @return string
     */
    public function store(Request $request)
    {
        $context = $this->dispatchInteractor(
            CreateCar::class,
            CarContext::class,
            array_merge([ 'user' => $request->user() ], $request->only('make', 'model'))
        );

        if ($context->ok()) {
            return 'Wow! Nice new ' . $context->car->make;
        } else {
            return 'ERROR: ' . $context->status()->error();
        }
    }
}
```

Organizers
----------

[](#organizers)

Complex scenarios may require the use of multiple interactors in sequence. If a registration form asks for a user's email, password, and VIN of their car, the submission might register a new user account and create a new vehicle for the user based on the VIN. These two actions are best broken up into `CreateUser` and `CreateVehicle` interactors. An organizer can be used to manage the execution of these interactors.

### Combining Interactors via an Organizer

[](#combining-interactors-via-an-organizer)

To create an organizer, extend `Deefour\Interactor\Organizer` and implement an `organize()` method that pushes interactors onto the queue. The `call()` method for an organizer is implemented by the library. Like a standard interactor, an organizer can require a specific context by type-hinting the constructor.

```
use Deefour\Interactor\Organizer;

class RegisterUser extends Organizer
{
    public function __construct(RegisterUserContext $context)
    {
        parent::__construct($context);
    }

    public function organize()
    {
        $this->enqueue(function ($context) {
            return new CreateUser(
                new CreateUserContext($context->user['first_name'], $context->user['last_name'])
            );
        });

        $this->enqueue(function ($context, $previous) {
            return new CreateVehicle(
                new CreateVehicleContext($previous->user, $context->vin)
            );
        });
    }
}
```

The `enqueue()` method accepts a `callable` which should return an interactor instance. The callables are executed in a first-in-first-out manner. Each callable receives the organizer's context along with the context of the previously executed interactor.

The deferred instantiation allows for information only available after the execution of a previous interactor to be used when creating the current interactor.

### Executing an Organizer

[](#executing-an-organizer)

An organizer is executed like any other interactor. Call the `call()` method to kick things off after instantiation.

```
$params = [
    'user' => [
        'first_name' => 'Jason',
        'last_name'  => 'Daly',
    ],
    'vin' => 'VINNUMBERHERE',
];

$context = new RegisterUserContext($params['user'], $params['vin']);

(new RegisterUser($context))->call();
```

### Organizer Failure and Rollback

[](#organizer-failure-and-rollback)

If a failure occurs during the execution of an organizer, `rollback()` will be called on each interactor that ran successfully prior to the failure, in reverse order. Override the empty `rollback()` method on `Deefour\Interactor\Interactor` to take advantage of this.

> **Note:** The `rollback()` method is **not** called when an interactor is executed on it's own, though it can be called manually by testing for failure on the context.

### Integration With Laravel 5

[](#integration-with-laravel-5)

A job in Laravel 5 can be treated as in interactor or organizer. The `handle()` method on a job called through Laravel's job dispatcher supports dependency injection through the [service container](http://laravel.com/docs/master/container). An implementation of the `CreateCar` interactor that takes advantage of dependency injection and Laravel's job dispatcher might look like this:

```
namespace App\Jobs;

use App\Car;
use App\Contexts\CreateCarContext as CarContext;
use Deefour\Interactor\Interactor;
use Illuminate\Contracts\Redis\Database as Redis;

class CreateCar extends Interactor
{
    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct(CarContext $context)
    {
        parent::__construct($context);
    }

    /**
     * Execute the command.
     *
     * @return void
     */
    public function handle(Redis $redis)
    {
        $c      = $this->context();
        $c->car = Car::create($c->only('make', 'model'));

        $redis->publish('A new' . (string)$c->car . ' was just added to the lot!');

        return $this->context();
    }
}
```

Laravel needs to be told to to use it's own `dispatch()` method instead of the one provided by this library. This allows both interactors and vanilla Laravel jobs can be dispatched.

```
namespace App\Http\Controllers;

use Illuminate\Foundation\Bus\DispatchesJobs;
use Deefour\Interactor\DispatchesInteractors;

class Controller
{
    use DispatchesJobs, DispatchesInteractors {
      DispatchesJobs::dispatch insteadof DispatchesInteractors;
    }
}
```

> **Note:** Interactors can even implement Laravel's `Illuminate\Contracts\Queue\ShouldQueue` to have execution deferred!

Contribute
----------

[](#contribute)

- Issue Tracker:
- Source Code:

Changelog
---------

[](#changelog)

#### 2.0.0 - January 26, 2017

[](#200---january-26-2017)

- Rewritten API for organizers.

#### 1.2.0 - October 16, 2016

[](#120---october-16-2016)

- Exceptions can now be passed to a context's `fail()` method. A passed exception will be thrown instead of a new `Deefour\Interactor\Exception\Failure`. Thanks to [@gpassarelli](https://github.com/gpassarelli) in [\#6](https://github.com/deefour/interactor/pull/6).

#### 1.1.0 - October 19, 2015

[](#110---october-19-2015)

- `call()` is no longer abstract; it's left as a blank stub to be overridden. This eliminates the need to define `call()` when another method is being used for business logic *(ie. when using a `handle()` to work with Laravel's command bus)*.
- **Breaking Chang** The `DispatchesInteractors` trait no longer resolves interactors through the service container.
    - The trait can now be used outside the context of Laravel. A basic `dispatch()` method has been implemented to execute the `call()` method on the interactor.
    - Within the context of Laravel, all dependency injection should be done on the `handle()` method, just like any other Laravel job. **Constructor injection is no longer supported.**
    - Laravel must be told to use the `Illuminate\Foundation\Bus\DispatchesJobs::dispatch()` method when using both Laravel's job dispatcher and this library's interactor dispatcher. See **[Integration with Laravel](#integration-with-laravel-5)** above for more information.

#### 1.0.0 - October 7, 2015

[](#100---october-7-2015)

- Release 1.0.0.

#### 0.7.0 - June 21, 2015

[](#070---june-21-2015)

- New `Organizer` and `CompositeContext` for grouping interactors together.

#### 0.6.2 - June 5, 2015

[](#062---june-5-2015)

- Now following PSR-2.

#### 0.6.0 - May 30, 2015

[](#060---may-30-2015)

- New `ContextFactory` for creating context objects.
- Now has `deefour/transformer` as dependency.
- `Context` now exteds `MutableTransformer`. This class no longer implements `ArrayAccess` directly.
- `attributes()` method on `Context` has been removed. Use `all()` or `raw()` *(for non-transformed version of attributes)* instead.
- `Interactor` has been simplified, using only type-hints to enforce proper context for an interactor.

#### 0.5.0 - May 25, 2015

[](#050---may-25-2015)

- Now suggesting `deefour/transformer` be required. If available, the context will be wrapped in a `MutableTransformer`, providing all the functionality available in [`deefour/transformer`](https://github.com/deefour/transformer) transparently on the context object.
- New `__isset()` implementation and better support for null context values.
- Improved code formatting.

#### 0.4.4 - February 20, 2015

[](#044---february-20-2015)

- Added `permit()` method to provide a watered down version of [rails/strong\_parameters](https://github.com/rails/strong_parameters) whitelisting against the contents of a `Context`.

#### 0.4.0 - February 1, 2015

[](#040---february-1-2015)

- Move much of the API out of the interactor and into the context
- Changed `perform()` to `call()`
- Add new trait with `dispatchInteractor()` method.

#### 0.3.0 - January 3, 2015

[](#030---january-3-2015)

- Refactor, striping out dependency on and support for Illuminate components.
- Compatibility changes to work easily with Laravel 5's new command bus and event handlers.
- Inverting resolution lookup; contexts now resolve interactors instead of the other way around.

#### 0.2.0 - October 7, 2014

[](#020---october-7-2014)

- Automatic context resolution from instantiated interactor.

#### 0.1.0 - October 2, 2014

[](#010---october-2-2014)

- Initial release

License
-------

[](#license)

Copyright (c) 2017 [Jason Daly](http://www.deefour.me) ([deefour](https://github.com/deefour)). Released under the [MIT License](http://deefour.mit-license.org/).

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity40

Moderate usage in the ecosystem

Community15

Small or concentrated contributor base

Maturity68

Established project with proven stability

 Bus Factor1

Top contributor holds 97.2% 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 ~53 days

Recently: every ~167 days

Total

23

Last Release

3072d ago

Major Versions

0.7.2 → 1.0.02015-10-07

1.2.1 → 2.0.02017-01-26

PHP version history (2 changes)0.1.0PHP &gt;=5.5.0

2.0.0PHP &gt;=5.6.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/0a2bddbe9f87813e53e82c1799b460513ede9f96a1d85ad7c186c0692a6f0762?d=identicon)[deefour](/maintainers/deefour)

---

Top Contributors

[![deefour](https://avatars.githubusercontent.com/u/14762?v=4)](https://github.com/deefour "deefour (139 commits)")[![FooBarQuaxx](https://avatars.githubusercontent.com/u/2607178?v=4)](https://github.com/FooBarQuaxx "FooBarQuaxx (3 commits)")[![michaelachrisco](https://avatars.githubusercontent.com/u/398491?v=4)](https://github.com/michaelachrisco "michaelachrisco (1 commits)")

---

Tags

business-logiccontextinteractorsphpservice-objectlaraveldtointeractordeefourservice object

###  Code Quality

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/deefour-interactor/health.svg)

```
[![Health](https://phpackages.com/badges/deefour-interactor/health.svg)](https://phpackages.com/packages/deefour-interactor)
```

###  Alternatives

[deefour/authorizer

Simple Authorization via PHP Classes

483.3k](/packages/deefour-authorizer)[yorcreative/laravel-argonaut-dto

Argonaut is a lightweight Data Transfer Object (DTO) package for Laravel that supports nested casting, recursive serialization, and validation out of the box. Ideal for service layers, APIs, and clean architecture workflows.

1062.8k1](/packages/yorcreative-laravel-argonaut-dto)[fab2s/dt0

Immutable DTOs with bidirectional casting. No framework required. 8x faster than the alternative.

101.6k1](/packages/fab2s-dt0)

PHPackages © 2026

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