<?php

/**
 * LICENSE: The MIT License (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * https://github.com/azure/azure-storage-php/LICENSE
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * PHP version 5
 *
 * @category  Microsoft
 * @package   MicrosoftAzure\Storage\Table\Models
 * @author    Azure Storage PHP SDK <dmsh@microsoft.com>
 * @copyright 2016 Microsoft Corporation
 * @license   https://github.com/azure/azure-storage-php/LICENSE
 * @link      https://github.com/azure/azure-storage-php
 */

namespace MicrosoftAzure\Storage\Table\Models;

use MicrosoftAzure\Storage\Common\Internal\Utilities;
use MicrosoftAzure\Storage\Table\Internal\TableResources as Resources;

/**
 * Holds results of calling queryEntities API
 *
 * @category  Microsoft
 * @package   MicrosoftAzure\Storage\Table\Models
 * @author    Azure Storage PHP SDK <dmsh@microsoft.com>
 * @copyright 2016 Microsoft Corporation
 * @license   https://github.com/azure/azure-storage-php/LICENSE
 * @link      https://github.com/azure/azure-storage-php
 */
class QueryEntitiesResult
{
    use TableContinuationTokenTrait;

    private $_entities;

    /**
     * Creates new QueryEntitiesResult instance.
     *
     * @param array $headers  The HTTP response headers.
     * @param array $entities The entities.
     *
     * @internal
     *
     * @return QueryEntitiesResult
     */
    public static function create(array $headers, array $entities)
    {
        $result  = new QueryEntitiesResult();
        $headers = array_change_key_case($headers);
        $nextPK  = Utilities::tryGetValue(
            $headers,
            Resources::X_MS_CONTINUATION_NEXTPARTITIONKEY
        );
        $nextRK  = Utilities::tryGetValue(
            $headers,
            Resources::X_MS_CONTINUATION_NEXTROWKEY
        );

        if ($nextRK != null && $nextPK != null) {
            $result->setContinuationToken(
                new TableContinuationToken(
                    '',
                    $nextPK,
                    $nextRK,
                    Utilities::getLocationFromHeaders($headers)
                )
            );
        }

        $result->setEntities($entities);

        return $result;
    }

    /**
     * Gets entities.
     *
     * @return array
     */
    public function getEntities()
    {
        return $this->_entities;
    }

    /**
     * Sets entities.
     *
     * @param array $entities The entities array.
     *
     * @return void
     */
    protected function setEntities(array $entities)
    {
        $this->_entities = $entities;
    }
}


<?php
/**
 * LICENSE: The MIT License (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * https://github.com/azure/azure-storage-php/LICENSE
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * PHP version 5
 *
 * @category  Microsoft
 * @package   MicrosoftAzure\Storage\Common\Internal\Http
 * @author    Azure Storage PHP SDK <dmsh@microsoft.com>
 * @copyright 2016 Microsoft Corporation
 * @license   https://github.com/azure/azure-storage-php/LICENSE
 * @link      https://github.com/azure/azure-storage-php
 */

namespace MicrosoftAzure\Storage\Common\Internal\Http;

/**
 * Helper class to format the http headers
 *
 * @ignore
 * @category  Microsoft
 * @package   MicrosoftAzure\Storage\Common\Internal\Http
 * @author    Azure Storage PHP SDK <dmsh@microsoft.com>
 * @copyright 2016 Microsoft Corporation
 * @license   https://github.com/azure/azure-storage-php/LICENSE
 * @link      https://github.com/azure/azure-storage-php
 */
class HttpFormatter
{
    /**
     * Convert a http headers array into an uniformed format for further process
     *
     * @param array $headers headers for format
     *
     * @return array
     */
    public static function formatHeaders(array $headers)
    {
        $result = array();
        foreach ($headers as $key => $value) {
            if (is_array($value) && count($value) == 1) {
                $result[strtolower($key)] = $value[0];
            } else {
                $result[strtolower($key)] = $value;
            }
        }

        return $result;
    }
}


<?php

/**
 * LICENSE: The MIT License (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * https://github.com/azure/azure-storage-php/LICENSE
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * PHP version 5
 *
 * @category  Microsoft
 * @package   MicrosoftAzure\Storage\Common\Models
 * @author    Azure Storage PHP SDK <dmsh@microsoft.com>
 * @copyright 2016 Microsoft Corporation
 * @license   https://github.com/azure/azure-storage-php/LICENSE
 * @link      https://github.com/azure/azure-storage-php
 */

namespace MicrosoftAzure\Storage\Common\Models;

/**
 * Result from calling GetServiceProperties REST wrapper.
 *
 * @category  Microsoft
 * @package   MicrosoftAzure\Storage\Common\Models
 * @author    Azure Storage PHP SDK <dmsh@microsoft.com>
 * @copyright 2016 Microsoft Corporation
 * @license   https://github.com/azure/azure-storage-php/LICENSE
 * @link      https://github.com/azure/azure-storage-php
 */
class GetServicePropertiesResult
{
    private $_serviceProperties;

    /**
     * Creates object from $parsedResponse.
     *
     * @internal
     * @param array $parsedResponse XML response parsed into array.
     *
     * @return \MicrosoftAzure\Storage\Common\Models\GetServicePropertiesResult
     */
    public static function create(array $parsedResponse)
    {
        $result = new GetServicePropertiesResult();
        $result->setValue(ServiceProperties::create($parsedResponse));

        return $result;
    }

    /**
     * Gets service properties object.
     *
     * @return \MicrosoftAzure\Storage\Common\Models\ServiceProperties
     */
    public function getValue()
    {
        return $this->_serviceProperties;
    }

    /**
     * Sets service properties object.
     *
     * @param ServiceProperties $serviceProperties object to use.
     *
     * @return void
     */
    protected function setValue($serviceProperties)
    {
        $this->_serviceProperties = clone $serviceProperties;
    }
}


<?php

/**
 * LICENSE: The MIT License (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * https://github.com/azure/azure-storage-php/LICENSE
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * PHP version 5
 *
 * @category  Microsoft
 * @package   MicrosoftAzure\Storage\File\Models
 * @author    Azure Storage PHP SDK <dmsh@microsoft.com>
 * @copyright 2017 Microsoft Corporation
 * @license   https://github.com/azure/azure-storage-php/LICENSE
 * @link      https://github.com/azure/azure-storage-php
 */

namespace MicrosoftAzure\Storage\File\Models;

use MicrosoftAzure\Storage\Common\Internal\Utilities;
use MicrosoftAzure\Storage\File\Internal\FileResources as Resources;

/**
 * Represents windows azure file object
 *
 * @category  Microsoft
 * @package   MicrosoftAzure\Storage\File\Models
 * @author    Azure Storage PHP SDK <dmsh@microsoft.com>
 * @copyright 2017 Microsoft Corporation
 * @license   https://github.com/azure/azure-storage-php/LICENSE
 * @link      https://github.com/azure/azure-storage-php
 */
class File
{
    private $name;
    private $length;

    /**
     * Gets file name.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Sets file name.
     *
     * @param string $name value.
     *
     * @return void
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Gets file length.
     *
     * @return int
     */
    public function getLength()
    {
        return $this->length;
    }

    /**
     * Sets file length.
     *
     * @param int $length value.
     *
     * @return void
     */
    public function setLength($length)
    {
        $this->length = $length;
    }

    /**
     * Creates a File object using the parsed array.
     *
     * @param  array $parsed The parsed array that contains the object information.
     *
     * @return File
     */
    public static function create(array $parsed)
    {
        $result = new File();
        $name = Utilities::tryGetValue($parsed, Resources::QP_NAME);
        $result->setName($name);
        $properties = Utilities::tryGetValue($parsed, Resources::QP_PROPERTIES);
        $length = \intval(Utilities::tryGetValue($properties, Resources::QP_CONTENT_LENGTH));
        $result->setLength($length);
        return $result;
    }
}


<?php

/**
 * LICENSE: The MIT License (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * https://github.com/azure/azure-storage-php/LICENSE
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * PHP version 5
 *
 * @category  Microsoft
 * @package   MicrosoftAzure\Storage\Common\Middlewares
 * @author    Azure Storage PHP SDK <dmsh@microsoft.com>
 * @copyright 2017 Microsoft Corporation
 * @license   https://github.com/azure/azure-storage-php/LICENSE
 * @link      https://github.com/azure/azure-storage-php
 */

namespace MicrosoftAzure\Storage\Common\Middlewares;

/**
 * This class provides the stack that handles the logic of applying each
 * middlewares to the request or the response.
 *
 * @category  Microsoft
 * @package   MicrosoftAzure\Storage\Common\Middlewares
 * @author    Azure Storage PHP SDK <dmsh@microsoft.com>
 * @copyright 2017 Microsoft Corporation
 * @license   https://github.com/azure/azure-storage-php/LICENSE
 * @link      https://github.com/azure/azure-storage-php
 */
class MiddlewareStack
{
    private $middlewares = array();

    /**
     * Push the given middleware into the middleware stack.
     *
     * @param  IMiddleware|callable $middleware The middleware to be pushed.
     *
     * @return void
     */
    public function push($middleware)
    {
        array_unshift($this->middlewares, $middleware);
    }

    /**
     * Apply the middlewares to the handler.
     *
     * @param  callable $handler the handler to which the middleware applies.
     *
     * @return callable
     */
    public function apply(callable $handler)
    {
        $result = $handler;
        foreach ($this->middlewares as $middleware) {
            $result = $middleware($result);
        }

        return $result;
    }
}


<?php

namespace Flugg\Responder\Transformers\Concerns;

use League\Fractal\Resource\ResourceInterface;
use League\Fractal\Scope;

