Skip to content

How to transform data

Data transformation is useful when sending data to the front, which happens in the Entity List getListData(), and in the Form or Show Page find() methods.

PRE-REQUISITES

Note that transformers need your data models to allow direct access to their attributes, like for instance product-⁠>price, and to implement Illuminate\Contracts\Support\Arrayable interface. Eloquent models fulfill those needs.

The transform() function

In an Entity List, a Show Page or a Form, you can use the transform() function which will:

  • apply all custom transformers on your list (see below),
  • transform the given model(s) into an array, handling pagination if a LengthAwarePaginator is provided.

Eloquent example in an Entity List:

php
class ProductEntityList extends SharpEntityList
{
    // [...]
    
    public function getListData(): array|Arrayable
    {
        return $this->transform(
            Product::with('pictures')
                ->paginate(50)
        );
    }
}
class ProductEntityList extends SharpEntityList
{
    // [...]
    
    public function getListData(): array|Arrayable
    {
        return $this->transform(
            Product::with('pictures')
                ->paginate(50)
        );
    }
}

Eloquent example in a Form (or a Show Page, they share the API):

php
class ProductForm extends SharpForm
{
    // [...]
    
    public function find($id): array
    {
        return $this->transform(
            Product::findOrFail($id)
        );
    }
}
class ProductForm extends SharpForm
{
    // [...]
    
    public function find($id): array
    {
        return $this->transform(
            Product::findOrFail($id)
        );
    }
}

Custom transformers

Wa can handle transformations with setCustomTransformer():

php
class ProductEntityList extends SharpEntityList
{
    // [...]
    
    function getListData(): array|Arrayable
    {
        return $this
            ->setCustomTransformer(
                'price',
                function ($price, $product, $attribute) {
                    return number_format($price, 2).' €';
                }
            )
            ->transform(
                Product::with('pictures')
                    ->paginate(50)
            );
    }
}
class ProductEntityList extends SharpEntityList
{
    // [...]
    
    function getListData(): array|Arrayable
    {
        return $this
            ->setCustomTransformer(
                'price',
                function ($price, $product, $attribute) {
                    return number_format($price, 2).' €';
                }
            )
            ->transform(
                Product::with('pictures')
                    ->paginate(50)
            );
    }
}

The setCustomTransformer() function takes the key of the attribute to transform, and either a Closure, an instance of a class which implements Code16\Sharp\Utils\Transformers\SharpAttributeTransformer, or even just the full class name of the latest.

TIP

Note that a custom transformer defined on a missing attribute will add the attribute to the result array. It's a convenient way to add a computed attribute, like for instance a full_name built with a bunch of real attributes.
But if this isn't the wanted behaviour, the solution is to define in the SharpAttributeTransformer implementation a public applyIfAttributeIsMissing() function, which when returning false ensure that Sharp will ignore the attribute if it is missing.

Sometimes you would like to transform an attribute of a related model in a hasMany relationship. For instance let's say you want to display the names of the sons of a father in caps:

php
return $this
    ->setCustomTransformer(
        "sons[name]",
        fn ($son) => strtoupper($son->name)
    )
    ->transform($father);
return $this
    ->setCustomTransformer(
        "sons[name]",
        fn ($son) => strtoupper($son->name)
    )
    ->transform($father);

The convention in this case is to use an array notation, given that $father-⁠>sons is a collection of objects with a name attribute

The ":" separator and transformers

Sometimes you'll need to reference a related attribute, like for instance the name of the author of a Post, either in an Entity List:

php
class ProductEntityList extends SharpEntityList
{
    // [...]
    
    function buildList(EntityListFieldsContainer $fields): void
    {
        $fieldsContainer->addField(
            EntityListDataContainer::make('author:name')
                ->setLabel('Author')
        );
    }
}
class ProductEntityList extends SharpEntityList
{
    // [...]
    
    function buildList(EntityListFieldsContainer $fields): void
    {
        $fieldsContainer->addField(
            EntityListDataContainer::make('author:name')
                ->setLabel('Author')
        );
    }
}

or in a Form / Show Page:

php
class ProductForm extends SharpForm
{
    // [...]
    
    function buildFormFields(FieldsContainer $formFields): void
    {
        $formFields->addField(
            SharpFormTextField::make('picture:legend')
                ->setLabel('Legend')
        );
    }
}
class ProductForm extends SharpForm
{
    // [...]
    
    function buildFormFields(FieldsContainer $formFields): void
    {
        $formFields->addField(
            SharpFormTextField::make('picture:legend')
                ->setLabel('Legend')
        );
    }
}

The : separator used here will be interpreted in transform(), and the $post-⁠>author-⁠>name attribute will be used.

Released under the MIT License.