PHPackages                             redgem/servicesio-bundle - 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. [Framework](/categories/framework)
4. /
5. redgem/servicesio-bundle

ActiveSymfony-bundle[Framework](/categories/framework)

redgem/servicesio-bundle
========================

ServicesIO is a Symfony bundle that provides a way to easily build a services provider

1.1.0(6y ago)0958MITPHPPHP &gt;=7.0

Since Oct 9Pushed 6y ago1 watchersCompare

[ Source](https://github.com/ghugot/ServicesIOBundle)[ Packagist](https://packagist.org/packages/redgem/servicesio-bundle)[ RSS](/packages/redgem-servicesio-bundle/feed)WikiDiscussions master Synced 2w ago

READMEChangelogDependenciesVersions (9)Used By (0)

ServicesIOBundle
================

[](#servicesiobundle)

*ServicesIOBundle* is a Symfony bundle that provides a way to easily build a services provider.

*ServicesIO* basically introduces two components :

- a reader. the reader build for you a model that wrap the input data.

    - from the standart input (i.e Request object).
    - from a third party service you want to request.
- a view layer to scaffold an object tree view of the reponse. The view layer is called by your controller, build the tree you want to render, and actually render it (json only so far).

Install the bundle
------------------

[](#install-the-bundle)

First of all, add and enable *ServicesIOBundle* in your project.

Add it in composer :

`composer require redgem/servicesio-bundle:1.0.*`

All set.

ServicesIO Model
----------------

[](#servicesio-model)

The model will help you to read and decode trees structures (such as json) passed, for instance, as requests to your controllers.

*documentation of the Model component to come later*

ServicesIO View
---------------

[](#servicesio-view)

You are probably used to *Twig* to build and render your HTML views on your projects.

The aim of *ServicesIO* is to be able to get something as powerful and efficient for data trees that *Twig* could be for HTML.

### Overview

[](#overview)

*ServicesIO view* provide you the ability to create :

- re-usable view components callable by path configuration.
- extensible view components, with datas placeholders and smart way to fill them.
- integration with bundles extension, to override view components by extending bundles.

Here is what you need to know before starting :

- The view rendering system runs by a two-steps system :

    1 - you will build a tree in your view on a language agnostic datastructure by creating connected nodes. a node is composed of 3 available elements :

    - a Collection, than handle a list of others nodes.
    - an Item, than handle named fields with nodes attached.
    - a scalar, that will be the piece of data to render (i.e *string*, *int*, *float*).

    2 - the rendering of the tree. Once it's fully build, we will be able to turn it into a data langage (only json so far) and send it to output.

### The basics : create your View

[](#the-basics--create-your-view)

Now, let's see that in action with a short example.

To make it easy to read, I will remove all code that is not related to our topic.

Let's create a small example project : 2 *entities* and 2 *controllers*.

The *Doctrine entities* :

```
class User
{
  /**
   * @MongoDB\Id
   */
  public $id;

  /**
   * @MongoDB\Field(type="string")
   */
  public $name;
}
```

```
class Message
{
  /**
   * @MongoDB\Id
   */
  public $id;

  /**
   * @MongoDB\Field(type="string")
   */
  public $title;

  /**
   * @MongoDB\Field(type="string")
   */
  public $description;

  /**
   * @MongoDB\Field(type="id")
   */
  public $user;
}
```

I assume to have 2 users : *author* and *visitor* and 3 entries for messages : message1, message2, message3.

Here are the controllers (without their return calls) :

```
class MessageController
{
  public function singleAction(int $id, DocumentManager $documentManager)
  {
    $single = $documentManager
      ->getRepository(Message::class)->find($id);
  }

  public function listingAction(DocumentManager $documentManager)
  {
    $listing = $documentManager
      ->getRepository(Message::class)->findAll();
  }
}
```

Based on that, we now have to create the *View* elements for *decorators* and *entities*, and call the rendering from the *controllers*.

Let's complete first the *messageAction*'s View, with a *ServicesIO view* class.

A basic `View` class has to :

- extends `Redgem\ServicesIOBundle\Lib\View\View`
- location is up to you, we recommend using `\View namespace` (and therefore in the `/View` directory) to make a clear organisation.
- implements the `content()` that is the place to build the view tree.
- class name is also up to you, we recommend to name it `View`.

```
namespace MyBundle\View;

use Redgem\ServicesIOBundle\Lib\View\View;

class SingleView extends View
{
  public function content()
  {
    return $this->createItem()
        ->set('message', $this->createItem()
        ->set('id', $this->params['single']->id)
        ->set('title', $this->params['single']->title);
        ->set('description', $this->params['single']->description)
      );
  }
}
```

We are here creating a simple tree with an Item object that containt 3 childrens : *id*, *title*, *description* to display our 3 corresponding *message* fields.

Finally, let's make the *controller* calling and rendering it :

The rendering service to call in *ServicesIOBundle* is called `Redgem\ServicesIOBundle\Lib\View\Service`.

The call takes 2 arguments :

- the *viewpath*. the view class name.
- an array of parameters to send.

```
use Redgem\ServicesIOBundle\Lib\View\Service as View;

class MessageController
{
  public function singleAction(int $id, DocumentManager $documentManager, View $view)
  {
    $single = $documentManager
      ->getRepository(Message::class)->find($id);

    return $view->render(
      SingleView::class,
      ['single' => $single]
    );
  }
}
```

The *single* variable sent as a parameter in the controller is accessible as *$this-&gt;params\['single'\]* in the view class.

Let's call it for the first *message* for example !

```
{
	"message" : {
	  "id" : "1",
	  "title" : "message 1 title",
	  "description" : "description 1 title"
	}
}
```

Nice, we now have the *json* representation of the *MessageView* class tree !

We can now replicate it for the *listing* action :

```
namespace MyBundle\View;

use Redgem\ServicesIOBundle\Lib\View\View;

class ListingView extends View
{
  public function content()
  {
    $collection = $this->createCollection();

    foreach($this->params['listing'] as $message) {

      $collection->push(
        $this->createItem()
          ->set('id', $this->params['message']->id)
          ->set('title', $this->params['message']->title);
          ->set('description', $this->params['message']->description)
        );
      );
    }

    return $this->createItem()
      ->set('listing', $collection);
  }
}
```

```
use Redgem\ServicesIOBundle\Lib\View\Service as View;

class MessageController
{
  public function listingAction(DocumentManager $documentManager, View $view)
  {
    $listing = $documentManager
      ->getRepository(Message::class)->findAll();

    return $view->render(
      ListingView::class,
      ['listing' => $listing]
    );
  }
}
```

Let's call it, and :

```
{
  "listing" : [
	  {
	    "id" : "1",
	    "title" : "message 1 title",
	    "description" : "description 1 title"
	  },
	  {
	    "id" : "2",
	    "title" : "message 2 title",
	    "description" : "description 2 title"
	  },
	  {
	    "id" : "3",
	    "title" : "message 3 title",
	    "description" : "description 3 title"
	  }
  ]
}
```

Excellent !

### Partials views and fragment controllers

[](#partials-views-and-fragment-controllers)

We have a problem. We did repeat ourselves between the 2 classes to create the partial view of a *message*.

Fortunately, there is a solution, : to create a reusable element.

```
namespace MyBundle\View;

use Redgem\ServicesIOBundle\Lib\View\View;

class MessageView extends View
{
  public function content()
  {
    return $this->createItem()
      ->set('id', $this->params['message']->id)
      ->set('title', $this->params['message']->title);
      ->set('description', $this->params['message']->description)
    );
  }
}
```

As yu can see, a reusable element is a totaly regular View class. That mean, you may use it to render directly a controller if you want to.

Finally, we use it on our *SingleView* and *ListingView* :

```
namespace MyBundle\View;

use Redgem\ServicesIOBundle\Lib\View\View;

class SingleView extends View
{
  public function content()
  {
    return $this->createItem()
      ->set('message', $this->partial(MessageView::class, ['message' => $this->params['single']]));
  }
}
```

```
namespace MyBundle\View;

use Redgem\ServicesIOBundle\Lib\View\View;

class ListingView extends View
{
  public function content()
  {
    $collection = $this->createCollection();

    foreach($this->params['listing'] as $message) {

      $collection->push(
        $this->partial(MessageView::class, ['message' => $message])
      );
    }

    return $this->createItem()
      ->set('listing', $collection);
  }
}
```

When calling the `partial()` method to get the subtree from there, it will pass for you the current context (i.e params you sent from the controller) merged with the params you add in the second method argument.

Of course, the *json* final rendering is still exactly the same.

Let's now enrich the data response everywhere with a new *UserView* :

```
namespace MyBundle\View;

use Redgem\ServicesIOBundle\Lib\View\View;

class UserView extends View
{
  public function content()
  {
    return $this->createItem()
      ->set('id', $this->params['user']->id)
      ->set('name', $this->params['user']->name);
    );
  }
}
```

and

```
namespace MyBundle\View;

use Redgem\ServicesIOBundle\Lib\View\View;

class MessageView extends View
{
  public function content()
  {
    return $this->createItem()
      ->set('id', $this->params['message']->id)
      ->set('title', $this->params['message']->title);
      ->set('description', $this->params['message']->description)
      ->set('user', ($this->params['message']->user == null) ? null
      	: $this->partial(UserView:class, ['user' => $this->params['message']])
      )
    );
  }
}
```

A *single* request will now display :

```
{
  "message" : {
    "id" : "1",
    "title" : "message 1 title",
    "description" : "description 1 title",
    "user": {
      "id": "1",
      "name": "author"
    }
  }
}
```

In addition to to `partial()` method, a `controller()` method is available. Instead of calling just a view, it will call a whole Symfony controller as a *fragment*.

Its prototype is :

`function controller($controller, $params = array())`

with :

- `$controller` : a regular Symfony controller name (as a string, with full class name)
- `$params` : an array, that will be merged with current params context and passed to the new controller.

The response will be handle on that way :

- If this new controller return a *ServicesIO view* response, the fragment tree will be merged on the main tree at the right place.
- Otherwise, the response will be threated as a string and merged on the main tree at the right place.

And one more thing :

- `get($service)` - string $service : a Symfony service name.
- `getParameter($parameter)` - string $parameter : a parameter name.

methods are available as well. They just use the *container* to call a *Symfony service* or a *parameter*.

### View extensions

[](#view-extensions)

We now want to decorate our response with the connected user on the top of it. It's easy to do with this `controller()` method.

I assume that the user is correctly authenticated by the *Security component* :

```
public function visitorAction(View $view)
{
  return $view->render(
    UserView::class,
    ['user' => $this->getUser()] //returns a User object
  );
}
```

And I add that to my views :

```
namespace MyBundle\View;

use Redgem\ServicesIOBundle\Lib\View\View;

class SingleView extends View
{
  public function content()
  {
    return $this->createItem()
      ->set('visitor', $this->controller('App\MyController\VisitorAction'))
      ->set('response', $this->createItem()
        ->set('message', $this->partial(MessageView::class, ['message' => $this->params['single']]))
      );
  }
}
```

```
namespace MyBundle\View;

use Redgem\ServicesIOBundle\Lib\View\View;

class ListingView extends View
{
  public function content()
  {
    $collection = $this->createCollection();

    foreach($this->params['listing'] as $message) {
      $collection->push(
        $this->partial(MessageView::class, ['message' => $message])
      );
    }

    return $this->createItem()
      ->set('visitor', $this->controller('App\MyController\VisitorAction'))
      ->set('response', $this->createItem()
        ->set('listing', $collection)
      );
  }
}
```

A *single* request will now display :

```
{
  "visitor": {
    "id": "2",
    "name": "visitor"
  },
  "content": {
	  "message" : {
	    "id" : "1",
	    "title" : "message 1 title",
	    "description" : "description 1 title",
	    "user": {
	      "id": "1",
	      "name": "author"
	    }
	  }
	}
}
```

And my problem of repeating myself is back... on the global decorator. I have created twice the main object with *visitor* and *response*.

We can solve it by changing our way of thinking. Instead of having only one class to build the tree, let's split it into 2 elements :

- a class to build the main decorator node (i.e, with *visitor* and *response*)
- filling the fields of the *response* node by each view classes.

First of all, let's create the decorator thing. It's still a regular *View* class :

```
namespace MyBundle\View;

use Redgem\ServicesIOBundle\Lib\View\View;

class DecoratorView extends View
{
  public function content()
  {
    return $this->createItem()
    	->set('visitor', $this->controller('App\MyController\VisitorAction'))
    	->set('response', null, 'fullResponse');
  }
}
```

There is a new third argument on the `set()` method. This third argument is a string, and set up a name for the placeholder option. The placeholder is an entry on the tree that can be replaced later by an another value.

In case of placeholder, the second value (*null* here) is a default value that will be displayed if the placeholder is not filled.

We can now transform our *SingleView* and *ListingView* to use this decorator :

```
namespace MyBundle\View;

use Redgem\ServicesIOBundle\Lib\View\View;

class SingleView extends View
{
  public function getParent()
  {
    return DecoratorView::class;
  }

  public function blockFullResponse()
  {
    return $this->createItem()
      ->set('message', $this->partial(MessageView::class, ['message' => $this->params['single']]));
  }
}
```

```
namespace MyBundle\View;

use Redgem\ServicesIOBundle\Lib\View\View;

class ListingView extends View
{
  public function getParent()
  {
    return DecoratorView::class;
  }

  public function blockFullResponse()
  {
    $collection = $this->createCollection();

    foreach($this->params['listing'] as $message) {
      $collection->push(
        $this->partial(MessageView::class, ['message' => $message])
      );
    }

    return $this->createItem()
      ->set('listing', $collection);
  }
}
```

We can see two differences :

- a new `getParent()` method appeared : that means that the root node will be deported in this view class. All the context is passed to this object, and obviously, this is a standard View class you can reuse where you want.
- the `content()` method is replaced by the *blockFullResponse()*. The `content()` method is the method that create the root node of the tree. It's therefore not compatible with the `getParent() method. A class with a getParent()` method will only fill the placeholders defined above it. This is the purpose of `blockXXX()` where XXX is the placeholder name in CamelCase. methods (i.e blockFullResponse here).

You can of course chain how many levels oh hierarchy you want with `getParent()` and define placeholders into all of them.

`getParent()` usually return a string. It can also return an array :

```
public function getParent()
{
  return array(MyFriendBundleView::class, MyBundleDecoratorView::class);
}
```

In this case, the first actually implemented View class will be chosen.

### Why all those fancy stuffs ?

[](#why-all-those-fancy-stuffs-)

Why doing that ? You may say it would be easier to use the regular extends PHP word for classes, and avoiding using *viewpath*, and you would be right. But *ServicesIO view* do provides you an easy, flexible, and clear way to build some view reusables pieces.

So Finally, we can call our controllers :

```
{
  "visitor": {
    "id": "2",
    "name": "visitor"
  },
  "content": {
	  "message" : {
	    "id" : "1",
	    "title" : "message 1 title",
	    "description" : "description 1 title",
	    "user": {
	      "id": "1",
	      "name": "author"
	    }
	  }
	}
}
```

```
{
  "visitor": {
    "id": "2",
    "name": "visitor"
  },
  "content": {
    "listing" : [
      {
        "id" : "1",
        "title" : "message 1 title",
        "description" : "description 1 title",
        "user": {
          "id": "1",
          "name": "author"
        }
      },
      {
        "id" : "2",
        "title" : "message 2 title",
        "description" : "description 2 title",
        "user": {
          "id": "1",
          "name": "author"
        }
      },
      {
        "id" : "3",
        "title" : "message 3 title",
        "description" : "description 3 title",
        "user": {
          "id": "1",
          "name": "author"
        }
      }
    ]
  }
}
```

###  Health Score

29

—

LowBetter than 57% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity14

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity63

Established project with proven stability

 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 ~232 days

Recently: every ~267 days

Total

8

Last Release

2284d ago

Major Versions

0.2.2 → 1.0.02020-01-17

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

1.0.0PHP &gt;=7.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/1c8f7f2eda258c3b0928159b495de36ef7f89fb430d30d4ccc45b8a743580db9?d=identicon)[ghugot](/maintainers/ghugot)

---

Top Contributors

[![ghugot](https://avatars.githubusercontent.com/u/1872675?v=4)](https://github.com/ghugot "ghugot (44 commits)")

### Embed Badge

![Health badge](/badges/redgem-servicesio-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/redgem-servicesio-bundle/health.svg)](https://phpackages.com/packages/redgem-servicesio-bundle)
```

###  Alternatives

[laravel/socialite

Laravel wrapper around OAuth 1 &amp; OAuth 2 libraries.

5.7k104.3M829](/packages/laravel-socialite)[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k38.6M289](/packages/laravel-dusk)[pinguo/php-msf

Pinguo Micro Service Framework For PHP

1.7k4.2k](/packages/pinguo-php-msf)[nineinchnick/edatatables

Grid widget for the Yii Framework, wrapper for the DataTables jQuery plugin

173.2k](/packages/nineinchnick-edatatables)[link-cloud/fast-hyperf

LinkCloud Fast Hyperf

241.2k1](/packages/link-cloud-fast-hyperf)

PHPackages © 2026

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