/**
 * A trait to be used by a transformer to override Fractal's transformer methods.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
trait OverridesFractal
{
    /**
     * Overrides Fractal's getter for available includes.
     *
     * @return array
     */
    public function getAvailableIncludes()
    {
        if ($this->relations == ['*']) {
            return $this->resolveScopedIncludes($this->getCurrentScope());
        }

        return array_keys($this->normalizeRelations($this->relations));
    }

    /**
     * Overrides Fractal's getter for default includes.
     *
     * @return array
     */
    public function getDefaultIncludes()
    {
        return array_keys($this->normalizeRelations($this->load));
    }

    /**
     * Overrides Fractal's method for including a relation.
     *
     * @param  \League\Fractal\Scope $scope
     * @param  string                $identifier
     * @param  mixed                 $data
     * @return \League\Fractal\Resource\ResourceInterface
     */
    protected function callIncludeMethod(Scope $scope, $identifier, $data)
    {
        $parameters = iterator_to_array($scope->getManager()->getIncludeParams($scope->getIdentifier($identifier)));

        return $this->includeResource($identifier, $data, $parameters);
    }

    /**
     * Resolve scoped includes for the given scope.
     *
     * @param  \League\Fractal\Scope $scope
     * @return array
     */
    protected function resolveScopedIncludes(Scope $scope): array
    {
        $level = count($scope->getParentScopes());
        $includes = $scope->getManager()->getRequestedIncludes();

        return collect($includes)->map(function ($include) {
            return explode('.', $include);
        })->filter(function ($include) use ($level) {
            return count($include) > $level;
        })->pluck($level)->unique()->all();
    }

    /**
     * Get the current scope of the transformer.
     *
     * @return \League\Fractal\Scope
     */
    public abstract function getCurrentScope();

    /**
     * Normalize relations to force a key value structure.
     *
     * @param  array $relations
     * @return array
     */
    protected abstract function normalizeRelations(array $relations): array;

    /**
     * Include a related resource.
     *
     * @param  string $identifier
     * @param  mixed  $data
     * @param  array  $parameters
     * @return \League\Fractal\Resource\ResourceInterface
     */
    protected abstract function includeResource(string $identifier, $data, array $parameters): ResourceInterface;
}


<?php

namespace Flugg\Responder\Http\Responses;

use Flugg\Responder\Contracts\ErrorFactory;
use Flugg\Responder\Contracts\ErrorSerializer;
use Flugg\Responder\Contracts\ResponseFactory;
use Flugg\Responder\Exceptions\InvalidErrorSerializerException;
use InvalidArgumentException;

/**
 * A builder class for building error responses.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class ErrorResponseBuilder extends ResponseBuilder
{
    /**
     * A factory for building error data output.
     *
     * @var \Flugg\Responder\Contracts\ErrorFactory
     */
    private $errorFactory;

    /**
     * A serializer for formatting error data.
     *
     * @var \Flugg\Responder\Contracts\ErrorSerializer
     */
    protected $serializer;

    /**
     * A code representing the error.
     *
     * @var string|null
     */
    protected $errorCode = null;

    /**
     * A message descibing the error.
     *
     * @var string|null
     */
    protected $message = null;

    /**
     * Additional data included with the error.
     *
     * @var array|null
     */
    protected $data = null;

    /**
     * A HTTP status code for the response.
     *
     * @var int
     */
    protected $status = 500;

    /**
     * Construct the builder class.
     *
     * @param \Flugg\Responder\Contracts\ResponseFactory $responseFactory
     * @param \Flugg\Responder\Contracts\ErrorFactory    $errorFactory
     */
    public function __construct(ResponseFactory $responseFactory, ErrorFactory $errorFactory)
    {
        $this->errorFactory = $errorFactory;

        parent::__construct($responseFactory);
    }

    /**
     * Set the error code and message.
     *
     * @param  mixed|null  $errorCode
     * @param  string|null $message
     * @return $this
     */
    public function error($errorCode = null, string $message = null)
    {
        $this->errorCode = $errorCode;
        $this->message = $message;

        return $this;
    }

    /**
     * Add additional data to the error.
     *
     * @param  array|null $data
     * @return $this
     */
    public function data(array $data = null)
    {
        $this->data = array_merge((array) $this->data, (array) $data);

        return $this;
    }

    /**
     * Set the error serializer.
     *
     * @param  \Flugg\Responder\Contracts\ErrorSerializer|string $serializer
     * @return $this
     * @throws \Flugg\Responder\Exceptions\InvalidErrorSerializerException
     */
    public function serializer($serializer)
    {
        if (is_string($serializer)) {
            $serializer = new $serializer;
        }

        if (! $serializer instanceof ErrorSerializer) {
            throw new InvalidErrorSerializerException;
        }

        $this->serializer = $serializer;

        return $this;
    }

    /**
     * Get the serialized response output.
     *
     * @return array
     */
    protected function getOutput(): array
    {
        return $this->errorFactory->make($this->serializer, $this->errorCode, $this->message, $this->data);
    }

    /**
     * Validate the HTTP status code for the response.
     *
     * @param  int $status
     * @return void
     * @throws \InvalidArgumentException
     */
    protected function validateStatusCode(int $status)
    {
        if ($status < 400 || $status >= 600) {
            throw new InvalidArgumentException("{$status} is not a valid error HTTP status code.");
        }
    }
}


<?php

namespace Flugg\Responder;

use Flugg\Responder\Contracts\Pagination\PaginatorFactory;
use Flugg\Responder\Contracts\Resources\ResourceFactory;
use Flugg\Responder\Contracts\TransformFactory;
use Flugg\Responder\Exceptions\InvalidSuccessSerializerException;
use Flugg\Responder\Pagination\CursorPaginator;
use Flugg\Responder\Transformers\Transformer;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use League\Fractal\Pagination\Cursor;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as CollectionResource;
use League\Fractal\Resource\NullResource;
use League\Fractal\Serializer\SerializerAbstract;

/**
 * A builder class responsible for building transformed arrays.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class TransformBuilder
{
    /**
     * A factory class for making Fractal resources.
     *
     * @var \Flugg\Responder\Contracts\Resources\ResourceFactory
     */
    protected $resourceFactory;

    /**
     * A factory for making transformed arrays.
     *
     * @var \Flugg\Responder\Contracts\TransformFactory
     */
    private $transformFactory;

    /**
     * A factory used to build Fractal paginator adapters.
     *
     * @var \Flugg\Responder\Contracts\Pagination\PaginatorFactory
     */
    protected $paginatorFactory;

    /**
     * The resource that's being built.
     *
     * @var \League\Fractal\Resource\ResourceInterface
     */
    protected $resource;

    /**
     * A serializer for formatting data after transforming.
     *
     * @var \League\Fractal\Serializer\SerializerAbstract
     */
    protected $serializer;

    /**
     * A list of included relations.
     *
     * @var array
     */
    protected $with = [];

    /**
     * A list of excluded relations.
     *
     * @var array
     */
    protected $without = [];

    /**
     * A list of sparse fieldsets.
     *
     * @var array
     */
    protected $only = [];

    /**
     * Construct the builder class.
     *
     * @param \Flugg\Responder\Contracts\Resources\ResourceFactory   $resourceFactory
     * @param \Flugg\Responder\Contracts\TransformFactory            $transformFactory
     * @param \Flugg\Responder\Contracts\Pagination\PaginatorFactory $paginatorFactory
     */
    public function __construct(ResourceFactory $resourceFactory, TransformFactory $transformFactory, PaginatorFactory $paginatorFactory)
    {
        $this->resourceFactory = $resourceFactory;
        $this->transformFactory = $transformFactory;
        $this->paginatorFactory = $paginatorFactory;
    }

    /**
     * Make a resource from the given data and transformer and set the resource key.
     *
     * @param  mixed                                                          $data
     * @param  \Flugg\Responder\Transformers\Transformer|callable|string|null $transformer
     * @param  string|null                                                    $resourceKey
     * @return $this
     */
    public function resource($data = null, $transformer = null, string $resourceKey = null)
    {
        $this->resource = $this->resourceFactory->make($data, $transformer, $resourceKey);

        if ($data instanceof CursorPaginator) {
            $this->cursor($this->paginatorFactory->makeCursor($data));
        } elseif ($data instanceof LengthAwarePaginator) {
            $this->paginator($this->paginatorFactory->make($data));
        }

        return $this;
    }

    /**
     * Manually set the cursor on the resource.
     *
     * @param  \League\Fractal\Pagination\Cursor $cursor
     * @return $this
     */
    public function cursor(Cursor $cursor)
    {
        if ($this->resource instanceof CollectionResource) {
            $this->resource->setCursor($cursor);
        }

        return $this;
    }

    /**
     * Manually set the paginator on the resource.
     *
     * @param  \League\Fractal\Pagination\IlluminatePaginatorAdapter $paginator
     * @return $this
     */
    public function paginator(IlluminatePaginatorAdapter $paginator)
    {
        if ($this->resource instanceof CollectionResource) {
            $this->resource->setPaginator($paginator);
        }

        return $this;
    }

    /**
     * Add meta data appended to the response data.
     *
     * @param  array $data
     * @return $this
     */
    public function meta(array $data)
    {
        $this->resource->setMeta($data);

        return $this;
    }

    /**
     * Include relations to the transform.
     *
     * @param  string[]|string $relations
     * @return $this
     */
    public function with($relations)
    {
        $relations = is_array($relations) ? $relations : func_get_args();

        foreach ($relations as $relation => $constraint) {
            if (is_numeric($relation)) {
                $relation = $constraint;
                $constraint = null;
            }

            $this->with = array_merge($this->with, [$relation => $constraint]);
        }

        return $this;
    }

    /**
     * Exclude relations from the transform.
     *
     * @param  string[]|string $relations
     * @return $this
     */
    public function without($relations)
    {
        $this->without = array_merge($this->without, is_array($relations) ? $relations : func_get_args());

        return $this;
    }

    /**
     * Filter fields to output using sparse fieldsets.
     *
     * @param  string[]|string $fields
     * @return $this
     */
    public function only($fields)
    {
        $this->only = array_merge($this->only, is_array($fields) ? $fields : func_get_args());

        return $this;
    }

    /**
     * Set the serializer.
     *
     * @param  \League\Fractal\Serializer\SerializerAbstract|string $serializer
     * @return $this
     * @throws \Flugg\Responder\Exceptions\InvalidSuccessSerializerException
     */
    public function serializer($serializer)
    {
        if (is_string($serializer)) {
            $serializer = new $serializer;
        }

        if (! $serializer instanceof SerializerAbstract) {
            throw new InvalidSuccessSerializerException;
        }

        $this->serializer = $serializer;

        return $this;
    }

    /**
     * Transform and serialize the data and return the transformed array.
     *
     * @return array|null
     */
    public function transform()
    {
        $this->prepareRelations($this->resource->getData(), $this->resource->getTransformer());

        return $this->transformFactory->make($this->resource ?: new NullResource, $this->serializer, [
            'includes' => $this->with,
            'excludes' => $this->without,
            'fieldsets' => $this->only,
        ]);
    }

    /**
     * Prepare requested relations for the transformation.
     *
     * @param  mixed                                                          $data
     * @param  \Flugg\Responder\Transformers\Transformer|callable|string|null $transformer
     * @return void
     */
    protected function prepareRelations($data, $transformer)
    {
        if ($transformer instanceof Transformer) {
            $relations = $transformer->relations($this->with);
            $defaultRelations = $this->removeExcludedRelations($transformer->defaultRelations($this->with));
            $this->with = array_merge($relations, $defaultRelations);
        }

        if ($data instanceof Model || $data instanceof Collection) {
            $this->eagerLoadRelations($data, $this->with, $transformer);
        }

        $this->with = array_keys($this->with);
    }

    /**
     * Filter out relations that have been explicitly excluded using the [without] method.
     *
     * @param  array $relations
     * @return array
     */
    protected function removeExcludedRelations(array $relations): array
    {
        return array_filter($relations, function ($relation) {
            return ! in_array($this->stripParametersFromRelation($relation), $this->without);
        }, ARRAY_FILTER_USE_KEY);
    }

    /**
     * Strip parameter suffix from the relation string by only taking what is in front of
     * the colon.
     *
     * @param  string $relation
     * @return string
     */
    protected function stripParametersFromRelation(string $relation): string
    {
        return explode(':', $relation)[0];
    }

    /**
     * Eager load all requested relations except the ones defined as an "include" method
     * in the transformers. We also strip away any parameters from the relation name
     * and normalize relations by swapping "null" constraints to empty closures.
     *
     * @param  mixed                                                          $data
     * @param  array                                                          $requested
     * @param  \Flugg\Responder\Transformers\Transformer|callable|string|null $transformer
     * @return void
     */
    protected function eagerLoadRelations($data, array $requested, $transformer)
    {
        $relations = collect(array_keys($requested))->reduce(function ($eagerLoads, $relation) use ($requested, $transformer) {
            $identifier = camel_case($this->stripParametersFromRelation($relation));

            if (method_exists($transformer, 'include' . ucfirst($identifier))) {
                return $eagerLoads;
            }

            return array_merge($eagerLoads, [$identifier => $requested[$relation] ?: function () { }]);
        }, []);

        $data->load($relations);
    }
}


