event4u/data-helpers

Framework-agnostic PHP library for data mapping, DTOs and utilities. Includes DataMapper, SimpleDto/LiteDto, DataAccessor/Mutator/Filter and helper classes (MathHelper, EnvHelper, etc.). Works with Laravel, Symfony/Doctrine or standalone PHP.

Fund package maintenance!
event4u-app
matze4u

Installs: 793

Dependents: 0

Suggesters: 0

Security: 0

Stars: 12

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/event4u/data-helpers

1.22.5 2025-12-01 19:45 UTC

This package is auto-updated.

Last update: 2025-12-01 19:45:41 UTC


README

event4u Data Helpers

Data Helpers

Packagist Version PHP License: MIT GitHub Code Quality Action Status GitHub PHPStan Action Status GitHub Test Matrix Action Status

Framework-agnostic PHP library with deep framework integration โ€“ get the power of framework-specific solutions without the lock-in.

Transform complex data structures, create type-safe DTOs and simplify data operations with expressive syntax. Works standalone in Pure PHP or with deep integration for Laravel and Symfony. Includes DataMapper, SimpleDto/LiteDto, DataAccessor/Mutator/Filter and utility helpers (MathHelper, EnvHelper, etc.).

// From this messy API response...
$apiResponse = [
    'data' => [
        'departments' => [
            ['users' => [['email' => 'alice@example.com'], ['email' => 'bob@example.com']]],
            ['users' => [['email' => 'charlie@example.com']]],
        ],
    ],
];

// ...to this clean result in a few lines
$accessor = new DataAccessor($apiResponse);
$emails = $accessor->get('data.departments.*.users.*.email');
// ['alice@example.com', 'bob@example.com', 'charlie@example.com']

๐ŸŽฏ Framework-Agnostic + Deep Integration โ€ข Pure PHP with zero dependencies โ€ข Optional Laravel & Symfony integration โ€ข No framework lock-in

๐Ÿ“– Full Documentation โ€ข Getting Started โ€ข API Reference

๐Ÿ’– Support the Development - Help us build better tools for the PHP community

๐Ÿ’ก Why Data Helpers?

๐ŸŽฏ Stop Writing Nested Loops

// โŒ Without Data Helpers
$emails = [];
foreach ($data['departments'] ?? [] as $dept) {
    foreach ($dept['users'] ?? [] as $user) {
        if (isset($user['email'])) {
            $emails[] = $user['email'];
        }
    }
}

// โœ… With Data Helpers
$emails = $accessor->get('departments.*.users.*.email');

๐Ÿš€ Key Benefits

  • ๐ŸŽฏ Framework-Agnostic + Deep Integration - Pure PHP with zero dependencies, optional deep Laravel/Symfony integration
  • Type-Safe - PHPStan Level 9 compliant with 4700+ tests
  • Fast - SimpleDto with #[UltraFast] is up to 12.1x faster than Other Serializer
  • Zero Dependencies - No required dependencies, optional framework integrations
  • No Framework Lock-In - Use framework features without being tied to a framework
  • No-Code Mapping - Store templates in database, create with drag-and-drop editors

๐Ÿ“ฆ Installation

composer require event4u/data-helpers

Requirements: PHP 8.2+

Framework support (all optional):

  • ๐Ÿ”ด Laravel 9+ - Collections, Eloquent Models
  • โšซ Symfony/Doctrine 6+ - Collections, Entities
  • ๐Ÿ”ง Standalone PHP - Works out of the box

๐Ÿ“– Installation Guide โ€ข Configuration

๐Ÿ”Œ Framework Integration

The best of both worlds: Use Data Helpers as a standalone library in pure PHP, or leverage deep framework integration for Laravel and Symfony โ€“ without framework lock-in.

๐ŸŽฏ Framework-Agnostic Core

Zero dependencies required. Works out of the box with:

  • โœ… Pure PHP - Arrays, objects, JSON, XML
  • โœ… Any Framework - No framework-specific code required
  • โœ… Portable - Move between frameworks without code changes
// Works everywhere - no framework needed
$dto = UserDto::fromArray(['name' => 'John', 'email' => 'john@example.com']);
$json = json_encode($dto);

