<file_sep><?php

namespace Plata\Fravel;

use League\Fractal\Manager;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\View\Factory as ViewFactoryContract;

class FravelServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->bootConfigurationFiles();

        $this->commands('command.make.transformer');
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        $this->registerFacade();

        $this->registerFractalManager();

        $this->registerFravelResponse();

        $this->registerTransformerGenerator();
    }

    private function registerFacade()
    {
        $this->app->singleton('Fractal', function () {
            return new FractalResourceFactory();
        });
    }

    private function registerFractalManager()
    {
        $this->app->singleton(Manager::class, function () {
            $manager = new Manager();
            $url = \URL::to(config('fravel.base_link', '/'));
            $serializer = config('fravel.serializer', \League\Fractal\Serializer\DataArraySerializer::class);

            $manager->setSerializer(new $serializer($url));

            return $manager;
        });
    }

    private function registerFravelResponse()
    {
        \Response::swap(new \Plata\Fravel\Response(
            $this->app[ViewFactoryContract::class],
            $this->app['redirect'],
            $this->app[Manager::class]
        ));
    }

    private function registerTransformerGenerator()
    {
        $this->app->singleton('transformer.creator', function ($app) {
            return new TransformerCreator($app['files']);
        });

        $this->app->singleton('command.make.transformer', function ($app) {
            // Once we have the migration creator registered, we will create the command
            // and inject the creator. The creator is responsible for the actual file
            // creation of the transformers, and may be extended by developers.
            $creator = $app['transformer.creator'];

            $composer = $app['composer'];

            return new TransformerMakeCommand($creator, $composer);
        });
    }

    private function bootConfigurationFiles()
    {
        $this->publishes([
            __DIR__.'/config/config.php' => app()->basePath() . '/config/fravel.php',
        ]);
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return [
            'Fractal',
            'transformer.creator',
            'command.make.transformer'
        ];
    }
}
<file_sep># Fravel
[![License](https://img.shields.io/badge/license-MIT-orange.svg)](https://packagist.org/packages/plata/fravel)

A Fractal wrapper for Laravel


Fractal is designed in such a way that it could be used by any frameworks or no framework at all. But wouldn't it be cool if we can use it like it's build right on top of Laravel?

## Installation

### Composer
```shell
composer require plata/fravel
```

Then in your `config/app.php`'s provider array:

```php
'providers' => [
  // ...
  'Plata\Fravel\FravelServiceProvider::class',
  // ...
]
```

and within the same file,

```php
'aliases' => [
  // ...
  ''Fractal' => \Plata\Fravel\Facade\Fractal::class',
  // ...
]
```


## Usage
For a collection of resource, 

```php
$resource = Fractal::collection(User::all(), $transformer);

return Response::fractal($resource);
```
For a single resource,

```php
$resource = Fractal::item(User::find(1), $transformer);

return Response::fractal($resource);
```

## Generators
Everyone knows that developers doesn't like repetitive tasks. That's why generators are really helpful for creating a base template for you!

### Transformers

Existing Model and Migration

```shell
php artisan make:transformer UserTransformer
```

For non existing model/migration, just append `-t` flag

```shell
php artisan make:model User -m -t
```

## Configurations

Fravel ships with a configuration file where you can change any Fractal specific behaviour. Just run:
```shell
php artisan vendor:publish
```

## Support

Need more control? Check this thorough documentation of Fravel.
<file_sep><?php

namespace Plata\Fravel;

use Illuminate\Filesystem\Filesystem;

class TransformerCreator
{
    /**
     * The filesystem instance.
     *
     * @var \Illuminate\Filesystem\Filesystem
     */
    protected $files;

    /**
     * Stub content.
     *
     * @var string
     */
    protected $stub;

    /**
     * Create a new transformer creator instance.
     *
     * @param  \Illuminate\Filesystem\Filesystem  $files
     * @return void
     */
    public function __construct(Filesystem $files)
    {
        $this->files = $files;
    }

    public function create($name, $path)
    {
        // Get the class template.
        $stub = $this->files->get(__DIR__.'/stubs/transformer.stub');

        // Make sure the path exists.
        if (!$this->files->exists($path))
            $this->files->makeDirectory($path);

        // Write the generated template.
        $this->files->put(
            $path.'/'.$name.'Transformer.php',
            $this->populateStub($stub, $name)
        );
    }

    protected function populateStub($stub, $name)
    {
        $this->stub = $stub;

        $defaultNamespace = config('app.name') == 'Laravel'
            ? 'App'
            : config('app.name');

        return $this->setClassName($name)
                ->setTransformerNamespace($defaultNamespace)
                ->setModelNamespace($name)
                ->setParameterType($name)
                ->setParameterName($name)
                ->stub;
    }

    private function setClassName($name)
    {
        $this->stub = str_replace(
            'StubTransformer',
            $name.'Transformer',
            $this->stub
        );

        return $this;
    }

    private function setTransformerNamespace($namespace)
    {
        $this->stub = str_replace(
            'Namespace',
            $namespace.'\\Transformers',
            $this->stub
        );

        return $this;
    }

    private function setModelNamespace($name)
    {
        $this->stub = str_replace(
            'use Model',
            'use '.config('fravel.model_namespace').'\\'.$name,
            $this->stub
        );

        return $this;
    }

    private function setParameterType($name)
    {
        $this->stub = str_replace(
            'ModelInstance',
            $name,
            $this->stub
        );

        return $this;
    }

    private function setParameterName($name)
    {
        $this->stub = str_replace(
            '$model',
            '$'.strtolower($name),
            $this->stub
        );

        return $this;
    }
}<file_sep><?php

namespace Plata\Fravel;

use League\Fractal\Resource\Item;
use League\Fractal\Resource\Collection;

class FractalResourceFactory
{
    /**
     * Create a new resource instance.
     *
     * @param mixed                             $data
     * @param callable|TransformerAbstract|null $transformer
     * @param string                            $resourceKey
     * @return Item
     */
    public function item($data, $transformer, $resourceKey)
    {
        return new Item($data, $transformer, $resourceKey);
    }

    /**
     * Create a new resource instance.
     *
     * @param mixed                             $data
     * @param callable|TransformerAbstract|null $transformer
     * @param string                            $resourceKey
     * @return Collection
     */
    public function collection($data, $transformer, $resourceKey)
    {
        return new Collection($data, $transformer, $resourceKey);
    }
}<file_sep><?php

namespace Plata\Fravel;

use Illuminate\Console\Command;
use Illuminate\Support\Composer;

class TransformerMakeCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'make:transformer {name : The name of the transformer.}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Creates a new transformer class';

    /**
     * TransformerCreator instance.
     *
     * @var TransformerCreator
     */
    private $transformerCreator;

    /**
     * Composer instance.
     *
     * @var Composer
     */
    private $composer;

    /**
     * Create a new command instance.
     *
     * @param TransformerCreator $transformerCreator
     * @param Composer $composer
     * @return void
     */
    public function __construct(TransformerCreator $transformerCreator, Composer $composer)
    {
        parent::__construct();
        $this->transformerCreator = $transformerCreator;
        $this->composer = $composer;
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $name = trim($this->input->getArgument('name'));

        // Checks to see if the name already has a Transformer suffix.
        $name = str_contains($name, 'Transformer')
            ? str_replace('Transformer', '', $name)
            : $name;

        $this->transformerCreator->create($name, app_path('Transformers'));

        $this->line("<info>Transformer created successfully.</info>");

        $this->composer->dumpAutoloads();
    }
}
<file_sep><?php

namespace Plata\Fravel;

use League\Fractal\Manager;
use Illuminate\Http\JsonResponse;
use Illuminate\Routing\ResponseFactory;
use League\Fractal\Resource\ResourceAbstract;

class Response extends ResponseFactory
{
    /**
     * Fractal manager instance.
     *
     * @var Manager
     */
    protected $fractalManager;

    /**
     * Create a new response factory instance.
     *
     * @param  \Illuminate\Contracts\View\Factory  $view
     * @param  \Illuminate\Routing\Redirector  $redirector
     * @param Manager $fractalManager
     * @return void
     */
    public function __construct(\Illuminate\Contracts\View\Factory $view, \Illuminate\Routing\Redirector $redirector, Manager $fractalManager)
    {
        parent::__construct($view, $redirector);

        $this->fractalManager = $fractalManager;
    }

    /**
     * Return a new JSON response from the application.
     *
     * @param  ResourceAbstract  $data
     * @param  int  $status
     * @param  array  $headers
     * @param  int  $options
     * @return \Illuminate\Http\JsonResponse
     */
    public function fractal(ResourceAbstract $data, $status = 200, array $headers = [], $options = 0)
    {
        return new JsonResponse($this->fractalManager->createData($data)->toArray(), $status, $headers, $options);
    }
}<file_sep><?php

/**
 * This file is part of Fravel,
 * a Fractal wrapper for Laravel.
 *
 * @license MIT
 * @package Plata\Fravel
 */
return [
    /*
    |--------------------------------------------------------------------------
    | Json Serializer
    |--------------------------------------------------------------------------
    |
    | This is the default json serializer that will be used throughout
    | the entire response. Of course you can override this if you
    | want to set the serializer to only a certain response.
    |
    */
    'serializer' => \League\Fractal\Serializer\DataArraySerializer::class,

    /*
    |--------------------------------------------------------------------------
    | Resource base link
    |--------------------------------------------------------------------------
    |
    | This will be use to generate resource links.
    |
    */
    'base_link' => '/',

    'model_namespace' => 'App'
];