<?php

namespace Flugg\Responder\Pagination;

use Flugg\Responder\Contracts\Pagination\PaginatorFactory as PaginatorFactoryContract;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use League\Fractal\Pagination\Cursor;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Pagination\PaginatorInterface;

/**
 * A factory class for making Fractal paginator adapters from a Laravel paginator.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class PaginatorFactory implements PaginatorFactoryContract
{
    /**
     * A list of query string values appended to the paginator links.
     *
     * @var array
     */
    protected $parameters;

    /**
     * Construct the factory class.
     *
     * @param array $parameters
     */
    public function __construct(array $parameters)
    {
        $this->parameters = $parameters;
    }

    /**
     * Make a Fractal paginator adapter from a Laravel paginator.
     *
     * @param  \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
     * @return \League\Fractal\Pagination\PaginatorInterface
     */
    public function make(LengthAwarePaginator $paginator): PaginatorInterface
    {
        $paginator->appends($this->parameters);

        return new IlluminatePaginatorAdapter($paginator);
    }

    /**
     * Make a Fractal paginator adapter from a Laravel paginator.
     *
     * @param  \Flugg\Responder\Pagination\CursorPaginator $paginator
     * @return \League\Fractal\Pagination\Cursor
     */
    public function makeCursor(CursorPaginator $paginator): Cursor
    {
        return new Cursor($paginator->cursor(), $paginator->previous(), $paginator->next(), $paginator->get()->count());
    }
}


<?php

namespace Flugg\Responder\Serializers;

use League\Fractal\Pagination\CursorInterface;
use League\Fractal\Pagination\PaginatorInterface;
use League\Fractal\Serializer\ArraySerializer;

/**
 * A serializer class responsible for formatting success data.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class SuccessSerializer extends ArraySerializer
{
    /**
     * Serialize collection resources.
     *
     * @param  string $resourceKey
     * @param  array  $data
     * @return array
     */
    public function collection($resourceKey, array $data)
    {
        return ['data' => $data];
    }

    /**
     * Serialize item resources.
     *
     * @param  string $resourceKey
     * @param  array  $data
     * @return array
     */
    public function item($resourceKey, array $data)
    {
        return ['data' => $data];
    }

    /**
     * Serialize null resources.
     *
     * @return array
     */
    public function null()
    {
        return ['data' => null];
    }

    /**
     * Format meta data.
     *
     * @param  array $meta
     * @return array
     */
    public function meta(array $meta)
    {
        return $meta;
    }

    /**
     * Format pagination data.
     *
     * @param  \League\Fractal\Pagination\PaginatorInterface $paginator
     * @return array
     */
    public function paginator(PaginatorInterface $paginator)
    {
        $pagination = parent::paginator($paginator)['pagination'];

        return [
            'pagination' => [
                'count' => $pagination['count'],
                'total' => $pagination['total'],
                'perPage' => $pagination['per_page'],
                'currentPage' => $pagination['current_page'],
                'totalPages' => $pagination['total_pages'],
                'links' => $pagination['links'],
            ],
        ];
    }

    /**
     * Format cursor data.
     *
     * @param  \League\Fractal\Pagination\CursorInterface $cursor
     * @return array
     */
    public function cursor(CursorInterface $cursor)
    {
        return [
            'cursor' => [
                'current' => $cursor->getCurrent(),
                'previous' => $cursor->getPrev(),
                'next' => $cursor->getNext(),
                'count' => (int) $cursor->getCount(),
            ],
        ];
    }

    /**
     * Merge includes into data.
     *
     * @param  array $transformedData
     * @param  array $includedData
     * @return array
     */
    public function mergeIncludes($transformedData, $includedData)
    {
        foreach (array_keys($includedData) as $key) {
            $includedData[$key] = $includedData[$key]['data'];
        }

        return array_merge($transformedData, $includedData);
    }
}


<?php

namespace Flugg\Responder\Resources;

use Flugg\Responder\Contracts\Resources\ResourceKeyResolver as ResourceKeyResolverContract;
use Illuminate\Database\Eloquent\Model;
use Traversable;

/**
 * This class is responsible for resolving resource keys.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class ResourceKeyResolver implements ResourceKeyResolverContract
{
    /**
     * Transformable to resource key mappings.
     *
     * @var array
     */
    protected $bindings = [];

    /**
     * Register a transformable to resource key binding.
     *
     * @param  string|array $transformable
     * @param  string       $resourceKey
     * @return void
     */
    public function bind($transformable, string $resourceKey)
    {
        $this->bindings = array_merge($this->bindings, is_array($transformable) ? $transformable : [
            $transformable => $resourceKey,
        ]);
    }

    /**
     * Resolve a resource key from the given data.
     *
     * @param  mixed $data
     * @return string
     */
    public function resolve($data)
    {
        $transformable = $this->resolveTransformableItem($data);

        if (is_object($transformable) && key_exists(get_class($transformable), $this->bindings)) {
            return $this->bindings[get_class($transformable)];
        }

        if ($transformable instanceof Model) {
            return $this->resolveFromModel($transformable);
        }

        return 'data';
    }

    /**
     * Resolve a resource key from the given model.
     *
     * @param  \Illuminate\Database\Eloquent\Model $model
     * @return string
     */
    public function resolveFromModel(Model $model)
    {
        if (method_exists($model, 'getResourceKey')) {
            return $model->getResourceKey();
        }

        return $model->getTable();
    }

    /**
     * Resolve a transformable item from the given data.
     *
     * @param  mixed $data
     * @return mixed
     */
    protected function resolveTransformableItem($data)
    {
        if (is_array($data) || $data instanceof Traversable) {
            foreach ($data as $item) {
                return $item;
            }
        }

        return $data;
    }
}


<?php

namespace Flugg\Responder\Transformers;

use Closure;
use Flugg\Responder\Contracts\Transformers\TransformerResolver;
use Flugg\Responder\Transformers\Concerns\HasRelationships;
use Flugg\Responder\Transformers\Concerns\MakesResources;
use Flugg\Responder\Transformers\Concerns\OverridesFractal;
use Illuminate\Contracts\Container\Container;
use League\Fractal\TransformerAbstract;

/**
 * An abstract base transformer class responsible for transforming data.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
abstract class Transformer extends TransformerAbstract
{
    use HasRelationships;
    use MakesResources;
    use OverridesFractal;

    /**
     * The container resolver callback.
     *
     * @var \Closure|null
     */
    protected static $containerResolver;

    /**
     * Set a container using a resolver callback.
     *
     * @param  \Closure $resolver
     * @return void
     */
    public static function containerResolver(Closure $resolver)
    {
        static::$containerResolver = $resolver;
    }

    /**
     * Resolve a container using the resolver callback.
     *
     * @return \Illuminate\Contracts\Container\Container
     */
    protected function resolveContainer(): Container
    {
        return call_user_func(static::$containerResolver);
    }

    /**
     * Resolve a transformer from a class name string.
     *
     * @param  string $transformer
     * @return mixed
     */
    protected function resolveTransformer(string $transformer)
    {
        $transformerResolver = $this->resolveContainer()->make(TransformerResolver::class);

        return $transformerResolver->resolve($transformer);
    }
}


<?php

namespace Flugg\Responder\Exceptions;