๐Ÿš€ Optional Deep Integration

When you need it: Add framework-specific features without changing your core code.

Laravel Integration (Optional)

// 1. Controller Injection - Automatic validation & filling
class UserController extends Controller
{
    public function store(UserDto $dto): JsonResponse
    {
        // $dto is automatically validated and filled from request
        $user = User::create($dto->toArray());
        return response()->json($user, 201);
    }
}

// 2. Eloquent Model Integration
$user = User::find(1);
$dto = UserDto::fromModel($user);  // From Model
$dto->toModel($user);              // To Model

// 3. Laravel-Specific Attributes
class UserProfileDto extends SimpleDto
{
    public function __construct(
        public readonly string $name,

        #[WhenAuth]  // Only when authenticated
        public readonly ?string $email = null,

        #[WhenCan('edit-posts')]  // Only with permission
        public readonly ?string $editUrl = null,

        #[WhenRole('admin')]  // Only for admins
        public readonly ?array $adminPanel = null,
    ) {}
}

// 4. Artisan Commands
php artisan make:dto UserDto
php artisan dto:typescript
php artisan dto:migrate-spatie

Symfony Integration (Optional)

// 1. Controller Injection - Automatic validation & filling
class UserController extends AbstractController
{
    #[Route('/users', methods: ['POST'])]
    public function create(UserDto $dto): JsonResponse
    {
        // $dto is automatically validated and filled from request
        $user = new User();
        $dto->toEntity($user);
        $this->entityManager->persist($user);
        $this->entityManager->flush();
        return $this->json($user, 201);
    }
}

// 2. Doctrine Entity Integration
$user = $this->entityManager->find(User::class, 1);
$dto = UserDto::fromEntity($user);  // From Entity
$dto->toEntity($user);              // To Entity

// 3. Symfony-Specific Attributes
class UserProfileDto extends SimpleDto
{
    public function __construct(
        public readonly string $name,

        #[WhenGranted('ROLE_ADMIN')]  // Only with permission
        public readonly ?string $email = null,

        #[WhenSymfonyRole('ROLE_MODERATOR')]  // Only for moderators
        public readonly ?array $moderationPanel = null,
    ) {}
}

// 4. Console Commands
php bin/console make:dto UserDto
php bin/console dto:typescript

๐Ÿ’ก Key Integration Features

Feature Pure PHP Laravel Symfony
DTOs & Validation โœ… โœ… โœ…
Controller Injection โŒ โœ… Auto โœ… Auto
Request Validation โœ… Manual โœ… Auto โœ… Auto
Model/Entity Mapping โœ… Plain Objects โœ… Eloquent โœ… Doctrine
Framework Attributes โŒ โœ… Auth/Can/Role โœ… Granted/Role
Code Generation โŒ โœ… Artisan โœ… Console
TypeScript Export โŒ โœ… โœ…

The Power: Get framework-specific features when you need them, without framework dependencies in your core code.

๐Ÿ“– Laravel Integration Guide โ€ข Symfony Integration Guide

โšก Core Components

The heart of this library: Data mapping and DTOs for transforming and structuring data, plus powerful data manipulation tools.

1๏ธโƒฃ DataAccessor - Read & Transform Data

Access deeply nested data with dot notation, wildcards, and powerful transformation methods:

$data = [
    'users' => [
        ['name' => 'Alice', 'age' => '30', 'email' => 'alice@example.com'],
        ['name' => 'Bob', 'age' => '25', 'email' => 'bob@example.com'],
    ],
];

$accessor = new DataAccessor($data);

// Generic get() - returns mixed
$emails = $accessor->get('users.*.email');
// ['users.0.email' => 'alice@example.com', 'users.1.email' => 'bob@example.com']

// Type-safe getters - strict type conversion with nullable return
$name = $accessor->getString('users.0.name');  // 'Alice'
$age = $accessor->getInt('users.0.age');       // 30 (string โ†’ int)
$missing = $accessor->getString('users.0.phone');  // null

// Collection getters for wildcards - returns DataCollection instances
$ages = $accessor->getIntCollection('users.*.age');  // DataCollection<int>
$names = $accessor->getStringCollection('users.*.name');  // DataCollection<string>

