Skip to content

Pipelines

Process data through multiple stages with pipelines.

Pipelines allow you to chain multiple processing stages:

  • Transformers - Transform data
  • Normalizers - Normalize data types
  • Validators - Validate data
  • Custom Stages - Create custom stages
use event4u\DataHelpers\SimpleDTO\Pipeline\DTOPipeline;
use event4u\DataHelpers\SimpleDTO\Pipeline\Stages\TransformerStage;
use event4u\DataHelpers\SimpleDTO\Pipeline\Transformers\TrimStringsTransformer;
$pipeline = new DTOPipeline();
$pipeline->addStage(new TransformerStage(new TrimStringsTransformer()));
$dto = UserDTO::fromArrayWithPipeline($data, $pipeline);
$pipeline = new DTOPipeline();
$pipeline->addStage(new NormalizerStage(new TypeNormalizer(['age' => 'int'])));
$pipeline->addStage(new TransformerStage(new TrimStringsTransformer()));
$pipeline->addStage(new ValidationStage());
$dto = UserDTO::fromArrayWithPipeline($data, $pipeline);
use event4u\DataHelpers\SimpleDTO\Pipeline\Transformers\TrimStringsTransformer;
$pipeline = new DTOPipeline();
$pipeline->addStage(new TransformerStage(new TrimStringsTransformer()));
$data = ['name' => ' John Doe '];
$dto = UserDTO::fromArrayWithPipeline($data, $pipeline);
// name: 'John Doe'
use event4u\DataHelpers\SimpleDTO\Pipeline\Transformers\LowerCaseTransformer;
$pipeline = new DTOPipeline();
$pipeline->addStage(new TransformerStage(new LowerCaseTransformer(['email'])));
$data = ['email' => 'JOHN@EXAMPLE.COM'];
$dto = UserDTO::fromArrayWithPipeline($data, $pipeline);
// email: 'john@example.com'
use event4u\DataHelpers\SimpleDTO\Pipeline\Transformers\UpperCaseTransformer;
$pipeline = new DTOPipeline();
$pipeline->addStage(new TransformerStage(new UpperCaseTransformer(['code'])));
$data = ['code' => 'abc123'];
$dto = UserDTO::fromArrayWithPipeline($data, $pipeline);
// code: 'ABC123'
use event4u\DataHelpers\SimpleDTO\Contracts\Transformer;
class SlugifyTransformer implements Transformer
{
public function __construct(
private array $fields = [],
) {}
public function transform(array $data): array
{
foreach ($this->fields as $field) {
if (isset($data[$field])) {
$data[$field] = $this->slugify($data[$field]);
}
}
return $data;
}
private function slugify(string $value): string
{
$slug = strtolower($value);
$slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
$slug = trim($slug, '-');
return $slug;
}
}
// Usage
$pipeline = new DTOPipeline();
$pipeline->addStage(new TransformerStage(new SlugifyTransformer(['title'])));
use event4u\DataHelpers\SimpleDTO\Contracts\PipelineStage;
class SanitizeStage implements PipelineStage
{
public function process(array $data): array
{
foreach ($data as $key => $value) {
if (is_string($value)) {
$data[$key] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
}
return $data;
}
public function getName(): string
{
return 'sanitize';
}
}
// Usage
$pipeline = new DTOPipeline();
$pipeline->addStage(new SanitizeStage());
$pipeline = new DTOPipeline();
// 1. Trim all strings
$pipeline->addStage(new TransformerStage(new TrimStringsTransformer()));
// 2. Lowercase email
$pipeline->addStage(new TransformerStage(new LowerCaseTransformer(['email'])));
// 3. Normalize types
$pipeline->addStage(new NormalizerStage(new TypeNormalizer([
'age' => 'int',
'active' => 'bool',
])));
// 4. Validate
$pipeline->addStage(new ValidationStage());
$dto = UserDTO::fromArrayWithPipeline($_POST, $pipeline);
$pipeline = new DTOPipeline();
// 1. Remove null values
$pipeline->addStage(new FilterStage(fn($v) => $v !== null));
// 2. Trim strings
$pipeline->addStage(new TransformerStage(new TrimStringsTransformer()));
// 3. Convert dates
$pipeline->addStage(new TransformerStage(new DateTransformer([
'created_at',
'updated_at',
])));
$dto = ProductDTO::fromArrayWithPipeline($apiResponse, $pipeline);
$pipeline = new DTOPipeline();
// 1. Sanitize HTML
$pipeline->addStage(new SanitizeStage());
// 2. Trim strings
$pipeline->addStage(new TransformerStage(new TrimStringsTransformer()));
// 3. Generate slug from title
$pipeline->addStage(new TransformerStage(new SlugifyTransformer(['slug'])));
// 4. Validate
$pipeline->addStage(new ValidationStage());
$dto = PostDTO::fromArrayWithPipeline($_POST, $pipeline);
$pipeline = new DTOPipeline();
$pipeline->setContext('user_id', auth()->id());
$pipeline->setContext('ip_address', request()->ip());
$pipeline->addStage(new AuditStage());
$dto = UserDTO::fromArrayWithPipeline($data, $pipeline);
class AuditStage implements PipelineStage
{
public function process(array $data): array
{
$userId = $this->getContext('user_id');
$ipAddress = $this->getContext('ip_address');
// Log audit trail
AuditLog::create([
'user_id' => $userId,
'ip_address' => $ipAddress,
'data' => $data,
]);
return $data;
}
public function getName(): string
{
return 'audit';
}
}
$pipeline = new DTOPipeline();
$pipeline->stopOnError(true); // Default
$pipeline->addStage(new ValidationStage());
try {
$dto = UserDTO::fromArrayWithPipeline($data, $pipeline);
} catch (Exception $e) {
// Handle error
}
$pipeline = new DTOPipeline();
$pipeline->stopOnError(false);
$pipeline->addStage(new ValidationStage());
$pipeline->addStage(new TransformerStage(new TrimStringsTransformer()));
$dto = UserDTO::fromArrayWithPipeline($data, $pipeline);
// Check for errors
$errors = $pipeline->getErrors();
// ✅ Good - correct order
$pipeline->addStage(new TrimStage()); // 1. Clean data
$pipeline->addStage(new NormalizeStage()); // 2. Normalize types
$pipeline->addStage(new ValidationStage()); // 3. Validate
// ❌ Bad - wrong order
$pipeline->addStage(new ValidationStage()); // Validates dirty data
$pipeline->addStage(new TrimStage());
// ✅ Good - reusable
class UserPipeline
{
public static function create(): DTOPipeline
{
$pipeline = new DTOPipeline();
$pipeline->addStage(new TrimStage());
$pipeline->addStage(new ValidationStage());
return $pipeline;
}
}
$dto = UserDTO::fromArrayWithPipeline($data, UserPipeline::create());
// ❌ Bad - not reusable
$pipeline = new DTOPipeline();
$pipeline->addStage(new TrimStage());
// ... repeat everywhere
// ✅ Good - single responsibility
class TrimStage implements PipelineStage
{
public function process(array $data): array
{
// Only trim strings
}
}
// ❌ Bad - multiple responsibilities
class ProcessStage implements PipelineStage
{
public function process(array $data): array
{
// Trim, validate, transform, etc.
}
}