use Exception;
use Flugg\Responder\Exceptions\Http\HttpException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

/**
 * An exception handler responsible for handling exceptions.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class Handler extends ExceptionHandler
{
    use ConvertsExceptions;

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Exception               $exception
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function render($request, Exception $exception)
    {
        if ($request->wantsJson()) {
            $this->convertDefaultException($exception);

            if ($exception instanceof HttpException) {
                return $this->renderResponse($exception);
            }
        }

        return parent::render($request, $exception);
    }
}


<?php

namespace Flugg\Responder;

use Flugg\Responder\Contracts\ErrorFactory as ErrorFactoryContract;
use Flugg\Responder\Contracts\ErrorMessageResolver as ErrorMessageResolverContract;
use Flugg\Responder\Contracts\ErrorSerializer;

/**
 * A factory class responsible for creating error arrays.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class ErrorFactory implements ErrorFactoryContract
{
    /**
     * A resolver for resolving messages from error codes.
     *
     * @var \Flugg\Responder\Contracts\ErrorMessageResolver
     */
    protected $messageResolver;

    /**
     * Construct the factory class.
     *
     * @param \Flugg\Responder\Contracts\ErrorMessageResolver $messageResolver
     */
    public function __construct(ErrorMessageResolverContract $messageResolver)
    {
        $this->messageResolver = $messageResolver;
    }

    /**
     * Make an error array from the given error code and message.
     *
     * @param  \Flugg\Responder\Contracts\ErrorSerializer $serializer
     * @param  mixed|null                                 $errorCode
     * @param  string|null                                $message
     * @param  array|null                                 $data
     * @return array
     */
    public function make(ErrorSerializer $serializer, $errorCode = null, string $message = null, array $data = null): array
    {
        if (isset($errorCode) && ! isset($message)) {
            $message = $this->messageResolver->resolve($errorCode);
        }

        return $serializer->format($errorCode, $message, $data);
    }
}


<?php

namespace Flugg\Responder;

use Flugg\Responder\Contracts\TransformFactory;
use League\Fractal\Manager;
use League\Fractal\Resource\ResourceInterface;
use League\Fractal\Serializer\SerializerAbstract;
use LogicException;

/**
 * A factory class responsible for transforming and serializing data utilizing Fractal.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class FractalTransformFactory implements TransformFactory
{
    /**
     * A manager for executing transforms.
     *
     * @var \League\Fractal\Manager
     */
    protected $manager;

    /**
     * Construct the factory class.
     *
     * @param \League\Fractal\Manager $manager
     */
    public function __construct(Manager $manager)
    {
        $this->manager = $manager;
    }

    /**
     * Transform the given resource, and serialize the data with the given serializer.
     *
     * @param  \League\Fractal\Resource\ResourceInterface    $resource
     * @param  \League\Fractal\Serializer\SerializerAbstract $serializer
     * @param  array                                         $options
     * @return array|null
     */
    public function make(ResourceInterface $resource, SerializerAbstract $serializer, array $options = [])
    {
        $options = $this->parseOptions($options, $resource);

        return $this->manager->setSerializer($serializer)
            ->parseIncludes($options['includes'])
            ->parseExcludes($options['excludes'])
            ->parseFieldsets($options['fieldsets'])
            ->createData($resource)
            ->toArray();
    }

    /**
     * Parse the transformation options.
     *
     * @param  array                                      $options
     * @param  \League\Fractal\Resource\ResourceInterface $resource
     * @return array
     */
    protected function parseOptions(array $options, ResourceInterface $resource): array
    {
        $options = array_merge([
            'includes' => [],
            'excludes' => [],
            'fieldsets' => [],
        ], $options);

        if (! empty($options['fieldsets'])) {
            if (is_null($resourceKey = $resource->getResourceKey())) {
                throw new LogicException('Filtering fields using sparse fieldsets require resource key to be set.');
            }

            $options['fieldsets'] = $this->parseFieldsets($options['fieldsets'], $resourceKey, $options['includes']);
        }

        return $options;
    }

    /**
     * Parse the fieldsets for Fractal.
     *
     * @param  array  $fieldsets
     * @param  string $resourceKey
     * @param  array  $includes
     * @return array
     */
    protected function parseFieldsets(array $fieldsets, string $resourceKey, array $includes): array
    {
        $includes = array_map(function ($include) use ($resourceKey) {
            return "$resourceKey.$include";
        }, $includes);

        foreach ($fieldsets as $key => $fields) {
            if (is_numeric($key)) {
                unset($fieldsets[$key]);
                $key = $resourceKey;
            }

            $fields = $this->parseFieldset($key, (array) $fields, $includes);
            $fieldsets[$key] = array_unique(array_merge(key_exists($key, $fieldsets) ? (array) $fieldsets[$key] : [], $fields));
        }

        return array_map(function ($fields) {
            return implode(',', $fields);
        }, $fieldsets);
    }

    /**
     * Parse the given fieldset and append any related resource keys.
     *
     * @param  string $key
     * @param  array  $fields
     * @param  array  $includes
     * @return array
     */
    protected function parseFieldset(string $key, array $fields, array $includes): array
    {
        $childIncludes = array_reduce($includes, function ($segments, $include) use ($key) {
            return array_merge($segments, $this->resolveChildIncludes($key, $include));
        }, []);

        return array_merge($fields, array_unique($childIncludes));
    }

    /**
     * Resolve included segments that are a direct child to the given resource key.
     *
     * @param  string $key
     * @param  string $include
     * @return array
     */
    protected function resolveChildIncludes($key, string $include): array
    {
        if (count($segments = explode('.', $include)) <= 1) {
            return [];
        }

        $relation = $key === array_shift($segments) ? [$segments[0]] : [];

        return array_merge($relation, $this->resolveChildIncludes($key, implode('.', $segments)));
    }
}


<?php

namespace Flugg\Responder;

use Flugg\Responder\Contracts\Responder as ResponderContract;
use Flugg\Responder\Http\Responses\ErrorResponseBuilder;
use Flugg\Responder\Http\Responses\SuccessResponseBuilder;

/**
 * A service class responsible for building responses.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class Responder implements ResponderContract
{
    /**
     * A builder for building success responses.
     *
     * @var \Flugg\Responder\Http\Responses\SuccessResponseBuilder
     */
    protected $successResponseBuilder;

    /**
     * A builder for building error responses.
     *
     * @var \Flugg\Responder\Http\Responses\ErrorResponseBuilder
     */
    protected $errorResponseBuilder;

    /**
     * Construct the service class.
     *
     * @param \Flugg\Responder\Http\Responses\SuccessResponseBuilder $successResponseBuilder
     * @param \Flugg\Responder\Http\Responses\ErrorResponseBuilder   $errorResponseBuilder
     */
    public function __construct(SuccessResponseBuilder $successResponseBuilder, ErrorResponseBuilder $errorResponseBuilder)
    {
        $this->successResponseBuilder = $successResponseBuilder;
        $this->errorResponseBuilder = $errorResponseBuilder;
    }

    /**
     * Build a successful response.
     *
     * @param  mixed                                                          $data
     * @param  callable|string|\Flugg\Responder\Transformers\Transformer|null $transformer
     * @param  string|null                                                    $resourceKey
     * @return \Flugg\Responder\Http\Responses\SuccessResponseBuilder
     */
    public function success($data = null, $transformer = null, string $resourceKey = null): SuccessResponseBuilder
    {
        return $this->successResponseBuilder->transform($data, $transformer, $resourceKey);
    }

    /**
     * Build an error response.
     *
     * @param  mixed|null  $errorCode
     * @param  string|null $message
     * @return \Flugg\Responder\Http\Responses\ErrorResponseBuilder
     */
    public function error($errorCode = null, string $message = null): ErrorResponseBuilder
    {
        return $this->errorResponseBuilder->error($errorCode, $message);
    }
}


<?php

namespace Flugg\Responder;

use Flugg\Responder\Contracts\ErrorMessageResolver as ErrorMessageResolverContract;
use Illuminate\Translation\Translator;

/**
 * A resolver class responsible for resolving messages from error codes.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class ErrorMessageResolver implements ErrorMessageResolverContract
{
    /**
     * A serivce for resolving messages from language files.
     *
     * @var \Illuminate\Translation\Translator
     */
    protected $translator;

    /**
     * A list of registered messages mapped to error codes.
     *
     * @var array
     */
    protected $messages = [];

    /**
     * Construct the resolver class.
     *
     * @param \Illuminate\Translation\Translator $translator
     */
    public function __construct(Translator $translator)
    {
        $this->translator = $translator;
    }

    /**
     * Register a message mapped to an error code.
     *
     * @param  mixed  $errorCode
     * @param  string $message
     * @return void
     */
    public function register($errorCode, string $message)
    {
        $this->messages = array_merge($this->messages, is_array($errorCode) ? $errorCode : [
            $errorCode => $message,
        ]);
    }

    /**
     * Resolve a message from the given error code.
     *
     * @param  mixed $errorCode
     * @return string|null
     */
    public function resolve($errorCode)
    {
        if (key_exists($errorCode, $this->messages)) {
            return $this->messages[$errorCode];
        }

        if ($this->translator->has($errorCode = "errors.$errorCode")) {
            return $this->translator->trans($errorCode);
        }

        return null;
    }
}


<?php

namespace Flugg\Responder\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\TransformsRequest;

/**
 * A middleware class responsible for converting incoming parameter keys to snake case.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class ConvertToSnakeCase extends TransformsRequest
{
    /**
     * A list of attributes that shouldn't be converted.
     *
     * @var array
     */
    protected $except = [
        //
    ];

    /**
     * Clean the data in the given array.
     *
     * @param  array $data
     * @return array
     */
    protected function cleanArray(array $data)
    {
        $parameters = [];

        foreach ($data as $key => $value) {
            $parameters[in_array($key, $this->except) ? $key : snake_case($key)] = $value;
        }

        return $parameters;
    }
}


<?php

namespace Flugg\Responder\Transformers;