// Transformation methods - filter, map, reduce directly on DataAccessor
$filtered = $accessor->filter(fn($user) => $user['age'] > 25);  // [['name' => 'Alice', ...]]
$mapped = $accessor->map(fn($user) => $user['name']);  // ['Alice', 'Bob']
$sum = $accessor->reduce(fn($carry, $user) => $carry + $user['age'], 0);  // 55

// first() and last() with optional callback
$firstUser = $accessor->first();  // ['name' => 'Alice', ...]
$lastAdult = $accessor->last(fn($user) => $user['age'] >= 18);

// Lazy evaluation for large datasets
foreach ($accessor->lazyFilter(fn($user) => $user['age'] > 25) as $user) {
    // Process items one at a time without loading all into memory
}

๐Ÿ“– DataAccessor Documentation

2๏ธโƒฃ DataCollection - Type-Safe Collections

Framework-independent collection class with fluent API. Uses DataAccessor for reading, DataMutator for writing, and DataFilter for SQL-like querying:

use event4u\DataHelpers\DataCollection;

$collection = DataCollection::make([1, 2, 3, 4, 5]);

// Filter, map, reduce with method chaining (delegates to DataAccessor)
$result = $collection
    ->filter(fn($item) => $item > 2)  // [3, 4, 5]
    ->map(fn($item) => $item * 2)     // [6, 8, 10]
    ->reduce(fn($carry, $item) => $carry + $item, 0);  // 24

// Dot-notation read access (via DataAccessor)
$collection = DataCollection::make([
    ['user' => ['name' => 'Alice', 'age' => 30]],
    ['user' => ['name' => 'Bob', 'age' => 25]],
]);
$name = $collection->get('0.user.name');  // 'Alice'

// Dot-notation write access (via DataMutator) - modifies in-place
$collection
    ->set('0.user.city', 'Berlin')
    ->merge('1.user', ['city' => 'Munich', 'country' => 'Germany'])
    ->transform('0.user.name', fn($name) => strtoupper($name));

// SQL-like filtering (via DataFilter) - returns new DataCollection
$users = DataCollection::make([
    ['name' => 'Alice', 'age' => 30, 'city' => 'Berlin'],
    ['name' => 'Bob', 'age' => 25, 'city' => 'Munich'],
    ['name' => 'Charlie', 'age' => 35, 'city' => 'Berlin'],
]);
$filtered = $users
    ->query()
    ->where('age', '>', 25)
    ->where('city', 'Berlin')
    ->orderBy('age', 'DESC')
    ->get();  // Returns new DataCollection

// Lazy evaluation for large datasets
foreach ($collection->lazyFilter(fn($item) => $item > 2) as $item) {
    // Process items one at a time without loading all into memory
}

๐Ÿ“– DataCollection Documentation

3๏ธโƒฃ DataMutator - Modify Nested Data

Safely modify nested structures:

$data = ['user' => ['profile' => []]];
DataMutator::make($data)
    ->set('user.profile.name', 'Alice')
    ->merge('user.profile', ['age' => 30]);
// $data is now modified: ['user' => ['profile' => ['name' => 'Alice', 'age' => 30]]]

๐Ÿ“– DataMutator Documentation

4๏ธโƒฃ DataFilter - Query Data

Filter and query data with SQL-like API:

$products = [
    ['id' => 1, 'name' => 'Laptop', 'category' => 'Electronics', 'price' => 1200],
    ['id' => 2, 'name' => 'Mouse', 'category' => 'Electronics', 'price' => 25],
    ['id' => 3, 'name' => 'Monitor', 'category' => 'Electronics', 'price' => 400],
];

$result = DataFilter::query($products)
    ->where('category', '=', 'Electronics')
    ->where('price', '>', 100)
    ->orderBy('price', 'DESC')
    ->get();
// Result: [Laptop ($1200), Monitor ($400)]

๐Ÿ“– DataFilter Documentation

5๏ธโƒฃ SimpleDto - Immutable Dtos

Create type-safe, immutable Data Transfer Objects with automatic type casting and multi-format serialization (JSON, XML, YAML, CSV):

