This commit is contained in:
Manish Verma
2016-12-13 18:18:25 +05:30
parent fc98add11c
commit 2d8e640e9b
2314 changed files with 97798 additions and 75664 deletions

View File

@@ -14,7 +14,7 @@
}
],
"require": {
"php": ">=5.5.9"
"php": ">=5.4.0"
},
"require-dev": {
"ext-fileinfo": "*",

View File

@@ -6,7 +6,6 @@ use DateTime;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Config;
use League\Flysystem\NotSupportedException;
use League\Flysystem\SafeStorage;
use RuntimeException;
abstract class AbstractFtpAdapter extends AbstractAdapter
@@ -26,6 +25,16 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
*/
protected $port = 21;
/**
* @var string|null
*/
protected $username;
/**
* @var string|null
*/
protected $password;
/**
* @var bool
*/
@@ -76,11 +85,6 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
*/
protected $alternativeRecursion = false;
/**
* @var SafeStorage
*/
protected $safeStorage;
/**
* Constructor.
*
@@ -88,7 +92,6 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
*/
public function __construct(array $config)
{
$this->safeStorage = new SafeStorage();
$this->setConfig($config);
}
@@ -223,7 +226,7 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
*/
public function getUsername()
{
return $this->safeStorage->retrieveSafely('username') ?: 'anonymous';
return empty($this->username) ? 'anonymous' : $this->username;
}
/**
@@ -235,7 +238,7 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
*/
public function setUsername($username)
{
$this->safeStorage->storeSafely('username', $username);
$this->username = $username;
return $this;
}
@@ -247,7 +250,7 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
*/
public function getPassword()
{
return $this->safeStorage->retrieveSafely('password');
return $this->password;
}
/**
@@ -259,7 +262,7 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
*/
public function setPassword($password)
{
$this->safeStorage->storeSafely('password', $password);
$this->password = $password;
return $this;
}

View File

@@ -73,13 +73,13 @@ class Local extends AbstractAdapter
{
$root = is_link($root) ? realpath($root) : $root;
$this->permissionMap = array_replace_recursive(static::$permissions, $permissions);
$this->ensureDirectory($root);
$realRoot = $this->ensureDirectory($root);
if ( ! is_dir($root) || ! is_readable($root)) {
if ( ! is_dir($realRoot) || ! is_readable($realRoot)) {
throw new LogicException('The root path ' . $root . ' is not readable.');
}
$this->setPathPrefix($root);
$this->setPathPrefix($realRoot);
$this->writeFlags = $writeFlags;
$this->linkHandling = $linkHandling;
}
@@ -89,7 +89,7 @@ class Local extends AbstractAdapter
*
* @param string $root root directory path
*
* @return void
* @return string real path to root
*
* @throws Exception in case the root directory can not be created
*/
@@ -104,6 +104,8 @@ class Local extends AbstractAdapter
throw new Exception(sprintf('Impossible to create the root directory "%s".', $root));
}
}
return realpath($root);
}
/**
@@ -256,7 +258,7 @@ class Local extends AbstractAdapter
public function listContents($directory = '', $recursive = false)
{
$result = [];
$location = $this->applyPathPrefix($directory);
$location = $this->applyPathPrefix($directory) . $this->pathSeparator;
if ( ! is_dir($location)) {
return [];

View File

@@ -1,39 +0,0 @@
<?php
namespace League\Flysystem;
final class SafeStorage
{
/**
* @var string
*/
private $hash;
/**
* @var array
*/
protected static $safeStorage = [];
public function __construct()
{
$this->hash = spl_object_hash($this);
static::$safeStorage[$this->hash] = [];
}
public function storeSafely($key, $value)
{
static::$safeStorage[$this->hash][$key] = $value;
}
public function retrieveSafely($key)
{
if (array_key_exists($key, static::$safeStorage[$this->hash])) {
return static::$safeStorage[$this->hash][$key];
}
}
public function __destruct()
{
unset(static::$safeStorage[$this->hash]);
}
}

View File