use Flugg\Responder\Contracts\Transformable;
use Flugg\Responder\Contracts\Transformers\TransformerResolver as TransformerResolverContract;
use Flugg\Responder\Exceptions\InvalidTransformerException;
use Illuminate\Contracts\Container\Container;
use Traversable;

/**
 * This class is responsible for resolving transformers.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class TransformerResolver implements TransformerResolverContract
{
    /**
     * Transformable to transformer mappings.
     *
     * @var array
     */
    protected $bindings = [];

    /**
     * A container used to resolve transformers.
     *
     * @var \Illuminate\Contracts\Container\Container
     */
    protected $container;

    /**
     * A fallback transformer to return when no transformer can be resolved.
     *
     * @var \Flugg\Responder\Transformers\Transformer|string|callable
     */
    protected $fallback;

    /**
     * Construct the resolver class.
     *
     * @param \Illuminate\Contracts\Container\Container                 $container
     * @param \Flugg\Responder\Transformers\Transformer|string|callable $fallback
     */
    public function __construct(Container $container, $fallback)
    {
        $this->container = $container;
        $this->fallback = $fallback;
    }

    /**
     * Register a transformable to transformer binding.
     *
     * @param  string|array         $transformable
     * @param  string|callback|null $transformer
     * @return void
     */
    public function bind($transformable, $transformer = null)
    {
        $this->bindings = array_merge($this->bindings, is_array($transformable) ? $transformable : [
            $transformable => $transformer,
        ]);
    }

    /**
     * Resolve a transformer.
     *
     * @param  \Flugg\Responder\Transformers\Transformer|string|callable $transformer
     * @return \Flugg\Responder\Transformers\Transformer|callable
     * @throws \Flugg\Responder\Exceptions\InvalidTransformerException
     */
    public function resolve($transformer)
    {
        if (is_string($transformer)) {
            return $this->container->make($transformer);
        }

        if (! is_callable($transformer) && ! $transformer instanceof Transformer) {
            throw new InvalidTransformerException;
        }

        return $transformer;
    }

    /**
     * Resolve a transformer from the given data.
     *
     * @param  mixed $data
     * @return \Flugg\Responder\Transformers\Transformer|callable
     */
    public function resolveFromData($data)
    {
        $transformer = $this->resolveTransformer($this->resolveTransformableItem($data));

        return $this->resolve($transformer);
    }

    /**
     * Resolve a transformer from the transformable element.
     *
     * @param  mixed $transformable
     * @return \Flugg\Responder\Contracts\Transformable|callable
     */
    protected function resolveTransformer($transformable)
    {
        if (is_object($transformable) && key_exists(get_class($transformable), $this->bindings)) {
            return $this->bindings[get_class($transformable)];
        }

        if ($transformable instanceof Transformable) {
            return $transformable->transformer();
        }

        return $this->resolve($this->fallback);
    }

    /**
     * Resolve a transformable item from the given data.
     *
     * @param  mixed $data
     * @return mixed
     */
    protected function resolveTransformableItem($data)
    {
        if (is_array($data) || $data instanceof Traversable) {
            foreach ($data as $item) {
                return $item;
            }
        }

        return $data;
    }
}


<?php

namespace Flugg\Responder;

use Flugg\Responder\Console\MakeTransformer;
use Flugg\Responder\Contracts\ErrorFactory as ErrorFactoryContract;
use Flugg\Responder\Contracts\ErrorMessageResolver as ErrorMessageResolverContract;
use Flugg\Responder\Contracts\ErrorSerializer as ErrorSerializerContract;
use Flugg\Responder\Contracts\Pagination\PaginatorFactory as PaginatorFactoryContract;
use Flugg\Responder\Contracts\Resources\ResourceFactory as ResourceFactoryContract;
use Flugg\Responder\Contracts\Resources\ResourceKeyResolver as ResourceKeyResolverContract;
use Flugg\Responder\Contracts\Responder as ResponderContract;
use Flugg\Responder\Contracts\ResponseFactory;
use Flugg\Responder\Contracts\ResponseFactory as ResponseFactoryContract;
use Flugg\Responder\Contracts\SimpleTransformer as SimpleTransformerContract;
use Flugg\Responder\Contracts\Transformers\TransformerResolver as TransformerResolverContract;
use Flugg\Responder\Contracts\TransformFactory as TransformFactoryContract;
use Flugg\Responder\Http\Responses\ErrorResponseBuilder;
use Flugg\Responder\Http\Responses\Factories\LaravelResponseFactory;
use Flugg\Responder\Http\Responses\Factories\LumenResponseFactory;
use Flugg\Responder\Pagination\PaginatorFactory;
use Flugg\Responder\Resources\ResourceFactory;
use Flugg\Responder\Resources\ResourceKeyResolver;
use Flugg\Responder\Transformers\Transformer as BaseTransformer;
use Flugg\Responder\Transformers\TransformerResolver;
use Illuminate\Contracts\Container\Container;
use Illuminate\Foundation\Application as Laravel;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider as BaseServiceProvider;
use Illuminate\Translation\Translator;
use Laravel\Lumen\Application as Lumen;
use League\Fractal\Manager;
use League\Fractal\Serializer\SerializerAbstract;

/**
 * A service provider class responsible for bootstrapping the parts of the Laravel package.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class ResponderServiceProvider extends BaseServiceProvider
{
    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = false;

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        if ($this->app instanceof Laravel) {
            $this->registerLaravelBindings();
        } elseif ($this->app instanceof Lumen) {
            $this->registerLumenBindings();
        }

        $this->registerSerializerBindings();
        $this->registerErrorBindings();
        $this->registerFractalBindings();
        $this->registerTransformerBindings();
        $this->registerResourceBindings();
        $this->registerPaginationBindings();
        $this->registerTransformationBindings();
        $this->registerServiceBindings();
    }

    /**
     * Register Laravel bindings.
     *
     * @return void
     */
    protected function registerLaravelBindings()
    {
        $this->app->singleton(ResponseFactoryContract::class, function ($app) {
            return $this->decorateResponseFactory($app->make(LaravelResponseFactory::class));
        });
    }

    /**
     * Register Lumen bindings.
     *
     * @return void
     */
    protected function registerLumenBindings()
    {
        $this->app->singleton(ResponseFactoryContract::class, function ($app) {
            return $this->decorateResponseFactory($app->make(LumenResponseFactory::class));
        });

        $this->app->bind(Translator::class, function ($app) {
            return $app['translator'];
        });
    }

    /**
     * Decorate response factories.
     *
     * @param  \Flugg\Responder\Contracts\ResponseFactory $factory
     * @return \Flugg\Responder\Contracts\ResponseFactory
     */
    protected function decorateResponseFactory(ResponseFactoryContract $factory): ResponseFactory
    {
        foreach ($this->app->config['responder.decorators'] as $decorator) {
            $factory = new $decorator($factory);
        };

        return $factory;
    }

    /**
     * Register serializer bindings.
     *
     * @return void
     */
    protected function registerSerializerBindings()
    {
        $this->app->bind(ErrorSerializerContract::class, function ($app) {
            return $app->make($app->config['responder.serializers.error']);
        });

        $this->app->bind(SerializerAbstract::class, function ($app) {
            return $app->make($app->config['responder.serializers.success']);
        });
    }

    /**
     * Register error bindings.
     *
     * @return void
     */
    protected function registerErrorBindings()
    {
        $this->app->singleton(ErrorMessageResolverContract::class, function ($app) {
            return $app->make(ErrorMessageResolver::class);
        });

        $this->app->singleton(ErrorFactoryContract::class, function ($app) {
            return $app->make(ErrorFactory::class);
        });

        $this->app->bind(ErrorResponseBuilder::class, function ($app) {
            return (new ErrorResponseBuilder($app->make(ResponseFactoryContract::class), $app->make(ErrorFactoryContract::class)))->serializer($app->make(ErrorSerializerContract::class));
        });
    }

    /**
     * Register Fractal bindings.
     *
     * @return void
     */
    protected function registerFractalBindings()
    {
        $this->app->bind(Manager::class, function ($app) {
            return (new Manager)->setRecursionLimit($app->config['responder.recursion_limit']);
        });
    }

    /**
     * Register transformer bindings.
     *
     * @return void
     */
    protected function registerTransformerBindings()
    {
        $this->app->singleton(TransformerResolverContract::class, function ($app) {
            return new TransformerResolver($app, $app->config['responder.fallback_transformer']);
        });

        BaseTransformer::containerResolver(function () {
            return $this->app->make(Container::class);
        });
    }

    /**
     * Register pagination bindings.
     *
     * @return void
     */
    protected function registerResourceBindings()
    {
        $this->app->singleton(ResourceKeyResolverContract::class, function ($app) {
            return $app->make(ResourceKeyResolver::class);
        });

        $this->app->singleton(ResourceFactoryContract::class, function ($app) {
            return $app->make(ResourceFactory::class);
        });
    }

    /**
     * Register pagination bindings.
     *
     * @return void
     */
    protected function registerPaginationBindings()
    {
        $this->app->bind(PaginatorFactoryContract::class, function ($app) {
            return new PaginatorFactory($app->make(Request::class)->query());
        });
    }

    /**
     * Register transformation bindings.
     *
     * @return void
     */
    protected function registerTransformationBindings()
    {
        $this->app->bind(TransformFactoryContract::class, function ($app) {
            return $app->make(FractalTransformFactory::class);
        });

        $this->app->bind(TransformBuilder::class, function ($app) {
            $request = $this->app->make(Request::class);
            $relations = $request->input($this->app->config['responder.load_relations_parameter'], []);
            $fieldsets = $request->input($app->config['responder.filter_fields_parameter'], []);

            return (new TransformBuilder($app->make(ResourceFactoryContract::class), $app->make(TransformFactoryContract::class), $app->make(PaginatorFactoryContract::class)))->serializer($app->make(SerializerAbstract::class))
                ->with(is_string($relations) ? explode(',', $relations) : $relations)
                ->only($fieldsets);
        });
    }

    /**
     * Register service bindings.
     *
     * @return void
     */
    protected function registerServiceBindings()
    {
        $this->app->bind(ResponderContract::class, function ($app) {
            return $app->make(Responder::class);
        });
    }

    /**
     * Bootstrap the application events.
     *
     * @return void
     */
    public function boot()
    {
        if ($this->app instanceof Laravel) {
            $this->bootLaravelApplication();
        } elseif ($this->app instanceof Lumen) {
            $this->bootLumenApplication();
        }

        $this->mergeConfigFrom(__DIR__ . '/../config/responder.php', 'responder');
        $this->commands(MakeTransformer::class);
    }

    /**
     * Bootstrap the Laravel application.
     *
     * @return void
     */
    protected function bootLaravelApplication()
    {
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__ . '/../config/responder.php' => config_path('responder.php'),
            ], 'config');
            $this->publishes([
                __DIR__ . '/../resources/lang/en/errors.php' => base_path('resources/lang/en/errors.php'),
            ], 'lang');
        }
    }

    /**
     * Bootstrap the Lumen application.
     *
     * @return void
     */
    protected function bootLumenApplication()
    {
        $this->app->configure('responder');
    }
}