use event4u\DataHelpers\SimpleDto\Attributes\NoCasts;

// Default: Automatic type casting enabled
class ReadmeUserDto extends SimpleDto
{
    public function __construct(
        public readonly string $name,
        public readonly string $email,
        public readonly int $age,
        public readonly AddressDto $address,  // Nested DTO (auto-cast by default)
    ) {}
}

// Automatic type conversion by default
$user = ReadmeUserDto::fromArray([
    'name' => 'John',
    'email' => 'john@example.com',
    'age' => '30',  // String "30" โ†’ int 30 (automatic)
    'address' => ['city' => 'Berlin'],  // Array โ†’ AddressDto (automatic)
]);

// Disable automatic casting for better performance
#[NoCasts]
class StrictUserDto extends SimpleDto
{
    public function __construct(
        public readonly string $name,
        public readonly int $age,  // Must be int, no conversion
        public readonly AddressDto $address,  // Must be AddressDto instance, no conversion
    ) {}
}

// Multi-format serialization
$json = $user->toJson();  // JSON
$xml = $user->toXml();    // XML
$yaml = $user->toYaml();  // YAML
$csv = $user->toCsv();    // CSV

๐Ÿ“– SimpleDto Documentation

Plain PHP Object Integration

SimpleDto seamlessly integrates with plain PHP objects (like Zend Framework models or any plain PHP classes):

use event4u\DataHelpers\SimpleDto;
use event4u\DataHelpers\SimpleDto\Attributes\HasObject;
use event4u\DataHelpers\SimpleDto\SimpleDtoObjectTrait;

// Plain PHP object
class Product
{
    public int $id;
    public string $name;
    public float $price;
}

// DTO with plain object integration
#[HasObject(Product::class)]
class ProductDto extends SimpleDto
{
    use SimpleDtoObjectTrait;

    public function __construct(
        public readonly int $id,
        public readonly string $name,
        public readonly float $price,
    ) {}
}

// Object โ†’ DTO
$product = new Product();
$product->id = 1;
$product->name = 'Laptop';
$product->price = 999.99;
$dto = ProductDto::fromObject($product);

// DTO โ†’ Object
$newProduct = $dto->toObject();  // Uses HasObject attribute

Also works with getters/setters:

class Customer
{
    private int $id;
    private string $name;

    public function getId(): int { return $this->id; }
    public function setId(int $id): void { $this->id = $id; }
    public function getName(): string { return $this->name; }
    public function setName(string $name): void { $this->name = $name; }
}

// fromObject() uses getters, toObject() uses setters
$dto = CustomerDto::fromObject($customer);
$newCustomer = $dto->toObject(Customer::class);

๐Ÿ“– Plain Object Integration Guide โ€ข Example Code

6๏ธโƒฃ LiteDto - Ultra-Fast Dtos

Create ultra-fast, minimalistic DTOs with essential features:

use event4u\DataHelpers\LiteDto;
use event4u\DataHelpers\LiteDto\Attributes\MapFrom;
use event4u\DataHelpers\LiteDto\Attributes\Hidden;

class UserDto extends LiteDto
{
    public function __construct(
        public readonly string $name,

        #[MapFrom('email_address')]
        public readonly string $email,

        #[Hidden]
        public readonly string $password,
    ) {}
}

$user = UserDto::from([
    'name' => 'John',
    'email_address' => 'john@example.com',
    'password' => 'secret',
]);

$array = $user->toArray();
// ['name' => 'John', 'email' => 'john@example.com']
// password is hidden

Performance: LiteDto is 7.6x faster than SimpleDto Normal (~2.3ฮผs vs ~18.5ฮผs)

๐Ÿ“– LiteDto Documentation

3๏ธโƒฃ DataMapper - Transform Data

Map between different data structures with templates. Supports multi-format output (JSON, XML, YAML, CSV):

$source = [
    'user' => ['name' => 'John Doe', 'email' => 'john@example.com'],
    'orders' => [
        ['id' => 1, 'status' => 'shipped', 'total' => 100],
        ['id' => 2, 'status' => 'pending', 'total' => 50],
        ['id' => 3, 'status' => 'shipped', 'total' => 200],
    ],
];

