thephpleague / fractal

Output complex, flexible, AJAX/RESTful data structures.

Home Page:fractal.thephpleague.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Suggestion] Use a container to fetch transformers

hubertnnn opened this issue · comments

Currently fractal requires transformer to be an object.
So your code will look like this:

$fractal = new Manager();
$array = $fractal->createData(new Collection($books, new BookTransformer))->toArray();

// and

public function includeAuthor(Book $book)
{
    return $this->item($book->author, new AuthorTransformer);
}

This may not work well with some frameworks (eg. Symfony),
since its possible that a transformer might have extra dependencies (eg. Translator).
Passing those dependencies between transformers is difficult and error prone,
so it would be much better to have a way to fetch transformers from a container.

The solution could be as simple as:

$fractal = new Manager();
$fractal->setContainer($container);
$array = $fractal->createData(new Collection($books, BookTransformer::class))->toArray();

// and

public function includeAuthor(Book $book)
{
    return $this->item($book->author, AuthorTransformer::class);
}

By default fractal could use a dummy container that just calls a constructor:

class ConstructionContainer implements \Psr\Container\ContainerInterface
{
    public function has(string $class)
    {
        return class_exists($class);
    }

    public function get(string $class)
    {
        return new new $class();
    }
}

This way if you provide a callable or object as $transformer then behavior will not change.
But if you provide a string, the transformer object will be fetched from the container.

There's nothing stopping you from using a container to build your transformers today

class BookTransformer
{

    public function __construct(
        protected AuthorTransformer $authorTransformer,
    ) {}

    ...

    public function includeAuthor(Book $book)
    {
        return $this->item($book->getAuthor(), $this->authorTransformer);
    }
}

In my opinion this is a better way to go about things since you end up with explicit dependencies rather than implicit service locator based dependencies that can be much harder to test.