<?php

namespace Flugg\Responder\Pagination;

use Closure;
use Illuminate\Support\Collection;
use LogicException;

/**
 * A paginator class for handling cursor-based pagination.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class CursorPaginator
{
    /**
     * A list of the items being paginated.
     *
     * @var \Illuminate\Support\Collection
     */
    protected $items;

    /**
     * The current cursor reference.
     *
     * @var int|string|null
     */
    protected $cursor;

    /**
     * The previous cursor reference.
     *
     * @var int|string|null
     */
    protected $previousCursor;

    /**
     * The next cursor reference.
     *
     * @var int|string|null
     */
    protected $nextCursor;

    /**
     * The current cursor resolver callback.
     *
     * @var \Closure|null
     */
    protected static $currentCursorResolver;

    /**
     * Create a new paginator instance.
     *
     * @param \Illuminate\Support\Collection|array|null $data
     * @param int|string|null                           $cursor
     * @param int|string|null                           $previousCursor
     * @param int|string|null                           $nextCursor
     */
    public function __construct($data, $cursor, $previousCursor, $nextCursor)
    {
        $this->cursor = $cursor;
        $this->previousCursor = $previousCursor;
        $this->nextCursor = $nextCursor;

        $this->set($data);
    }

    /**
     * Retrieve the current cursor reference.
     *
     * @return int|string|null
     */
    public function cursor()
    {
        return $this->cursor;
    }

    /**
     * Retireve the next cursor reference.
     *
     * @return int|string|null
     */
    public function previous()
    {
        return $this->previousCursor;
    }

    /**
     * Retireve the next cursor reference.
     *
     * @return int|string|null
     */
    public function next()
    {
        return $this->nextCursor;
    }

    /**
     * Get the slice of items being paginated.
     *
     * @return array
     */
    public function items(): array
    {
        return $this->items->all();
    }

    /**
     * Get the paginator's underlying collection.
     *
     * @return \Illuminate\Support\Collection
     */
    public function get(): Collection
    {
        return $this->items;
    }

    /**
     * Set the paginator's underlying collection.
     *
     * @param  \Illuminate\Support\Collection|array|null $data
     * @return self
     */
    public function set($data): CursorPaginator
    {
        $this->items = $data instanceof Collection ? $data : collect($data);

        return $this;
    }

    /**
     * Resolve the current cursor using the cursor resolver.
     *
     * @param  string $name
     * @return mixed
     * @throws \LogicException
     */
    public static function resolveCursor(string $name = 'cursor')
    {
        if (isset(static::$currentCursorResolver)) {
            return call_user_func(static::$currentCursorResolver, $name);
        }

        throw new LogicException("Could not resolve cursor with the name [{$name}].");
    }

    /**
     * Set the current cursor resolver callback.
     *
     * @param  \Closure $resolver
     * @return void
     */
    public static function cursorResolver(Closure $resolver)
    {
        static::$currentCursorResolver = $resolver;
    }
}


<?php

namespace Flugg\Responder\Console;

use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Input\InputOption;

/**
 * An Artisan command class responsible for making transformer classes.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class MakeTransformer extends GeneratorCommand
{
    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'make:transformer';

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

    /**
     * The type of class being generated.
     *
     * @var string
     */
    protected $type = 'Transformer';

    /**
     * Get the stub file for the generator.
     *
     * @return string
     */
    protected function getStub()
    {
        if ($this->option('plain')) {
            return __DIR__ . '/../../resources/stubs/transformer.plain.stub';
        }

        return __DIR__ . '/../../resources/stubs/transformer.model.stub';
    }

    /**
     * Get the default namespace for the class.
     *
     * @param  string $rootNamespace
     * @return string
     */
    protected function getDefaultNamespace($rootNamespace)
    {
        return $rootNamespace . '\Transformers';
    }

    /**
     * Build the class with the given name.
     *
     * @param  string $name
     * @return string
     */
    protected function buildClass($name)
    {
        $replace = [];

        if (! $this->option('model') && ! $this->option('plain')) {
            $this->input->setOption('model', $this->resolveModelFromClassName());
        }

        if ($this->option('model')) {
            $replace = $this->buildModelReplacements($replace);
        }

        return str_replace(array_keys($replace), array_values($replace), parent::buildClass($name));
    }

    /**
     * Resolve a model from the given class name.
     *
     * @return string
     */
    protected function resolveModelFromClassName()
    {
        return 'App\\' . str_replace('Transformer', '', array_last(explode('/', $this->getNameInput())));
    }

    /**
     * Build the model replacement values.
     *
     * @param  array $replace
     * @return array
     */
    protected function buildModelReplacements(array $replace)
    {
        if (! class_exists($modelClass = $this->parseModel($this->option('model')))) {
            if ($this->confirm("A {$modelClass} model does not exist. Do you want to generate it?", true)) {
                $this->call('make:model', ['name' => $modelClass]);
            }
        }

        return array_merge($replace, [
            'DummyFullModelClass' => $modelClass,
            'DummyModelClass' => class_basename($modelClass),
            'DummyModelVariable' => lcfirst(class_basename($modelClass)),
        ]);
    }

    /**
     * Get the fully-qualified model class name.
     *
     * @param  string $model
     * @return string
     */
    protected function parseModel($model)
    {
        if (preg_match('([^A-Za-z0-9_/\\\\])', $model)) {
            throw new InvalidArgumentException('Model name contains invalid characters.');
        }

        $model = trim(str_replace('/', '\\', $model), '\\');

        if (! starts_with($model, $rootNamespace = $this->laravel->getNamespace())) {
            $model = $rootNamespace . $model;
        }

        return $model;
    }

    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return [
            ['model', 'm', InputOption::VALUE_OPTIONAL, 'Generate a model transformer.'],
            ['plain', 'p', InputOption::VALUE_NONE, 'Generate a plain transformer.'],
        ];
    }
}


<?php

namespace Flugg\Responder\Serializers;

use Flugg\Responder\Contracts\ErrorSerializer as ErrorSerializerContract;

/**
 * A serializer class responsible for formatting error data.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class ErrorSerializer implements ErrorSerializerContract
{
    /**
     * Format the error data.
     *
     * @param  mixed|null  $errorCode
     * @param  string|null $message
     * @param  array|null  $data
     * @return array
     */
    public function format($errorCode = null, string $message = null, array $data = null): array
    {
        $response = [
            'error' => [
                'code' => $errorCode,
                'message' => $message,
            ],
        ];

        if (is_array($data)) {
            $response['error'] = array_merge($response['error'], $data);
        }

        return $response;
    }
}


<?php

namespace Flugg\Responder\Transformers\Concerns;

use Illuminate\Database\Eloquent\Model;