$result = DataMapper::from($source)
    ->template([
        'customer_name' => '{{ user.name }}',
        'customer_email' => '{{ user.email }}',
        'shipped_orders' => [
            'WHERE' => [
                '{{ orders.*.status }}' => 'shipped',
            ],
            'ORDER BY' => [
                '{{ orders.*.total }}' => 'DESC',
            ],
            '*' => [
                'id' => '{{ orders.*.id }}',
                'total' => '{{ orders.*.total }}',
            ],
        ],
    ])
    ->map()
    ->getTarget();

๐Ÿ’ก No-Code Data Mapping: Templates can be stored in a database and created with a drag-and-drop editor - perfect for import wizards, API integrations and ETL pipelines without writing code!

โš ๏ธ XML Files: When loading XML files with sourceFile(), the root element name is preserved. Always include it in your paths: '{{ company.name }}' for <company><name>...</name></company>.

๐Ÿ“– DataMapper Documentation

7๏ธโƒฃ Utility Helpers - Common Data Operations

Simplify common data operations with specialized helper classes:

use event4u\DataHelpers\Helpers\MathHelper;
use event4u\DataHelpers\Helpers\EnvHelper;

// Math operations with precision
$result = MathHelper::add('10.5', '20.3', 2);  // 30.8
$average = MathHelper::average([10, 20, 30]);  // 20.0
$sum = MathHelper::sum([5, 10, 15]);  // 30.0

// Environment variable access with type casting
$debug = EnvHelper::boolean('APP_DEBUG', false);
$port = EnvHelper::integer('APP_PORT', 8080);
$timeout = EnvHelper::float('REQUEST_TIMEOUT', 30.0);

Available Helpers:

  • MathHelper - Precision math operations using bcmath (add, subtract, multiply, divide, modulo, powerOf, squareRoot, compare, min, max, sum, average, product, time conversions)
  • EnvHelper - Type-safe environment variable access with framework detection (get, has, string, integer, float, boolean, array)
  • ConfigHelper - Singleton configuration manager with framework detection and dot notation (getInstance, get, getBoolean, getInteger, getFloat, getString, getArray, has, set, reset)
  • DotPathHelper - Dot notation path utilities with wildcard support (segments, buildPrefix, isWildcard, containsWildcard)
  • ObjectHelper - Deep object cloning with recursion control (copy)

๐Ÿ“– Helpers Documentation

๐ŸŽฏ Advanced Features

No-Code Data Mapping

Store templates in database and create mappings without programming:

// Load template from database (created with drag-and-drop editor)
$template = Mappings::find(3)->template;

$result = DataMapper::from($source)
    ->template($template)
    ->map()
    ->getTarget();

Perfect for:

  • ๐Ÿ“ฅ Import Wizards - Let users map CSV/Excel columns to your data model
  • ๐Ÿ”Œ API Integration - Configure API mappings without code changes
  • ๐Ÿข Multi-Tenant Systems - Each tenant can have custom data mappings
  • ๐Ÿ”„ Dynamic ETL - Build data transformation pipelines visually
  • ๐Ÿ“ Form Builders - Map form submissions to different data structures

๐Ÿ“– Template-Based Mapping Guide

Complex Nested Mapping

Map complex nested structures to Eloquent Models or Doctrine Entities:

// Automatic relation detection for Eloquent/Doctrine
$company = new Company();
$result = DataMapper::from($jsonData)
    ->target($company)
    ->template([
        'name' => '{{ company.name }}',
        'departments' => [
            '*' => [
                'name' => '{{ company.departments.*.name }}',
                'budget' => '{{ company.departments.*.budget }}',
            ],
        ],
    ])
    ->map()
    ->getTarget();
  • โœ… Automatic Relation Detection
  • โœ… Type Casting (string โ†’ int/float/bool)
  • โœ… Snake_case โ†’ camelCase conversion
  • โœ… Nested Wildcards

๐Ÿ“– Advanced Mapping Guide

Pipeline API

Transform data with composable filters:

use Tests\Utils\Docu\TrimStrings;
use Tests\Utils\Docu\LowercaseEmails;
use Tests\Utils\Docu\SkipEmptyValues;