@@ -85,7 +85,7 @@ class Util
$normalized = preg_replace('#\p{C}+|^\./#u', '', $path);
$normalized = static::normalizeRelativePath($normalized);
if (preg_match('#(^|/)\.{2}(/|$)#', $normalized)) {
if (preg_match('#/\.{2}|^\.{2}/|^\.{2}$#', $normalized)) {
throw new LogicException(
'Path is outside of the defined root, path: [' . $path . '], resolved: [' . $normalized . ']'
);
@@ -110,7 +110,7 @@ class Util
$path = preg_replace('#/\.(?=/)|^\./|(/|^)\./?$#', '', $path);
// Regex for resolving relative paths
$regex = '#/*[^/\.]+/\.\.(?=/|$)#Uu';
$regex = '#/*[^/\.]+/\.\.#Uu';
while (preg_match($regex, $path)) {
$path = preg_replace($regex, '', $path);

21
vendor/league/fractal/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013 Phil Sturgeon <me@philsturgeon.uk>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

51
vendor/league/fractal/composer.json vendored Normal file
View File

@@ -0,0 +1,51 @@
{
"name": "league/fractal",
"description": "Handle the output of complex data structures ready for API output.",
"keywords": [
"league",
"api",
"json",
"rest"
],
"homepage": "http://fractal.thephpleague.com/",
"license": "MIT",
"authors": [
{
"name": "Phil Sturgeon",
"email": "me@philsturgeon.uk",
"homepage": "http://philsturgeon.uk/",
"role": "Developer"
}
],
"require": {
"php": ">=5.4"
},
"require-dev": {
"phpunit/phpunit": "~4.0",
"mockery/mockery": "~0.9",
"illuminate/contracts": "~5.0",
"squizlabs/php_codesniffer": "~1.5",
"pagerfanta/pagerfanta": "~1.0.0",
"zendframework/zend-paginator":"~2.3"
},
"suggest": {
"illuminate/pagination": "The Illuminate Pagination component.",
"pagerfanta/pagerfanta": "Pagerfanta Paginator",
"zendframework/zend-paginator": "Zend Framework Paginator"
},
"autoload": {
"psr-4": {
"League\\Fractal\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"League\\Fractal\\Test\\": "test"
}
},
"extra": {
"branch-alias": {
"dev-master": "0.13-dev"
}
}
}

318
vendor/league/fractal/src/Manager.php vendored Normal file
View File

@@ -0,0 +1,318 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal;
use League\Fractal\Resource\ResourceInterface;
use League\Fractal\Serializer\DataArraySerializer;
use League\Fractal\Serializer\SerializerAbstract;
/**
* Manager
*
* Not a wildly creative name, but the manager is what a Fractal user will interact
* with the most. The manager has various configurable options, and allows users
* to create the "root scope" easily.
*/
class Manager
{
/**
* Array of scope identifiers for resources to include.
*
* @var array
*/
protected $requestedIncludes = [];
/**
* Array of scope identifiers for resources to exclude.
*
* @var array
*/
protected $requestedExcludes = [];
/**
* Array containing modifiers as keys and an array value of params.
*
* @var array
*/
protected $includeParams = [];
/**
* The character used to separate modifier parameters.
*
* @var string
*/
protected $paramDelimiter = '|';
/**
* Upper limit to how many levels of included data are allowed.
*
* @var int
*/
protected $recursionLimit = 10;
/**
* Serializer.
*
* @var SerializerAbstract
*/
protected $serializer;
/**
* Create Data.
*
* Main method to kick this all off. Make a resource then pass it over, and use toArray()
*
* @param ResourceInterface $resource
* @param string $scopeIdentifier
* @param Scope $parentScopeInstance
*
* @return Scope
*/
public function createData(ResourceInterface $resource, $scopeIdentifier = null, Scope $parentScopeInstance = null)
{
$scopeInstance = new Scope($this, $resource, $scopeIdentifier);
// Update scope history
if ($parentScopeInstance !== null) {
// This will be the new children list of parents (parents parents, plus the parent)
$scopeArray = $parentScopeInstance->getParentScopes();
$scopeArray[] = $parentScopeInstance->getScopeIdentifier();
$scopeInstance->setParentScopes($scopeArray);
}
return $scopeInstance;
}
/**
* Get Include Params.
*
* @param string $include
*
* @return \League\Fractal\ParamBag
*/
public function getIncludeParams($include)
{
$params = isset($this->includeParams[$include]) ? $this->includeParams[$include] : [];
return new ParamBag($params);
}
/**
* Get Requested Includes.
*
* @return array
*/
public function getRequestedIncludes()
{
return $this->requestedIncludes;
}
/**
* Get Requested Excludes.
*
* @return array
*/
public function getRequestedExcludes()
{
return $this->requestedExcludes;
}
/**
* Get Serializer.
*
* @return SerializerAbstract
*/
public function getSerializer()
{
if (! $this->serializer) {
$this->setSerializer(new DataArraySerializer());
}
return $this->serializer;
}
/**
* Parse Include String.
*
* @param array|string $includes Array or csv string of resources to include
*
* @return $this
*/
public function parseIncludes($includes)
{
// Wipe these before we go again
$this->requestedIncludes = $this->includeParams = [];
if (is_string($includes)) {
$includes = explode(',', $includes);
}
if (! is_array($includes)) {
throw new \InvalidArgumentException(
'The parseIncludes() method expects a string or an array. '.gettype($includes).' given'
);
}
foreach ($includes as $include) {
list($includeName, $allModifiersStr) = array_pad(explode(':', $include, 2), 2, null);
// Trim it down to a cool level of recursion
$includeName = $this->trimToAcceptableRecursionLevel($includeName);
if (in_array($includeName, $this->requestedIncludes)) {
continue;
}
$this->requestedIncludes[] = $includeName;
// No Params? Bored
if ($allModifiersStr === null) {
continue;
}
// Matches multiple instances of 'something(foo|bar|baz)' in the string
// I guess it ignores : so you could use anything, but probably don't do that
preg_match_all('/([\w]+)(\(([^\)]+)\))?/', $allModifiersStr, $allModifiersArr);
// [0] is full matched strings...
$modifierCount = count($allModifiersArr[0]);
$modifierArr = [];
for ($modifierIt = 0; $modifierIt < $modifierCount; $modifierIt++) {
// [1] is the modifier
$modifierName = $allModifiersArr[1][$modifierIt];
// and [3] is delimited params
$modifierParamStr = $allModifiersArr[3][$modifierIt];
// Make modifier array key with an array of params as the value
$modifierArr[$modifierName] = explode($this->paramDelimiter, $modifierParamStr);
}
$this->includeParams[$includeName] = $modifierArr;
}
// This should be optional and public someday, but without it includes would never show up
$this->autoIncludeParents();
return $this;
}
/**
* Parse Exclude String.
*
* @param array|string $excludes Array or csv string of resources to exclude
*
* @return $this
*/
public function parseExcludes($excludes)
{
$this->requestedExcludes = [];
if (is_string($excludes)) {
$excludes = explode(',', $excludes);
}
if (! is_array($excludes)) {
throw new \InvalidArgumentException(
'The parseExcludes() method expects a string or an array. '.gettype($excludes).' given'
);
}
foreach ($excludes as $excludeName) {
$excludeName = $this->trimToAcceptableRecursionLevel($excludeName);
if (in_array($excludeName, $this->requestedExcludes)) {
continue;
}
$this->requestedExcludes[] = $excludeName;
}
return $this;
}
/**
* Set Recursion Limit.
*
* @param int $recursionLimit
*
* @return $this
*/
public function setRecursionLimit($recursionLimit)
{
$this->recursionLimit = $recursionLimit;
return $this;
}
/**
* Set Serializer
*
* @param SerializerAbstract $serializer
*
* @return $this
*/
public function setSerializer(SerializerAbstract $serializer)
{
$this->serializer = $serializer;
return $this;
}
/**
* Auto-include Parents
*
* Look at the requested includes and automatically include the parents if they
* are not explicitly requested. E.g: [foo, bar.baz] becomes [foo, bar, bar.baz]
*
* @internal
*
* @return void
*/
protected function autoIncludeParents()
{
$parsed = [];
foreach ($this->requestedIncludes as $include) {
$nested = explode('.', $include);
$part = array_shift($nested);
$parsed[] = $part;
while (count($nested) > 0) {
$part .= '.'.array_shift($nested);
$parsed[] = $part;
}
}
$this->requestedIncludes = array_values(array_unique($parsed));
}
/**
* Trim to Acceptable Recursion Level
*
* Strip off any requested resources that are too many levels deep, to avoid DiCaprio being chased
* by trains or whatever the hell that movie was about.
*
* @internal
*
* @param string $includeName
*
* @return string
*/
protected function trimToAcceptableRecursionLevel($includeName)
{
return implode('.', array_slice(explode('.', $includeName), 0, $this->recursionLimit));
}
}

View File

@@ -0,0 +1,163 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Pagination;
/**
* A generic cursor adapter.
*
* @author Isern Palaus <ipalaus@ipalaus.com>
* @author Michele Massari <michele@michelemassari.net>
*/
class Cursor implements CursorInterface
{
/**
* Current cursor value.
*
* @var mixed
*/
protected $current;
/**
* Previous cursor value.
*
* @var mixed
*/
protected $prev;
/**
* Next cursor value.
*
* @var mixed
*/
protected $next;
/**
* Items being held for the current cursor position.
*
* @var int
*/
protected $count;
/**
* Create a new Cursor instance.
*
* @param int $current
* @param null $prev
* @param mixed $next
* @param int $count
*
* @return void
*/
public function __construct($current = null, $prev = null, $next = null, $count = null)
{
$this->current = $current;
$this->prev = $prev;
$this->next = $next;
$this->count = $count;
}
/**
* Get the current cursor value.
*
* @return mixed
*/
public function getCurrent()
{
return $this->current;
}
/**
* Set the current cursor value.
*
* @param int $current
*
* @return Cursor
*/
public function setCurrent($current)
{
$this->current = $current;
return $this;
}
/**
* Get the prev cursor value.
*
* @return mixed
*/
public function getPrev()
{
return $this->prev;
}
/**
* Set the prev cursor value.
*
* @param int $prev
*
* @return Cursor
*/
public function setPrev($prev)
{
$this->prev = $prev;
return $this;
}
/**
* Get the next cursor value.
*
* @return mixed
*/
public function getNext()
{
return $this->next;
}
/**
* Set the next cursor value.
*
* @param int $next
*
* @return Cursor
*/
public function setNext($next)
{
$this->next = $next;
return $this;
}
/**
* Returns the total items in the current cursor.
*
* @return int
*/
public function getCount()
{
return $this->count;
}
/**
* Set the total items in the current cursor.
*
* @param int $count
*
* @return Cursor
*/
public function setCount($count)
{
$this->count = $count;
return $this;
}
}

View File

@@ -0,0 +1,48 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Pagination;
/**
* A common interface for cursors to use.
*
* @author Isern Palaus <ipalaus@ipalaus.com>
*/
interface CursorInterface
{
/**
* Get the current cursor value.
*
* @return mixed
*/
public function getCurrent();
/**
* Get the prev cursor value.
*
* @return mixed
*/
public function getPrev();
/**
* Get the next cursor value.
*
* @return mixed
*/
public function getNext();
/**
* Returns the total items in the current cursor.
*
* @return int
*/
public function getCount();
}

View File

@@ -0,0 +1,114 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Pagination;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/**
* A paginator adapter for illuminate/pagination.
*
* @author Maxime Beaudoin <firalabs@gmail.com>
* @author Marc Addeo <marcaddeo@gmail.com>
*/
class IlluminatePaginatorAdapter implements PaginatorInterface
{
/**
* The paginator instance.
*
* @var \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
protected $paginator;
/**
* Create a new illuminate pagination adapter.
*
* @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
*
* @return void
*/
public function __construct(LengthAwarePaginator $paginator)
{
$this->paginator = $paginator;
}
/**
* Get the current page.
*
* @return int
*/
public function getCurrentPage()
{
return $this->paginator->currentPage();
}
/**
* Get the last page.
*
* @return int
*/
public function getLastPage()
{
return $this->paginator->lastPage();
}
/**
* Get the total.
*
* @return int
*/
public function getTotal()
{
return $this->paginator->total();
}
/**
* Get the count.
*
* @return int
*/
public function getCount()
{
return $this->paginator->count();
}
/**
* Get the number per page.
*
* @return int
*/
public function getPerPage()
{
return $this->paginator->perPage();
}
/**
* Get the url for the given page.
*
* @param int $page
*
* @return string
*/
public function getUrl($page)
{
return $this->paginator->url($page);
}
/**
* Get the paginator instance.
*
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
public function getPaginator()
{
return $this->paginator;
}
}

View File

@@ -0,0 +1,132 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Pagination;
use Pagerfanta\Pagerfanta;
/**
* A paginator adapter for pagerfanta/pagerfanta.
*
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*/
class PagerfantaPaginatorAdapter implements PaginatorInterface
{
/**
* The paginator instance.
*
* @var \Pagerfanta\Pagerfanta
*/
protected $paginator;
/**
* The route generator.
*
* @var callable
*/
protected $routeGenerator;
/**
* Create a new pagerfanta pagination adapter.
*
* @param \Pagerfanta\Pagerfanta $paginator
* @param callable $routeGenerator
*
* @return void
*/
public function __construct(Pagerfanta $paginator, $routeGenerator)
{
$this->paginator = $paginator;
$this->routeGenerator = $routeGenerator;
}
/**
* Get the current page.
*
* @return int
*/
public function getCurrentPage()
{
return $this->paginator->getCurrentPage();
}
/**
* Get the last page.
*
* @return int
*/
public function getLastPage()
{
return $this->paginator->getNbPages();
}
/**
* Get the total.
*
* @return int
*/
public function getTotal()
{
return count($this->paginator);
}
/**
* Get the count.
*
* @return int
*/
public function getCount()
{
return count($this->paginator->getCurrentPageResults());
}
/**
* Get the number per page.
*
* @return int
*/
public function getPerPage()
{
return $this->paginator->getMaxPerPage();
}
/**
* Get the url for the given page.
*
* @param int $page
*
* @return string
*/
public function getUrl($page)
{
return call_user_func($this->routeGenerator, $page);
}
/**
* Get the paginator instance.
*
* @return \Pagerfanta\Pagerfanta
*/
public function getPaginator()
{
return $this->paginator;
}
/**
* Get the the route generator.
*
* @return callable
*/
public function getRouteGenerator()
{
return $this->routeGenerator;
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Pagination;
/**
* A common interface for paginators to use
*
* @author Marc Addeo <marcaddeo@gmail.com>
*/
interface PaginatorInterface
{
/**
* Get the current page.
*
* @return int
*/
public function getCurrentPage();
/**
* Get the last page.
*
* @return int
*/
public function getLastPage();
/**
* Get the total.
*
* @return int
*/
public function getTotal();
/**
* Get the count.
*
* @return int
*/
public function getCount();
/**
* Get the number per page.
*
* @return int
*/
public function getPerPage();
/**
* Get the url for the given page.
*
* @param int $page
*
* @return string
*/
public function getUrl($page);
}

View File

@@ -0,0 +1,132 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Pagination;
use Zend\Paginator\Paginator;
/**
* A paginator adapter for zendframework/zend-paginator.
*
* @author Abdul Malik Ikhsan <samsonasik@gmail.com>
*/
class ZendFrameworkPaginatorAdapter implements PaginatorInterface
{
/**
* The paginator instance.
*
* @var \Zend\Paginator\Paginator
*/
protected $paginator;
/**
* The route generator.
*
* @var callable
*/
protected $routeGenerator;
/**
* Create a new zendframework pagination adapter.
*
* @param \Zend\Paginator\Paginator $paginator
* @param callable $routeGenerator
*
* @return void
*/
public function __construct(Paginator $paginator, $routeGenerator)
{
$this->paginator = $paginator;
$this->routeGenerator = $routeGenerator;
}
/**
* Get the current page.
*
* @return int
*/
public function getCurrentPage()
{
return $this->paginator->getCurrentPageNumber();
}
/**
* Get the last page.
*
* @return int
*/
public function getLastPage()
{
return $this->paginator->count();
}
/**
* Get the total.
*
* @return int
*/
public function getTotal()
{
return $this->paginator->getTotalItemCount();
}
/**
* Get the count.
*
* @return int
*/
public function getCount()
{
return $this->paginator->getCurrentItemCount();
}
/**
* Get the number per page.
*
* @return int
*/
public function getPerPage()
{
return $this->paginator->getItemCountPerPage();
}
/**
* Get the url for the given page.
*
* @param int $page
*
* @return string
*/
public function getUrl($page)
{
return call_user_func($this->routeGenerator, $page);
}
/**
* Get the paginator instance.
*
* @return \Zend\Paginator\Paginator
*/
public function getPaginator()
{
return $this->paginator;
}
/**
* Get the the route generator.
*
* @return callable
*/
public function getRouteGenerator()
{
return $this->routeGenerator;
}
}

163
vendor/league/fractal/src/ParamBag.php vendored Normal file
View File

@@ -0,0 +1,163 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal;
/**
* A handy interface for getting at include parameters.
*/
class ParamBag implements \ArrayAccess, \IteratorAggregate
{
/**
* @var array
*/
protected $params = [];
/**
* Create a new parameter bag instance.
*
* @param array $params
*
* @return void
*/
public function __construct(array $params)
{
$this->params = $params;
}
/**
* Get parameter values out of the bag.
*
* @param string $key
*
* @return mixed
*/
public function get($key)
{
return $this->__get($key);
}
/**
* Get parameter values out of the bag via the property access magic method.
*
* @param string $key
*
* @return mixed
*/
public function __get($key)
{
return isset($this->params[$key]) ? $this->params[$key] : null;
}
/**
* Check if a param exists in the bag via an isset() check on the property.
*
* @param string $key
*
* @return bool
*/
public function __isset($key)
{
return isset($this->params[$key]);
}
/**
* Disallow changing the value of params in the data bag via property access.
*
* @param string $key
* @param mixed $value
*
* @throws \LogicException
*
* @return void
*/
public function __set($key, $value)
{
throw new \LogicException('Modifying parameters is not permitted');
}
/**
* Disallow unsetting params in the data bag via property access.
*
* @param string $key
*
* @throws \LogicException
*
* @return void
*/
public function __unset($key)
{
throw new \LogicException('Modifying parameters is not permitted');
}
/**
* Check if a param exists in the bag via an isset() and array access.
*
* @param string $key
*
* @return bool
*/
public function offsetExists($key)
{
return $this->__isset($key);
}
/**
* Get parameter values out of the bag via array access.
*
* @param string $key
*
* @return mixed
*/
public function offsetGet($key)
{
return $this->__get($key);
}
/**
* Disallow changing the value of params in the data bag via array access.
*
* @param string $key
* @param mixed $value
*
* @throws \LogicException
*
* @return void
*/
public function offsetSet($key, $value)
{
throw new \LogicException('Modifying parameters is not permitted');
}
/**
* Disallow unsetting params in the data bag via array access.
*
* @param string $key
*
* @throws \LogicException
*
* @return void
*/
public function offsetUnset($key)
{
throw new \LogicException('Modifying parameters is not permitted');
}
/**
* IteratorAggregate for iterating over the object like an array.
*
* @return \ArrayIterator
*/
public function getIterator()
{
return new \ArrayIterator($this->params);
}
}

View File

@@ -0,0 +1,114 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Resource;
use ArrayIterator;
use League\Fractal\Pagination\CursorInterface;
use League\Fractal\Pagination\PaginatorInterface;
/**
* Resource Collection
*
* The data can be a collection of any sort of data, as long as the
* "collection" is either array or an object implementing ArrayIterator.
*/
class Collection extends ResourceAbstract
{
/**
* A collection of data.
*
* @var array|ArrayIterator
*/
protected $data;
/**
* A the paginator instance.
*
* @var PaginatorInterface
*/
protected $paginator;
/**
* The cursor instance.
*
* @var CursorInterface
*/
protected $cursor;
/**
* Get the paginator instance.
*
* @return PaginatorInterface
*/
public function getPaginator()
{
return $this->paginator;
}
/**
* Determine if the resource has a paginator implementation.
*
* @return bool
*/
public function hasPaginator()
{
return $this->paginator instanceof PaginatorInterface;
}
/**
* Get the cursor instance.
*
* @return CursorInterface
*/
public function getCursor()
{
return $this->cursor;
}
/**
* Determine if the resource has a cursor implementation.
*
* @return bool
*/
public function hasCursor()
{
return $this->cursor instanceof CursorInterface;
}
/**
* Set the paginator instance.
*
* @param PaginatorInterface $paginator
*
* @return $this
*/
public function setPaginator(PaginatorInterface $paginator)
{
$this->paginator = $paginator;
return $this;
}
/**
* Set the cursor instance.
*
* @param CursorInterface $cursor
*
* @return $this
*/
public function setCursor(CursorInterface $cursor)
{
$this->cursor = $cursor;
return $this;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Resource;
/**
* Item Resource
*
* The Item Resource can stored any mixed data, usually an ORM, ODM or
* other sort of intelligent result, DataMapper model, etc but could
* be a basic array, object, or whatever you like.
*/
class Item extends ResourceAbstract
{
//
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Resource;
/**
* Null Resource
*
* The Null Resource represents a resource that doesn't exist. This can be
* useful to indicate that a certain relationship is null in some output
* formats (e.g. JSON API), which require even a relationship that is null at
* the moment to be listed.
*/
class NullResource extends ResourceAbstract
{
/**
* Get the data.
*
* @return mixed
*/
public function getData()
{
// Null has no data associated with it.
}
}

View File

@@ -0,0 +1,182 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Resource;
use League\Fractal\TransformerAbstract;
abstract class ResourceAbstract implements ResourceInterface
{
/**
* Any item to process.
*
* @var mixed
*/
protected $data;
/**
* Array of meta data.
*
* @var array
*/
protected $meta = [];
/**
* The resource key.
*
* @var string
*/
protected $resourceKey;
/**
* A callable to process the data attached to this resource.
*
* @var callable|TransformerAbstract|null
*/
protected $transformer;
/**
* Create a new resource instance.
*
* @param mixed $data
* @param callable|TransformerAbstract|null $transformer
* @param string $resourceKey
*/
public function __construct($data = null, $transformer = null, $resourceKey = null)
{
$this->data = $data;
$this->transformer = $transformer;
$this->resourceKey = $resourceKey;
}
/**
* Get the data.
*
* @return mixed
*/
public function getData()
{
return $this->data;
}
/**
* Set the data.
*
* @param mixed $data
*
* @return $this
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
/**
* Get the meta data.
*
* @return array
*/
public function getMeta()
{
return $this->meta;
}
/**
* Get the meta data.
*
* @param string $metaKey
*
* @return array
*/
public function getMetaValue($metaKey)
{
return $this->meta[$metaKey];
}
/**
* Get the resource key.
*
* @return string
*/
public function getResourceKey()
{
return $this->resourceKey;
}
/**
* Get the transformer.
*
* @return callable|TransformerAbstract
*/
public function getTransformer()
{
return $this->transformer;
}
/**
* Set the transformer.
*
* @param callable|TransformerAbstract $transformer
*
* @return $this
*/
public function setTransformer($transformer)
{
$this->transformer = $transformer;
return $this;
}
/**
* Set the meta data.
*
* @param array $meta
*
* @return $this
*/
public function setMeta(array $meta)
{
$this->meta = $meta;
return $this;
}
/**
* Set the meta data.
*
* @param string $metaKey
* @param mixed $metaValue
*
* @return $this
*/
public function setMetaValue($metaKey, $metaValue)
{
$this->meta[$metaKey] = $metaValue;
return $this;
}
/**
* Set the resource key.
*
* @param string $resourceKey
*
* @return $this
*/
public function setResourceKey($resourceKey)
{
$this->resourceKey = $resourceKey;
return $this;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Resource;
interface ResourceInterface
{
/**
* Get the resource key.
*
* @return string
*/
public function getResourceKey();
/**
* Get the data.
*
* @return mixed
*/
public function getData();
/**
* Get the transformer.
*
* @return callable|\League\Fractal\TransformerAbstract
*/
public function getTransformer();
/**
* Set the data.
*
* @param mixed $data
*
* @return $this
*/
public function setData($data);
/**
* Set the transformer.
*
* @param callable|\League\Fractal\TransformerAbstract $transformer
*
* @return $this
*/
public function setTransformer($transformer);
}

423
vendor/league/fractal/src/Scope.php vendored Normal file
View File

@@ -0,0 +1,423 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
**/
namespace League\Fractal;
use InvalidArgumentException;
use League\Fractal\Resource\Collection;
use League\Fractal\Resource\Item;
use League\Fractal\Resource\NullResource;
use League\Fractal\Resource\ResourceInterface;
use League\Fractal\Serializer\SerializerAbstract;
/**
* Scope
*
* The scope class acts as a tracker, relating a specific resource in a specific
* context. For example, the same resource could be attached to multiple scopes.
* There are root scopes, parent scopes and child scopes.
*/
class Scope
{
/**
* @var array
*/
protected $availableIncludes = [];
/**
* @var string
*/
protected $scopeIdentifier;
/**
* @var \League\Fractal\Manager
*/
protected $manager;
/**
* @var ResourceInterface
*/
protected $resource;
/**
* @var array
*/
protected $parentScopes = [];
/**
* Create a new scope instance.
*
* @param Manager $manager
* @param ResourceInterface $resource
* @param string $scopeIdentifier
*
* @return void
*/
public function __construct(Manager $manager, ResourceInterface $resource, $scopeIdentifier = null)
{
$this->manager = $manager;
$this->resource = $resource;
$this->scopeIdentifier = $scopeIdentifier;
}
/**
* Embed a scope as a child of the current scope.
*
* @internal
*
* @param string $scopeIdentifier
* @param ResourceInterface $resource
*
* @return \League\Fractal\Scope
*/
public function embedChildScope($scopeIdentifier, $resource)
{
return $this->manager->createData($resource, $scopeIdentifier, $this);
}
/**
* Get the current identifier.
*
* @return string
*/
public function getScopeIdentifier()
{
return $this->scopeIdentifier;
}
/**
* Get the unique identifier for this scope.
*
* @param string $appendIdentifier
*
* @return string
*/
public function getIdentifier($appendIdentifier = null)
{
$identifierParts = array_merge($this->parentScopes, [$this->scopeIdentifier, $appendIdentifier]);
return implode('.', array_filter($identifierParts));
}
/**
* Getter for parentScopes.
*
* @return mixed
*/
public function getParentScopes()
{
return $this->parentScopes;
}
/**
* Getter for resource.
*
* @return ResourceInterface
*/
public function getResource()
{
return $this->resource;
}
/**
* Getter for manager.
*
* @return \League\Fractal\Manager
*/
public function getManager()
{
return $this->manager;
}
/**
* Is Requested.
*
* Check if - in relation to the current scope - this specific segment is allowed.
* That means, if a.b.c is requested and the current scope is a.b, then c is allowed. If the current
* scope is a then c is not allowed, even if it is there and potentially transformable.
*
* @internal
*
* @param string $checkScopeSegment
*
* @return bool Returns the new number of elements in the array.
*/
public function isRequested($checkScopeSegment)
{
if ($this->parentScopes) {
$scopeArray = array_slice($this->parentScopes, 1);
array_push($scopeArray, $this->scopeIdentifier, $checkScopeSegment);
} else {
$scopeArray = [$checkScopeSegment];
}
$scopeString = implode('.', (array) $scopeArray);
return in_array($scopeString, $this->manager->getRequestedIncludes());
}
/**
* Is Excluded.
*
* Check if - in relation to the current scope - this specific segment should
* be excluded. That means, if a.b.c is excluded and the current scope is a.b,
* then c will not be allowed in the transformation whether it appears in
* the list of default or available, requested includes.
*
* @internal
*
* @param string $checkScopeSegment
*
* @return bool
*/
public function isExcluded($checkScopeSegment)
{
if ($this->parentScopes) {
$scopeArray = array_slice($this->parentScopes, 1);
array_push($scopeArray, $this->scopeIdentifier, $checkScopeSegment);
} else {
$scopeArray = [$checkScopeSegment];
}
$scopeString = implode('.', (array) $scopeArray);
return in_array($scopeString, $this->manager->getRequestedExcludes());
}
/**
* Push Parent Scope.
*
* Push a scope identifier into parentScopes
*
* @internal
*
* @param string $identifierSegment
*
* @return int Returns the new number of elements in the array.
*/
public function pushParentScope($identifierSegment)
{
return array_push($this->parentScopes, $identifierSegment);
}
/**
* Set parent scopes.
*
* @internal
*
* @param string[] $parentScopes Value to set.
*
* @return $this
*/
public function setParentScopes($parentScopes)
{
$this->parentScopes = $parentScopes;
return $this;
}
/**
* Convert the current data for this scope to an array.
*
* @return array
*/
public function toArray()
{
list($rawData, $rawIncludedData) = $this->executeResourceTransformers();
$serializer = $this->manager->getSerializer();
$data = $this->serializeResource($serializer, $rawData);
// If the serializer wants the includes to be side-loaded then we'll
// serialize the included data and merge it with the data.
if ($serializer->sideloadIncludes()) {
$includedData = $serializer->includedData($this->resource, $rawIncludedData);
// If the serializer wants to inject additional information
// about the included resources, it can do so now.
$data = $serializer->injectData($data, $rawIncludedData);
if ($this->isRootScope()) {
// If the serializer wants to have a final word about all
// the objects that are sideloaded, it can do so now.
$includedData = $serializer->filterIncludes(
$includedData,
$data
);
}
$data = array_merge($data, $includedData);
}
if ($this->resource instanceof Collection) {
if ($this->resource->hasCursor()) {
$pagination = $serializer->cursor($this->resource->getCursor());
} elseif ($this->resource->hasPaginator()) {
$pagination = $serializer->paginator($this->resource->getPaginator());
}
if (! empty($pagination)) {
$this->resource->setMetaValue(key($pagination), current($pagination));
}
}
// Pull out all of OUR metadata and any custom meta data to merge with the main level data
$meta = $serializer->meta($this->resource->getMeta());
return array_merge($data, $meta);
}
/**
* Convert the current data for this scope to JSON.
*
* @return string
*/
public function toJson()
{
return json_encode($this->toArray());
}
/**
* Execute the resources transformer and return the data and included data.
*
* @internal
*
* @return array
*/
protected function executeResourceTransformers()
{
$transformer = $this->resource->getTransformer();
$data = $this->resource->getData();
$transformedData = $includedData = [];
if ($this->resource instanceof Item) {
list($transformedData, $includedData[]) = $this->fireTransformer($transformer, $data);
} elseif ($this->resource instanceof Collection) {
foreach ($data as $value) {
list($transformedData[], $includedData[]) = $this->fireTransformer($transformer, $value);
}
} elseif ($this->resource instanceof NullResource) {
$transformedData = null;
$includedData = [];
} else {
throw new InvalidArgumentException(
'Argument $resource should be an instance of League\Fractal\Resource\Item'
.' or League\Fractal\Resource\Collection'
);
}
return [$transformedData, $includedData];
}
/**
* Serialize a resource
*
* @internal
*
* @param SerializerAbstract $serializer
* @param mixed $data
*
* @return array
*/
protected function serializeResource(SerializerAbstract $serializer, $data)
{
$resourceKey = $this->resource->getResourceKey();
if ($this->resource instanceof Collection) {
return $serializer->collection($resourceKey, $data);
}
if ($this->resource instanceof Item) {
return $serializer->item($resourceKey, $data);
}
return $serializer->null();
}
/**
* Fire the main transformer.
*
* @internal
*
* @param TransformerAbstract|callable $transformer
* @param mixed $data
*
* @return array
*/
protected function fireTransformer($transformer, $data)
{
$includedData = [];
if (is_callable($transformer)) {
$transformedData = call_user_func($transformer, $data);
} else {
$transformer->setCurrentScope($this);
$transformedData = $transformer->transform($data);
}
if ($this->transformerHasIncludes($transformer)) {
$includedData = $this->fireIncludedTransformers($transformer, $data);
$transformedData = $this->manager->getSerializer()->mergeIncludes($transformedData, $includedData);
}
return [$transformedData, $includedData];
}
/**
* Fire the included transformers.
*
* @internal
*
* @param \League\Fractal\TransformerAbstract $transformer
* @param mixed $data
*
* @return array
*/
protected function fireIncludedTransformers($transformer, $data)
{
$this->availableIncludes = $transformer->getAvailableIncludes();
return $transformer->processIncludedResources($this, $data) ?: [];
}
/**
* Determine if a transformer has any available includes.
*
* @internal
*
* @param TransformerAbstract|callable $transformer
*
* @return bool
*/
protected function transformerHasIncludes($transformer)
{
if (! $transformer instanceof TransformerAbstract) {
return false;
}
$defaultIncludes = $transformer->getDefaultIncludes();
$availableIncludes = $transformer->getAvailableIncludes();
return ! empty($defaultIncludes) || ! empty($availableIncludes);
}
/**
* Check, if this is the root scope.
*
* @return bool
*/
protected function isRootScope()
{
return empty($this->parentScopes);
}
}

View File

@@ -0,0 +1,136 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Serializer;
use League\Fractal\Pagination\CursorInterface;
use League\Fractal\Pagination\PaginatorInterface;
use League\Fractal\Resource\ResourceInterface;
class ArraySerializer extends SerializerAbstract
{
/**
* Serialize a collection.
*
* @param string $resourceKey
* @param array $data
*
* @return array
*/
public function collection($resourceKey, array $data)
{
return [$resourceKey ?: 'data' => $data];
}
/**
* Serialize an item.
*
* @param string $resourceKey
* @param array $data
*
* @return array
*/
public function item($resourceKey, array $data)
{
return $data;
}
/**
* Serialize null resource.
*
* @return array
*/
public function null()
{
return [];
}
/**
* Serialize the included data.
*
* @param ResourceInterface $resource
* @param array $data
*
* @return array
*/
public function includedData(ResourceInterface $resource, array $data)
{
return $data;
}
/**
* Serialize the meta.
*
* @param array $meta
*
* @return array
*/
public function meta(array $meta)
{
if (empty($meta)) {
return [];
}
return ['meta' => $meta];
}
/**
* Serialize the paginator.
*
* @param PaginatorInterface $paginator
*
* @return array
*/
public function paginator(PaginatorInterface $paginator)
{
$currentPage = (int) $paginator->getCurrentPage();
$lastPage = (int) $paginator->getLastPage();
$pagination = [
'total' => (int) $paginator->getTotal(),
'count' => (int) $paginator->getCount(),
'per_page' => (int) $paginator->getPerPage(),
'current_page' => $currentPage,
'total_pages' => $lastPage,
];
$pagination['links'] = [];
if ($currentPage > 1) {
$pagination['links']['previous'] = $paginator->getUrl($currentPage - 1);
}
if ($currentPage < $lastPage) {
$pagination['links']['next'] = $paginator->getUrl($currentPage + 1);
}
return ['pagination' => $pagination];
}
/**
* Serialize the cursor.
*
* @param CursorInterface $cursor
*
* @return array
*/
public function cursor(CursorInterface $cursor)
{
$cursor = [
'current' => $cursor->getCurrent(),
'prev' => $cursor->getPrev(),
'next' => $cursor->getNext(),
'count' => (int) $cursor->getCount(),
];
return ['cursor' => $cursor];
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Serializer;
class DataArraySerializer extends ArraySerializer
{
/**
* Serialize a collection.
*
* @param string $resourceKey
* @param array $data
*
* @return array
*/
public function collection($resourceKey, array $data)
{
return ['data' => $data];
}
/**
* Serialize an item.
*
* @param string $resourceKey
* @param array $data
*
* @return array
*/
public function item($resourceKey, array $data)
{
return ['data' => $data];
}
/**
* Serialize null resource.
*
* @return array
*/
public function null()
{
return ['data' => []];
}
}

View File

@@ -0,0 +1,567 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Serializer;
use InvalidArgumentException;
use League\Fractal\Pagination\PaginatorInterface;
use League\Fractal\Resource\ResourceInterface;
class JsonApiSerializer extends ArraySerializer
{
protected $baseUrl;
protected $rootObjects;
/**
* JsonApiSerializer constructor.
*
* @param string $baseUrl
*/
public function __construct($baseUrl = null)
{
$this->baseUrl = $baseUrl;
$this->rootObjects = [];
}
/**
* Serialize a collection.
*
* @param string $resourceKey
* @param array $data
*
* @return array
*/
public function collection($resourceKey, array $data)
{
$resources = [];
foreach ($data as $resource) {
$resources[] = $this->item($resourceKey, $resource)['data'];
}
return ['data' => $resources];
}
/**
* Serialize an item.
*
* @param string $resourceKey
* @param array $data
*
* @return array
*/
public function item($resourceKey, array $data)
{
$id = $this->getIdFromData($data);
$resource = [
'data' => [
'type' => $resourceKey,
'id' => "$id",
'attributes' => $data,
],
];
unset($resource['data']['attributes']['id']);
if ($this->shouldIncludeLinks()) {
$resource['data']['links'] = [
'self' => "{$this->baseUrl}/$resourceKey/$id",
];
}
return $resource;
}
/**
* Serialize the paginator.
*
* @param PaginatorInterface $paginator
*
* @return array
*/
public function paginator(PaginatorInterface $paginator)
{
$currentPage = (int)$paginator->getCurrentPage();
$lastPage = (int)$paginator->getLastPage();
$pagination = [
'total' => (int)$paginator->getTotal(),
'count' => (int)$paginator->getCount(),
'per_page' => (int)$paginator->getPerPage(),
'current_page' => $currentPage,
'total_pages' => $lastPage,
];
$pagination['links'] = [];
$pagination['links']['self'] = $paginator->getUrl($currentPage);
$pagination['links']['first'] = $paginator->getUrl(1);
if ($currentPage > 1) {
$pagination['links']['prev'] = $paginator->getUrl($currentPage - 1);
}
if ($currentPage < $lastPage) {
$pagination['links']['next'] = $paginator->getUrl($currentPage + 1);
}
$pagination['links']['last'] = $paginator->getUrl($lastPage);
return ['pagination' => $pagination];
}
/**
* Serialize the meta.
*
* @param array $meta
*
* @return array
*/
public function meta(array $meta)
{
if (empty($meta)) {
return [];
}
$result['meta'] = $meta;
if (array_key_exists('pagination', $result['meta'])) {
$result['links'] = $result['meta']['pagination']['links'];
unset($result['meta']['pagination']['links']);
}
return $result;
}
/**
* @return array
*/
public function null()
{
return [
'data' => null,
];
}
/**
* Serialize the included data.
*
* @param ResourceInterface $resource
* @param array $data
*
* @return array
*/
public function includedData(ResourceInterface $resource, array $data)
{
list($serializedData, $linkedIds) = $this->pullOutNestedIncludedData($data);
foreach ($data as $value) {
foreach ($value as $includeObject) {
if ($this->isNull($includeObject) || $this->isEmpty($includeObject)) {
continue;
}
$includeObjects = $this->createIncludeObjects($includeObject);
foreach ($includeObjects as $object) {
$includeType = $object['type'];
$includeId = $object['id'];
$cacheKey = "$includeType:$includeId";
if (!array_key_exists($cacheKey, $linkedIds)) {
$serializedData[] = $object;
$linkedIds[$cacheKey] = $object;
}
}
}
}
return empty($serializedData) ? [] : ['included' => $serializedData];
}
/**
* Indicates if includes should be side-loaded.
*
* @return bool
*/
public function sideloadIncludes()
{
return true;
}
/**
* @param array $data
* @param array $includedData
*
* @return array
*/
public function injectData($data, $includedData)
{
$relationships = $this->parseRelationships($includedData);
if (!empty($relationships)) {
$data = $this->fillRelationships($data, $relationships);
}
return $data;
}
/**
* Hook to manipulate the final sideloaded includes.
* The JSON API specification does not allow the root object to be included
* into the sideloaded `included`-array. We have to make sure it is
* filtered out, in case some object links to the root object in a
* relationship.
*
* @param array $includedData
* @param array $data
*
* @return array
*/
public function filterIncludes($includedData, $data)
{
if (!isset($includedData['included'])) {
return $includedData;
}
// Create the RootObjects
$this->createRootObjects($data);
// Filter out the root objects
$filteredIncludes = array_filter($includedData['included'], [$this, 'filterRootObject']);
// Reset array indizes
$includedData['included'] = array_merge([], $filteredIncludes);
return $includedData;
}
/**
* Filter function to delete root objects from array.
*
* @param array $object
*
* @return bool
*/
protected function filterRootObject($object)
{
return !$this->isRootObject($object);
}
/**
* Set the root objects of the JSON API tree.
*
* @param array $objects
*/
protected function setRootObjects(array $objects = [])
{
$this->rootObjects = array_map(function ($object) {
return "{$object['type']}:{$object['id']}";
}, $objects);
}
/**
* Determines whether an object is a root object of the JSON API tree.
*
* @param array $object
*
* @return bool
*/
protected function isRootObject($object)
{
$objectKey = "{$object['type']}:{$object['id']}";
return in_array($objectKey, $this->rootObjects);
}
/**
* @param array $data
*
* @return bool
*/
protected function isCollection($data)
{
return array_key_exists('data', $data) &&
array_key_exists(0, $data['data']);
}
/**
* @param array $data
*
* @return bool
*/
protected function isNull($data)
{
return array_key_exists('data', $data) && $data['data'] === null;
}
/**
* @param array $data
*
* @return bool
*/
protected function isEmpty($data)
{
return array_key_exists('data', $data) && $data['data'] === [];
}
/**
* @param array $data
* @param array $relationships
*
* @return array
*/
protected function fillRelationships($data, $relationships)
{
if ($this->isCollection($data)) {
foreach ($relationships as $key => $relationship) {
$data = $this->fillRelationshipAsCollection($data, $relationship, $key);
}
} else { // Single resource
foreach ($relationships as $key => $relationship) {
$data = $this->fillRelationshipAsSingleResource($data, $relationship, $key);
}
}
return $data;
}
/**
* @param array $includedData
*
* @return array
*/
protected function parseRelationships($includedData)
{
$relationships = [];
foreach ($includedData as $key => $inclusion) {
foreach ($inclusion as $includeKey => $includeObject) {
$relationships = $this->buildRelationships($includeKey, $relationships, $includeObject, $key);
}
}
return $relationships;
}
/**
* @param array $data
*
* @return integer
*/
protected function getIdFromData(array $data)
{
if (!array_key_exists('id', $data)) {
throw new InvalidArgumentException(
'JSON API resource objects MUST have a valid id'
);
}
return $data['id'];
}
/**
* Keep all sideloaded inclusion data on the top level.
*
* @param array $data
*
* @return array
*/
protected function pullOutNestedIncludedData(array $data)
{
$includedData = [];
$linkedIds = [];
foreach ($data as $value) {
foreach ($value as $includeObject) {
if (isset($includeObject['included'])) {
foreach ($includeObject['included'] as $object) {
$includeType = $object['type'];
$includeId = $object['id'];
$cacheKey = "$includeType:$includeId";
if (!array_key_exists($cacheKey, $linkedIds)) {
$includedData[] = $object;
$linkedIds[$cacheKey] = $object;
}
}
}
}
}
return [$includedData, $linkedIds];
}
/**
* Whether or not the serializer should include `links` for resource objects.
*
* @return bool
*/
protected function shouldIncludeLinks()
{
return $this->baseUrl !== null;
}
/**
* Check if the objects are part of a collection or not
*
* @param $includeObject
*
* @return array
*/
private function createIncludeObjects($includeObject)
{
if ($this->isCollection($includeObject)) {
$includeObjects = $includeObject['data'];
return $includeObjects;
} else {
$includeObjects = [$includeObject['data']];
return $includeObjects;
}
}
/**
* Sets the RootObjects, either as collection or not.
*
* @param $data
*/
private function createRootObjects($data)
{
if ($this->isCollection($data)) {
$this->setRootObjects($data['data']);
} else {
$this->setRootObjects([$data['data']]);
}
}
/**
* Loops over the relationships of the provided data and formats it
*
* @param $data
* @param $relationship
* @param $key
*
* @return array
*/
private function fillRelationshipAsCollection($data, $relationship, $key)
{
foreach ($relationship as $index => $relationshipData) {
$data['data'][$index]['relationships'][$key] = $relationshipData;
if ($this->shouldIncludeLinks()) {
$data['data'][$index]['relationships'][$key] = array_merge([
'links' => [
'self' => "{$this->baseUrl}/{$data['data'][$index]['type']}/{$data['data'][$index]['id']}/relationships/$key",
'related' => "{$this->baseUrl}/{$data['data'][$index]['type']}/{$data['data'][$index]['id']}/$key",
],
], $data['data'][$index]['relationships'][$key]);
}
}
return $data;
}
/**
* @param $data
* @param $relationship
* @param $key
*
* @return array
*/
private function fillRelationshipAsSingleResource($data, $relationship, $key)
{
$data['data']['relationships'][$key] = $relationship[0];
if ($this->shouldIncludeLinks()) {
$data['data']['relationships'][$key] = array_merge([
'links' => [
'self' => "{$this->baseUrl}/{$data['data']['type']}/{$data['data']['id']}/relationships/$key",
'related' => "{$this->baseUrl}/{$data['data']['type']}/{$data['data']['id']}/$key",
],
], $data['data']['relationships'][$key]);
return $data;
}
return $data;
}
/**
* @param $includeKey
* @param $relationships
* @param $includeObject
* @param $key
*
* @return array
*/
private function buildRelationships($includeKey, $relationships, $includeObject, $key)
{
$relationships = $this->addIncludekeyToRelationsIfNotSet($includeKey, $relationships);
if ($this->isNull($includeObject)) {
$relationship = $this->null();
} elseif ($this->isEmpty($includeObject)) {
$relationship = [
'data' => [],
];
} elseif ($this->isCollection($includeObject)) {
$relationship = ['data' => []];
$relationship = $this->addIncludedDataToRelationship($includeObject, $relationship);
} else {
$relationship = [
'data' => [
'type' => $includeObject['data']['type'],
'id' => $includeObject['data']['id'],
],
];
}
$relationships[$includeKey][$key] = $relationship;
return $relationships;
}
/**
* @param $includeKey
* @param $relationships
*
* @return array
*/
private function addIncludekeyToRelationsIfNotSet($includeKey, $relationships)
{
if (!array_key_exists($includeKey, $relationships)) {
$relationships[$includeKey] = [];
return $relationships;
}
return $relationships;
}
/**
* @param $includeObject
* @param $relationship
*
* @return array
*/
private function addIncludedDataToRelationship($includeObject, $relationship)
{
foreach ($includeObject['data'] as $object) {
$relationship['data'][] = [
'type' => $object['type'],
'id' => $object['id'],
];
}
return $relationship;
}
}

View File

@@ -0,0 +1,130 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal\Serializer;
use League\Fractal\Pagination\CursorInterface;
use League\Fractal\Pagination\PaginatorInterface;
use League\Fractal\Resource\ResourceInterface;
abstract class SerializerAbstract
{
/**
* Serialize a collection.
*
* @param string $resourceKey
* @param array $data
*
* @return array
*/
abstract public function collection($resourceKey, array $data);
/**
* Serialize an item.
*
* @param string $resourceKey
* @param array $data
*
* @return array
*/
abstract public function item($resourceKey, array $data);
/**
* Serialize null resource.
*
* @return array
*/
abstract public function null();
/**
* Serialize the included data.
*
* @param ResourceInterface $resource
* @param array $data
*
* @return array
*/
abstract public function includedData(ResourceInterface $resource, array $data);
/**
* Serialize the meta.
*
* @param array $meta
*
* @return array
*/
abstract public function meta(array $meta);
/**
* Serialize the paginator.
*
* @param PaginatorInterface $paginator
*
* @return array
*/
abstract public function paginator(PaginatorInterface $paginator);
/**
* Serialize the cursor.
*
* @param CursorInterface $cursor
*
* @return array
*/
abstract public function cursor(CursorInterface $cursor);
public function mergeIncludes($transformedData, $includedData)
{
// If the serializer does not want the includes to be side-loaded then
// the included data must be merged with the transformed data.
if (! $this->sideloadIncludes()) {
return array_merge($transformedData, $includedData);
}
return $transformedData;
}
/**
* Indicates if includes should be side-loaded.
*
* @return bool
*/
public function sideloadIncludes()
{
return false;
}
/**
* Hook for the serializer to inject custom data based on the relationships of the resource.
*
* @param array $data
* @param array $rawIncludedData
*
* @return array
*/
public function injectData($data, $rawIncludedData)
{
return $data;
}
/**
* Hook for the serializer to modify the final list of includes.
*
* @param array $includedData
* @param array $data
*
* @return array
*/
public function filterIncludes($includedData, $data)
{
return $includedData;
}
}

View File

@@ -0,0 +1,283 @@
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me@philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal;
use League\Fractal\Resource\Collection;
use League\Fractal\Resource\Item;
use League\Fractal\Resource\NullResource;
use League\Fractal\Resource\ResourceInterface;
/**
* Transformer Abstract
*
* All Transformer classes should extend this to utilize the convenience methods
* collection() and item(), and make the self::$availableIncludes property available.
* Extend it and add a `transform()` method to transform any default or included data
* into a basic array.
*/
abstract class TransformerAbstract
{
/**
* Resources that can be included if requested.
*
* @var array
*/
protected $availableIncludes = [];
/**
* Include resources without needing it to be requested.
*
* @var array
*/
protected $defaultIncludes = [];
/**
* The transformer should know about the current scope, so we can fetch relevant params.
*
* @var Scope
*/
protected $currentScope;
/**
* Getter for availableIncludes.
*
* @return array
*/
public function getAvailableIncludes()
{
return $this->availableIncludes;
}
/**
* Getter for defaultIncludes.
*
* @return array
*/
public function getDefaultIncludes()
{
return $this->defaultIncludes;
}
/**
* Getter for currentScope.
*
* @return \League\Fractal\Scope
*/
public function getCurrentScope()
{
return $this->currentScope;
}
/**
* Figure out which includes we need.
*
* @internal
*
* @param Scope $scope
*
* @return array
*/
private function figureOutWhichIncludes(Scope $scope)
{
$includes = $this->getDefaultIncludes();
foreach ($this->getAvailableIncludes() as $include) {
if ($scope->isRequested($include)) {
$includes[] = $include;
}
}
foreach ($includes as $include) {
if ($scope->isExcluded($include)) {
$includes = array_diff($includes, [$include]);
}
}
return $includes;
}
/**
* This method is fired to loop through available includes, see if any of
* them are requested and permitted for this scope.
*
* @internal
*
* @param Scope $scope
* @param mixed $data
*
* @return array
*/
public function processIncludedResources(Scope $scope, $data)
{
$includedData = [];
$includes = $this->figureOutWhichIncludes($scope);
foreach ($includes as $include) {
$includedData = $this->includeResourceIfAvailable(
$scope,
$data,
$includedData,
$include
);
}
return $includedData === [] ? false : $includedData;
}
/**
* Include a resource only if it is available on the method.
*
* @internal
*
* @param Scope $scope
* @param mixed $data
* @param array $includedData
* @param string $include
*
* @return array
*/
private function includeResourceIfAvailable(
Scope $scope,
$data,
$includedData,
$include
) {
if ($resource = $this->callIncludeMethod($scope, $include, $data)) {
$childScope = $scope->embedChildScope($include, $resource);
$includedData[$include] = $childScope->toArray();
}
return $includedData;
}
/**
* Call Include Method.
*
* @internal
*
* @param Scope $scope
* @param string $includeName
* @param mixed $data
*
* @throws \Exception
*
* @return \League\Fractal\Resource\ResourceInterface
*/
protected function callIncludeMethod(Scope $scope, $includeName, $data)
{
$scopeIdentifier = $scope->getIdentifier($includeName);
$params = $scope->getManager()->getIncludeParams($scopeIdentifier);
// Check if the method name actually exists
$methodName = 'include'.str_replace(' ', '', ucwords(str_replace('_', ' ', str_replace('-', ' ', $includeName))));
$resource = call_user_func([$this, $methodName], $data, $params);
if ($resource === null) {
return false;
}
if (! $resource instanceof ResourceInterface) {
throw new \Exception(sprintf(
'Invalid return value from %s::%s(). Expected %s, received %s.',
__CLASS__,
$methodName,
'League\Fractal\Resource\ResourceInterface',
is_object($resource) ? get_class($resource) : gettype($resource)
));
}
return $resource;
}
/**
* Setter for availableIncludes.
*
* @param array $availableIncludes
*
* @return $this
*/
public function setAvailableIncludes($availableIncludes)
{
$this->availableIncludes = $availableIncludes;
return $this;
}
/**
* Setter for defaultIncludes.
*
* @param array $defaultIncludes
*
* @return $this
*/
public function setDefaultIncludes($defaultIncludes)
{
$this->defaultIncludes = $defaultIncludes;
return $this;
}
/**
* Setter for currentScope.
*
* @param Scope $currentScope
*
* @return $this
*/
public function setCurrentScope($currentScope)
{
$this->currentScope = $currentScope;
return $this;
}
/**
* Create a new item resource object.
*
* @param mixed $data
* @param TransformerAbstract|callable $transformer
* @param string $resourceKey
*
* @return Item
*/
protected function item($data, $transformer, $resourceKey = null)
{
return new Item($data, $transformer, $resourceKey);
}
/**
* Create a new collection resource object.
*
* @param mixed $data
* @param TransformerAbstract|callable $transformer
* @param string $resourceKey
*
* @return Collection
*/
protected function collection($data, $transformer, $resourceKey = null)
{
return new Collection($data, $transformer, $resourceKey);
}
/**
* Create a new null resource object.
*
* @return NullResource
*/
protected function null()
{
return new NullResource();
}
}