/**
 * A trait to be used by a transformer to handle relations
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
trait HasRelationships
{
    /**
     * List of available relations.
     *
     * @var string[]
     */
    protected $relations = [];

    /**
     * A list of autoloaded default relations.
     *
     * @var array
     */
    protected $load = [];

    /**
     * Get a list of whitelisted relations that are requested, including nested relations.
     *
     * @param  array $requested
     * @return array
     */
    public function relations(array $requested = []): array
    {
        $requested = $this->normalizeRelations($requested);
        $relations = $this->applyQueryConstraints($this->extractRelations($requested));
        $nestedRelations = $this->nestedRelations($requested, $relations, 'relations');

        return array_merge($relations, $nestedRelations);
    }

    /**
     * Get a list of default relations including nested relations.
     *
     * @param  array $requested
     * @return array
     */
    public function defaultRelations(array $requested = []): array
    {
        $requested = $this->normalizeRelations($requested);
        $relations = $this->applyQueryConstraints($this->normalizeRelations($this->load));
        $nestedRelations = $this->nestedRelations($relations, array_merge($relations, $requested), 'defaultRelations');

        return array_merge($relations, $nestedRelations);
    }

    /**
     * Get a list of available relations from the transformer with a normalized structure.
     *
     * @return array
     */
    protected function availableRelations(): array
    {
        return $this->normalizeRelations(array_merge($this->relations, $this->load));
    }

    /**
     * Get nested relations from transformers resolved from the $available parameter that
     * also occur in the $requested parameter.
     *
     * @param  array  $requested
     * @param  array  $available
     * @param  string $method
     * @return array
     */
    protected function nestedRelations(array $requested, array $available, string $method): array
    {
        $transformers = $this->mappedTransformers($available);

        return collect(array_keys($transformers))->reduce(function ($nestedRelations, $relation) use ($requested, $method, $transformers) {
            $transformer = $transformers[$relation];
            $children = $this->extractChildRelations($requested, $relation);
            $childRelations = $this->wrapChildRelations($transformer->$method($children), $relation);

            return array_merge($nestedRelations, $childRelations);
        }, []);
    }

    /**
     * Extract available root relations from the given list of relations.
     *
     * @param  array $relations
     * @return array
     */
    protected function extractRelations(array $relations): array
    {
        $available = $this->availableRelations();

        return array_filter($this->mapRelations($relations, function ($relation, $constraint) {
            $identifier = explode('.', $relation)[0];
            $constraint = $identifier === $relation ? $constraint : null;

            return [$identifier => $constraint ?: $this->resolveQueryConstraint($identifier)];
        }), function ($relation) use ($available) {
            return array_has($available, explode(':', $relation)[0]);
        }, ARRAY_FILTER_USE_KEY);
    }

    /**
     * Extract all nested relations under a given identifier.
     *
     * @param  array  $relations
     * @param  string $identifier
     * @return array
     */
    protected function extractChildRelations(array $relations, string $identifier): array
    {
        return array_reduce(array_keys($relations), function ($nested, $relation) use ($relations, $identifier) {
            if (! starts_with($relation, "$identifier.")) {
                return $nested;
            }

            $nestedIdentifier = explode('.', $relation);
            array_shift($nestedIdentifier);

            return array_merge($nested, [implode('.', $nestedIdentifier) => $relations[$relation]]);
        }, []);
    }

    /**
     * Wrap the identifier of each relation of the given list of nested relations with
     * the parent relation identifier using dot notation.
     *
     * @param  array  $nestedRelations
     * @param  string $relation
     * @return array
     */
    protected function wrapChildRelations(array $nestedRelations, string $relation): array
    {
        return $this->mapRelations($nestedRelations, function ($nestedRelation, $constraint) use ($relation) {
            return ["$relation.$nestedRelation" => $constraint];
        });
    }

    /**
     * Normalize relations to force an [identifier => constraint/transformer] structure.
     *
     * @param  array $relations
     * @return array
     */
    protected function normalizeRelations(array $relations): array
    {
        return array_reduce(array_keys($relations), function ($normalized, $relation) use ($relations) {
            if (is_numeric($relation)) {
                return array_merge($normalized, [$relations[$relation] => null]);
            }

            return array_merge($normalized, [$relation => $relations[$relation]]);
        }, []);
    }

    /**
     * Map over a list of relations with the [identifier => constraint/transformer] structure.
     *
     * @param  array    $relations
     * @param  callable $callback
     * @return array
     */
    protected function mapRelations(array $relations, callable $callback): array
    {
        $mapped = [];

        foreach ($relations as $identifier => $value) {
            $mapped = array_merge($mapped, $callback($identifier, $value));
        }

        return $mapped;
    }

    /**
     * Applies any query constraints defined in the transformer to the list of relaations.
     *
     * @param  array $relations
     * @return array
     */
    protected function applyQueryConstraints(array $relations): array
    {
        return $this->mapRelations($relations, function ($relation, $constraint) {
            return [$relation => is_callable($constraint) ? $constraint : $this->resolveQueryConstraint($relation)];
        });
    }

    /**
     * Resolve a query constraint for a given relation identifier.
     *
     * @param  string $identifier
     * @return \Closure|null
     */
    protected function resolveQueryConstraint(string $identifier)
    {
        if (! method_exists($this, $method = 'load' . ucfirst(camel_case($identifier)))) {
            return null;
        }

        return function ($query) use ($method) {
            return $this->$method($query);
        };
    }

    /**
     * Resolve a relation from a model instance and an identifier.
     *
     * @param  \Illuminate\Database\Eloquent\Model $model
     * @param  string                              $identifier
     * @return mixed
     */
    protected function resolveRelation(Model $model, string $identifier)
    {
        $identifier = camel_case($identifier);
        $relation = $model->$identifier;

        if (method_exists($this, $method = 'filter' . ucfirst($identifier))) {
            return $this->$method($relation);
        }

        return $relation;
    }

    /**
     * Resolve a list of transformers from a list of relations mapped to transformers.
     *
     * @param  array $relations
     * @return array
     */
    protected function mappedTransformers(array $relations): array
    {
        $transformers = collect($this->availableRelations())->filter(function ($transformer) {
            return ! is_null($transformer);
        })->map(function ($transformer) {
            return $this->resolveTransformer($transformer);
        })->all();

        return array_intersect_key($transformers, $relations);
    }

    /**
     * Get a related transformer class mapped to a relation identifier.
     *
     * @param  string $identifier
     * @return string|null
     */
    protected function mappedTransformerClass(string $identifier)
    {
        return $this->availableRelations()[$identifier] ?? null;
    }

    /**
     * Resolve a transformer from a class name string.
     *
     * @param  string $transformer
     * @return mixed
     */
    protected abstract function resolveTransformer(string $transformer);
}


<?php

namespace Flugg\Responder\Transformers\Concerns;

use Countable;
use Flugg\Responder\Contracts\Resources\ResourceFactory;
use Illuminate\Contracts\Container\Container;
use Illuminate\Database\Eloquent\Model;
use League\Fractal\Resource\ResourceInterface;
use LogicException;

/**
 * A trait to be used by a transformer to make related resources.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
trait MakesResources
{
    /**
     * A list of cached related resources.
     *
     * @var \League\Fractal\ResourceInterface[]
     */
    protected $resources = [];

    /**
     * Make a resource.
     *
     * @param  mixed                                                          $data
     * @param  \Flugg\Responder\Transformers\Transformer|string|callable|null $transformer
     * @param  string|null                                                    $resourceKey
     * @return \League\Fractal\Resource\ResourceInterface
     */
    protected function resource($data = null, $transformer = null, string $resourceKey = null): ResourceInterface
    {
        if ($data instanceof ResourceInterface) {
            return $data;
        }

        $resourceFactory = $this->resolveContainer()->make(ResourceFactory::class);

        return $resourceFactory->make($data, $transformer, $resourceKey);
    }

    /**
     * Include a related resource.
     *
     * @param  string $identifier
     * @param  mixed  $data
     * @param  array  $parameters
     * @return \League\Fractal\Resource\ResourceInterface
     * @throws \LogicException
     */
    protected function includeResource(string $identifier, $data, array $parameters): ResourceInterface
    {
        $transformer = $this->mappedTransformerClass($identifier);

        if (method_exists($this, $method = 'include' . ucfirst(camel_case($identifier)))) {
            $resource = $this->resource($this->$method($data, collect($parameters)), $transformer, $identifier);
        } elseif ($data instanceof Model) {
            $resource = $this->includeResourceFromModel($data, $identifier, $transformer);
        } else {
            throw new LogicException('Relation [' . $identifier . '] not found in [' . get_class($this) . '].');
        }

        return $resource;
    }

    /**
     * Include a related resource from a model and cache the resource type for following calls.
     *
     * @param  \Illuminate\Database\Eloquent\Model                            $model
     * @param  string                                                         $identifier
     * @param  \Flugg\Responder\Transformers\Transformer|string|callable|null $transformer
     * @return \League\Fractal\Resource\ResourceInterface
     */
    protected function includeResourceFromModel(Model $model, string $identifier, $transformer = null): ResourceInterface
    {
        $data = $this->resolveRelation($model, $identifier);

        if (! $this->shouldCacheResource($data)) {
            return $this->resource($data, $transformer, $identifier);
        } elseif (key_exists($identifier, $this->resources)) {
            return $this->resources[$identifier]->setData($data);
        }

        return $this->resources[$identifier] = $this->resource($data, $transformer, $identifier);
    }

    /**
     * Indicates if the resource should be cached.
     *
     * @param  mixed $data
     * @return bool
     */
    protected function shouldCacheResource($data): bool
    {
        return is_array($data) || $data instanceof Countable ? count($data) > 0 : is_null($data);
    }

    /**
     * Resolve a container using the resolver callback.
     *
     * @return \Illuminate\Contracts\Container\Container
     */
    protected abstract function resolveContainer(): Container;

    /**
     * Resolve relation data from a model.
     *
     * @param  \Illuminate\Database\Eloquent\Model $model
     * @param  string                              $identifier
     * @return mixed
     */
    protected abstract function resolveRelation(Model $model, string $identifier);

    /**
     * Get a related transformer class mapped to a relation identifier.
     *
     * @param  string $identifier
     * @return string
     */
    protected abstract function mappedTransformerClass(string $identifier);
}


<?php

namespace Flugg\Responder\Resources;

use Flugg\Responder\Contracts\Resources\ResourceFactory as ResourceFactoryContract;
use Flugg\Responder\Contracts\Resources\ResourceKeyResolver as ResourceKeyResolverContract;
use Flugg\Responder\Contracts\Transformers\TransformerResolver;
use Illuminate\Support\Arr;
use League\Fractal\Resource\Collection as CollectionResource;
use League\Fractal\Resource\Item as ItemResource;
use League\Fractal\Resource\NullResource;
use League\Fractal\Resource\Primitive;
use League\Fractal\Resource\ResourceInterface;
use Traversable;