$source = ['name' => '  John  ', 'email' => 'JOHN@EXAMPLE.COM'];
$mapping = ['name' => '{{ name }}', 'email' => '{{ email }}'];

$result = DataMapper::from($source)
    ->template($mapping)
    ->pipeline([
        new TrimStrings(),
        new LowercaseEmails(),
        new SkipEmptyValues(),
    ])
    ->map()
    ->getTarget();

// $result = ['name' => 'John', 'email' => 'john@example.com']

๐Ÿ“– Pipeline Documentation

Template Expressions

Use Twig-like expressions with 18+ built-in filters:

$mapping = [
    'name' => '{{ user.firstName | ucfirst }} {{ user.lastName | ucfirst }}',
    'email' => '{{ user.email | lower | trim }}',
    'role' => '{{ user.role | upper ?? "USER" }}',
];

๐Ÿ“– Template Expressions

Query Builder

Laravel-style fluent interface for building queries:

$result = DataMapper::query()
    ->source('products', $data)
    ->where('category', 'Electronics')
    ->where('price', '>', 100)
    ->orderBy('price', 'DESC')
    ->groupBy('category', ['total' => ['COUNT']])
    ->get();

๐Ÿ“– Query Builder Documentation

๐Ÿ“š Documentation

Comprehensive documentation with guides, examples and API reference is available at:

๐Ÿ”— event4u-app.github.io/data-helpers

The documentation includes:

  • ๐Ÿ“– Getting Started Guides - Installation, configuration and quick start tutorials
  • ๐Ÿ”ง Main Classes - Detailed guides for DataAccessor, DataMutator, DataMapper and DataFilter
  • ๐ŸŽฏ SimpleDto - Type-safe Dtos with validation, casting and collections
  • โšก LiteDto - Ultra-fast, minimalistic Dtos (7.6x faster than SimpleDto)
  • ๐Ÿš€ Advanced Features - Template expressions, query builder, pipelines and reverse mapping
  • ๐Ÿ”Œ Framework Integration - Laravel, Symfony and Doctrine integration guides
  • ๐Ÿ’ก 90+ Code Examples - Runnable examples for every feature
  • ๐Ÿ“Š Performance Benchmarks - Optimization tips and benchmark results
  • ๐Ÿ” Complete API Reference - Full API documentation for all classes and methods

๐Ÿงช Testing & Quality

  • โœ… 4700+ tests with comprehensive coverage
  • โœ… PHPStan Level 9 - Highest static analysis level
  • โœ… 100% type coverage - All methods fully typed
  • โœ… Continuous Integration - Automated testing across PHP 8.2, 8.3, 8.4

๐Ÿ“– Contributing Guide โ€ข Development Setup

โšก Performance

All operations are highly optimized:

  • Simple access: ~0.1ฮผs
  • Nested access: ~0.5ฮผs
  • Wildcards: ~1ฮผs
  • SimpleDto #[UltraFast] is up to 12.1x faster than Other Serializer

๐Ÿ“– Performance Benchmarks โ€ข Optimization Tips

๐Ÿค Contributing

Contributions are welcome! Please see the Contributing Guide for details.

# Install dependencies
composer install

# Run tests
composer test

# Run quality checks
composer quality

๐Ÿ’– Sponsoring

This package is part of the event4u ecosystem - a comprehensive event management platform. Your sponsorship helps us:

  • ๐Ÿš€ Develop event4u - The next-generation event management app
  • ๐Ÿ“ฆ Maintain open-source packages - Like this Data Helpers library
  • ๐Ÿ”ง Build new tools - More packages and utilities for the PHP community
  • ๐Ÿ“š Improve documentation - Better guides and examples
  • ๐Ÿ› Fix bugs faster - Dedicated time for maintenance and support

Support the Development

Sponsor @matze4u ย ย  Sponsor event4u-app

Every contribution, no matter how small, makes a difference and is greatly appreciated! ๐Ÿ™

๐Ÿ“„ License

MIT License. See LICENSE for details.

๐ŸŒŸ Show Your Support

If this package helps you, please consider: