Build an API sometimes may usually be exhausting, over all, if it was existing changes continuous in databases. Is very common created a class and this class pass him parameter, in small applications this frequently easy, but in a big application, ¿how resolved this?. The response is transform.
Default Laravel provides a class in his dependencies, that allow to create each model his respective manipulation information.
This class will we can found in the file app/vendor/league/fractal/src/TransformerAbstract, This is an abstract class which will intercept each one of our models.
<?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\Primitive;
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);
if ($childScope->getResource() instanceof Primitive) {
$includedData[$include] = $childScope->transformPrimitiveResource();
} else {
$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 primitive resource object.
*
* @param mixed $data
* @param callable|null $transformer
* @param string $resourceKey
*
* @return Primitive
*/
protected function primitive($data, $transformer = null, $resourceKey = null)
{
return new Primitive($data, $transformer, $resourceKey);
}
/**
* 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();
}
}
¿So, how uses that? Sample, first we go to the console, we will write the next command: php artisan:make transformer. Automatically, Laravel will has created for our a class with all necessary an used.
php artisan make:transformer MyFolder(optional)/MyTransformer
Then will we go to our model that we want to apply a transform:
In we model instancing a property public
that property will equal to instance of our transformer
Working in the necessary in our model (queries, relationships, anyway)
we are ready
<?php
namespace App\Models;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Model;
use Silber\Bouncer\Database\Role as BounceRole;
use App\Transformers\RoleTransformer;
class Role extends BounceRole
{
const TYPE_ADMIN = 'admin';
const TYPE_USER = 'user';
public $transformer = RoleTransformer::class;
protected $table = 'participant.roles';
protected $fillable = [
'name',
'title',
'type',
'user_limit',
];
}
Now will we go to file transform, previously created. Once there, import our model and do we next.
We pass our model to the transform method.
Instantiate other models and relationships you have associated with your model.
-Build a solid data structure consistent for your users, and that do not require changes.
<?php
namespace App\Transformers;
use App\Models\Role;
use League\Fractal\TransformerAbstract;
class RoleTransformer extends TransformerAbstract
{
/**
* List of resources to automatically include
*
* @var array
*/
protected $defaultIncludes = [
//
];
/**
* List of resources possible to include
*
* @var array
*/
protected $availableIncludes = [
//
];
/**
* A Fractal transformer.
*
* @return array
*/
public function transform(Role $role)
{
$abilities = $role->getAbilities()->map(function ($ability) {
return $ability->only(['id', 'name', 'title']);
});
return $role->only('id', 'name', 'title', 'type') + [ 'userLimit' => $role->user_limit, 'abilities' => $abilities ];
}
}
And Done !!! your structure should look something like this:
{
"status": 200,
"content": [
{
"id": 7,
"codRole": "DIR",
"nbName": "Director",
"createdAt": "2022-0120T15:57:34.000000Z",
"updatedAt": "2022-0120T15:57:34.000000Z",
"estatus": {
"id": 1,
"code": "AR",
"name": "ACTIVE"
}
}
Top comments (0)