/**
 * This class is responsible for making Fractal resources from a variety of data types.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class ResourceFactory implements ResourceFactoryContract
{
    /**
     * A service class, used to normalize data.
     *
     * @var \Flugg\Responder\Resources\DataNormalizer
     */
    protected $normalizer;

    /**
     * A resolver class, used to resolve resource keys.
     *
     * @var \Flugg\Responder\Contracts\Transformers\TransformerResolver
     */
    protected $transformerResolver;

    /**
     * A resolver class, used to resolve resource keys.
     *
     * @var \Flugg\Responder\Contracts\Resources\ResourceKeyResolver
     */
    protected $resourceKeyResolver;

    /**
     * Construct the factory class.
     *
     * @param \Flugg\Responder\Resources\DataNormalizer                   $normalizer
     * @param \Flugg\Responder\Contracts\Transformers\TransformerResolver $transformerResolver
     * @param \Flugg\Responder\Contracts\Resources\ResourceKeyResolver    $resourceKeyResolver
     */
    public function __construct(DataNormalizer $normalizer, TransformerResolver $transformerResolver, ResourceKeyResolverContract $resourceKeyResolver)
    {
        $this->normalizer = $normalizer;
        $this->transformerResolver = $transformerResolver;
        $this->resourceKeyResolver = $resourceKeyResolver;
    }

    /**
     * Make resource from the given data.
     *
     * @param  mixed                                                          $data
     * @param  \Flugg\Responder\Transformers\Transformer|string|callable|null $transformer
     * @param  string|null                                                    $resourceKey
     * @return \League\Fractal\Resource\ResourceInterface
     */
    public function make($data = null, $transformer = null, string $resourceKey = null): ResourceInterface
    {
        if ($data instanceof ResourceInterface) {
            return $this->makeFromResource($data, $transformer, $resourceKey);
        } elseif (is_null($data = $this->normalizer->normalize($data))) {
            return $this->instatiateResource($data, null, $resourceKey);
        }

        $transformer = $this->resolveTransformer($data, $transformer);
        $resourceKey = $this->resolveResourceKey($data, $resourceKey);

        return $this->instatiateResource($data, $transformer, $resourceKey);
    }

    /**
     * Make resource from the given resource.
     *
     * @param  \League\Fractal\Resource\ResourceInterface                     $resource
     * @param  \Flugg\Responder\Transformers\Transformer|string|callable|null $transformer
     * @param  string|null                                                    $resourceKey
     * @return \League\Fractal\Resource\ResourceInterface
     */
    public function makeFromResource(ResourceInterface $resource, $transformer = null, string $resourceKey = null): ResourceInterface
    {
        $transformer = $this->resolveTransformer($resource->getData(), $transformer ?: $resource->getTransformer());
        $resourceKey = $this->resolveResourceKey($resource->getData(), $resourceKey ?: $resource->getResourceKey());

        return $resource->setTransformer($transformer)->setResourceKey($resourceKey);
    }

    /**
     * Instatiate a new resource instance.
     *
     * @param  mixed                                                   $data
     * @param  \Flugg\Responder\Transformers\Transformer|callable|null $transformer
     * @param  string|null                                             $resourceKey
     * @return \League\Fractal\Resource\ResourceInterface
     */
    protected function instatiateResource($data, $transformer = null, string $resourceKey = null): ResourceInterface
    {
        if (is_null($data)) {
            return new NullResource(null, null, $resourceKey);
        } elseif ($this->shouldCreateCollection($data)) {
            return new CollectionResource($data, $transformer, $resourceKey);
        } elseif (is_scalar($data)) {
            return new Primitive($data, $transformer, $resourceKey);
        }

        return new ItemResource($data, $transformer, $resourceKey);
    }

    /**
     * Indicates if the data belongs to a collection resource.
     *
     * @param  mixed $data
     * @return bool
     */
    protected function shouldCreateCollection($data): bool
    {
        if (is_array($data)) {
            return ! Arr::isAssoc($data) && ! is_scalar(Arr::first($data));
        }

        return $data instanceof Traversable;
    }

    /**
     * Resolve a transformer.
     *
     * @param  mixed                                                          $data
     * @param  \Flugg\Responder\Transformers\Transformer|string|callable|null $transformer
     * @return \Flugg\Responder\Transformers\Transformer|callable
     */
    protected function resolveTransformer($data, $transformer)
    {
        if (isset($transformer)) {
            return $this->transformerResolver->resolve($transformer);
        }

        return $this->transformerResolver->resolveFromData($data);
    }

    /**
     * Resolve a resource key.
     *
     * @param  mixed       $data
     * @param  string|null $resourceKey
     * @return null|string
     */
    protected function resolveResourceKey($data, string $resourceKey = null)
    {
        return isset($resourceKey) ? $resourceKey : $this->resourceKeyResolver->resolve($data);
    }
}


<?php

namespace Flugg\Responder\Http\Responses;

use Flugg\Responder\Contracts\ResponseFactory;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;

/**
 * An abstract builder class for building responses.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
abstract class ResponseBuilder implements Arrayable, Jsonable
{
    /**
     * A factory for making responses.
     *
     * @var \Flugg\Responder\Contracts\ResponseFactory
     */
    protected $responseFactory;

    /**
     * A HTTP status code for the response.
     *
     * @var int
     */
    protected $status;

    /**
     * Construct the builder class.
     *
     * @param \Flugg\Responder\Contracts\ResponseFactory $responseFactory
     */
    public function __construct(ResponseFactory $responseFactory)
    {
        $this->responseFactory = $responseFactory;
    }

    /**
     * Decorate the response with the given decorator.
     *
     * @param  string[]|string $decorator
     * @return $this
     */
    public function decorator($decorator)
    {
        $decorators = is_array($decorator) ? $decorator : func_get_args();

        foreach ($decorators as $decorator) {
            $this->responseFactory = new $decorator($this->responseFactory);
        };

        return $this;
    }

    /**
     * Respond with a successful response.
     *
     * @param  int|null $status
     * @param  array    $headers
     * @return \Illuminate\Http\JsonResponse
     */
    public function respond(int $status = null, array $headers = []): JsonResponse
    {
        if (! is_null($status)) {
            $this->setStatusCode($status);
        }

        return $this->responseFactory->make($this->getOutput(), $this->status, $headers);
    }

    /**
     * Convert the response to an array.
     *
     * @return array
     */
    public function toArray(): array
    {
        return $this->respond()->getData(true);
    }

    /**
     * Convert the response to an Illuminate collection.
     *
     * @return \Illuminate\Support\Collection
     */
    public function toCollection(): Collection
    {
        return new Collection($this->toArray());
    }

    /**
     * Convert the response to JSON.
     *
     * @param  int $options
     * @return string
     */
    public function toJson($options = 0): string
    {
        return json_encode($this->toArray(), $options);
    }

    /**
     * Set the HTTP status code for the response.
     *
     * @param  int $status
     * @return void
     */
    protected function setStatusCode(int $status)
    {
        $this->validateStatusCode($this->status = $status);
    }

    /**
     * Get the serialized response output.
     *
     * @return array
     */
    abstract protected function getOutput(): array;

    /**
     * Convert the response to an array.
     *
     * @param  int $status
     * @return void
     */
    abstract protected function validateStatusCode(int $status);
}


<?php

namespace Flugg\Responder\Resources;

use Flugg\Responder\Pagination\CursorPaginator;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Query\Builder;

/**
 * This class is responsible for normalizing resource data.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 */
class DataNormalizer
{
    /**
     * Normalize the data for a resource.
     *
     * @param  mixed $data
     * @return mixed
     */
    public function normalize($data = null)
    {
        if ($this->isInstanceOf($data, [Builder::class, EloquentBuilder::class, CursorPaginator::class])) {
            return $data->get();
        } elseif ($data instanceof Paginator) {
            return $data->getCollection();
        } elseif ($data instanceof Relation) {
            return $this->normalizeRelation($data);
        }

        return $data;
    }

    /**
     * Normalize a relationship.
     *
     * @param  \Illuminate\Database\Eloquent\Relations\Relation $relation
     * @return \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|null
     */
    protected function normalizeRelation(Relation $relation)
    {
        if ($this->isInstanceOf($relation, [BelongsTo::class, HasOne::class, MorphOne::class, MorphTo::class])) {
            return $relation->first();
        }

        return $relation->get();
    }

    /**
     * Indicates if the given data is an instance of any of the given class names.
     *
     * @param  mixed $data
     * @param  array $classes
     * @return bool
     */
    protected function isInstanceOf($data, array $classes): bool
    {
        foreach ($classes as $class) {
            if ($data instanceof $class) {
                return true;
            }
        }

        return false;
    }
}


<?php

namespace Flugg\Responder\Http\Responses;

use BadMethodCallException;
use Flugg\Responder\Contracts\ResponseFactory;
use Flugg\Responder\TransformBuilder;
use InvalidArgumentException;
use League\Fractal\Pagination\Cursor;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Serializer\SerializerAbstract;

/**
 * A builder class for building error responses.
 *
 * @package flugger/laravel-responder
 * @author  Alexander Tømmerås <flugged@gmail.com>
 * @license The MIT License
 *
 * @method $this meta(array $meta)
 * @method $this with(array | string $relations)
 * @method $this without(array | string $relations)
 * @method $this serializer(SerializerAbstract | string $serializer)
 * @method $this paginator(IlluminatePaginatorAdapter $paginator)
 * @method $this cursor(Cursor $cursor)
 */
class SuccessResponseBuilder extends ResponseBuilder
{
    /**
     * A builder for building transformed arrays.
     *
     * @var \Flugg\Responder\TransformBuilder
     */
    protected $transformBuilder;

    /**
     * A HTTP status code for the response.
     *
     * @var int
     */
    protected $status = 200;

    /**
     * Construct the builder class.
     *
     * @param \Flugg\Responder\Contracts\ResponseFactory $responseFactory
     * @param \Flugg\Responder\TransformBuilder          $transformBuilder
     */
    public function __construct(ResponseFactory $responseFactory, TransformBuilder $transformBuilder)
    {
        $this->transformBuilder = $transformBuilder;

        parent::__construct($responseFactory);
    }

    /**
     * Set resource data for the transformation.
     *
     * @param  mixed                                                          $data
     * @param  \Flugg\Responder\Transformers\Transformer|callable|string|null $transformer
     * @param  string|null                                                    $resourceKey
     * @return self
     */
    public function transform($data = null, $transformer = null, string $resourceKey = null): SuccessResponseBuilder
    {
        $this->transformBuilder->resource($data, $transformer, $resourceKey);

        return $this;
    }

    /**
     * Dynamically send calls to the transform builder.
     *
     * @param  string $name
     * @param  array  $arguments
     * @return self|void
     */
    public function __call($name, $arguments)
    {
        if (in_array($name, ['cursor', 'paginator', 'meta', 'with', 'without', 'only', 'serializer'])) {
            $this->transformBuilder->$name(...$arguments);

            return $this;
        }

        throw new BadMethodCallException;
    }

    /**
     * Get the serialized response output.
     *
     * @return mixed
     */
    protected function getOutput(): array
    {
        return $this->transformBuilder->transform();
    }

    /**
     * Validate the HTTP status code for the response.
     *
     * @param  int $status
     * @return void
     * @throws \InvalidArgumentException
     */
    protected function validateStatusCode(int $status)
    {
        if ($status < 100 || $status >= 400) {
            throw new InvalidArgumentException("{$status} is not a valid success HTTP status code.");
        }
    }
}