Skip to main content
The data pipeline configures how data objects are constructed from a payload. A normalized array is passed through multiple pipes that transform it into property values for the data object constructor.

Default Pipeline

The pipeline consists of these pipes by default:
  • AuthorizedDataPipe - Checks if the user is authorized to perform the request
  • MapPropertiesDataPipe - Maps property names
  • FillRouteParameterPropertiesDataPipe - Fills property values from route parameters
  • ValidatePropertiesDataPipe - Validates properties
  • DefaultValuesDataPipe - Adds default values for unset properties
  • CastPropertiesDataPipe - Casts property values

Customizing the Pipeline

Define a custom pipeline for a data object:
class SongData extends Data
{
    public function __construct(
        // ...
    ) {
    }

    public static function pipeline(): DataPipeline
    {
        return DataPipeline::create()
            ->into(static::class)
            ->through(AuthorizedDataPipe::class)
            ->through(MapPropertiesDataPipe::class)
            ->through(FillRouteParameterPropertiesDataPipe::class)
            ->through(ValidatePropertiesDataPipe::class)
            ->through(DefaultValuesDataPipe::class)
            ->through(CastPropertiesDataPipe::class);
    }
}

Creating a DataPipe

Implement the DataPipe interface:
interface DataPipe
{
    public function handle(mixed $payload, DataClass $class, array $properties, CreationContext $creationContext): array;
}

Parameters

  • payload - The non-normalized payload
  • class - DataClass object for the data object (more info)
  • properties - Key-value properties for constructing the data object
  • creationContext - Context containing:
    • dataClass - The data class being created
    • validationStrategy - The validation strategy in use
    • mapPropertyNames - Whether property names should be mapped
    • disableMagicalCreation - Whether to use magical creation methods
    • ignoredMagicalMethods - Which magical methods are ignored
    • casts - Collection of global casts

Preparing Data for the Pipeline

Modify the payload after normalization but before entering the pipeline:
class SongMetadata
{
    public function __construct(
        public string $releaseYear,
        public string $producer,
    ) {}
}

class SongData extends Data
{
    public function __construct(
        public string $title,
        public SongMetadata $metadata,
    ) {}
    
    public static function prepareForPipeline(array $properties): array
    {
        $properties['metadata'] = Arr::only($properties, ['release_year', 'producer']);
        
        return $properties;
    }
}
Now create a data object with a flat structure:
$songData = SongData::from([
    'title' => 'Never gonna give you up',
    'release_year' => '1987',
    'producer' => 'Stock Aitken Waterman',
]);

Extending the Pipeline

Add a pipe at the beginning without creating a whole new pipeline:
class SongData extends Data
{
    public static function pipeline(): DataPipeline
    {
        return parent::pipeline()->firstThrough(GuessCasingForKeyDataPipe::class);
    }
}

Pipeline and Magic Methods

When using magic creation methods, the pipeline is not used (since you manually control construction). Only when you pass a request object is a minimal version used for authorization and validation.

Build docs developers (and LLMs) love