Property Mapping
Learn how to map source keys to different property names using MapFrom attribute.
What is Property Mapping?
Section titled “What is Property Mapping?”Property mapping allows you to map source data keys to different property names in your Dto:
class UserDto extends SimpleDto{ public function __construct( #[MapFrom('full_name')] public readonly string $name,
#[MapFrom('email_address')] public readonly string $email, ) {}}
$dto = UserDto::fromArray([ 'full_name' => 'John Doe', 'email_address' => 'john@example.com',]);
echo $dto->name; // 'John Doe'echo $dto->email; // 'john@example.com'Basic Usage
Section titled “Basic Usage”Simple Mapping
Section titled “Simple Mapping”use event4u\DataHelpers\SimpleDto\Attributes\MapFrom;
class ProductDto extends SimpleDto{ public function __construct( #[MapFrom('product_name')] public readonly string $name,
#[MapFrom('product_price')] public readonly float $price,
#[MapFrom('product_sku')] public readonly string $sku, ) {}}Nested Path Mapping
Section titled “Nested Path Mapping”class UserDto extends SimpleDto{ public function __construct( public readonly string $name,
#[MapFrom('contact.email')] public readonly string $email,
#[MapFrom('contact.phone')] public readonly ?string $phone = null, ) {}}
$dto = UserDto::fromArray([ 'name' => 'John Doe', 'contact' => [ 'email' => 'john@example.com', 'phone' => '+1234567890', ],]);Real-World Examples
Section titled “Real-World Examples”API Response Mapping
Section titled “API Response Mapping”class UserDto extends SimpleDto{ public function __construct( #[MapFrom('user_id')] public readonly int $id,
#[MapFrom('user_name')] public readonly string $name,
#[MapFrom('user_email')] public readonly string $email,
#[MapFrom('created_at')] public readonly Carbon $createdAt, ) {}}
// Map from API response$dto = UserDto::fromArray([ 'user_id' => 1, 'user_name' => 'John Doe', 'user_email' => 'john@example.com', 'created_at' => '2024-01-15',]);Database Column Mapping
Section titled “Database Column Mapping”class OrderDto extends SimpleDto{ public function __construct( #[MapFrom('order_id')] public readonly int $id,
#[MapFrom('customer_name')] public readonly string $customerName,
#[MapFrom('order_total')] public readonly float $total,
#[MapFrom('order_status')] public readonly string $status, ) {}}Legacy System Integration
Section titled “Legacy System Integration”class LegacyUserDto extends SimpleDto{ public function __construct( #[MapFrom('usr_id')] public readonly int $userId,
#[MapFrom('usr_nm')] public readonly string $userName,
#[MapFrom('usr_eml')] public readonly string $userEmail,
#[MapFrom('usr_crt_dt')] public readonly Carbon $createdDate, ) {}}Combining with Other Features
Section titled “Combining with Other Features”MapFrom + Cast
Section titled “MapFrom + Cast”class EventDto extends SimpleDto{ public function __construct( #[MapFrom('event_name')] public readonly string $name,
#[MapFrom('event_date'), Cast(DateTimeCast::class)] public readonly Carbon $date, ) {}}MapFrom + Validation
Section titled “MapFrom + Validation”class UserDto extends SimpleDto{ public function __construct( #[MapFrom('full_name'), Required, Min(3)] public readonly string $name,
#[MapFrom('email_address'), Required, Email] public readonly string $email, ) {}}Best Practices
Section titled “Best Practices”Use Descriptive Property Names
Section titled “Use Descriptive Property Names”// ✅ Good - clear property names#[MapFrom('usr_nm')]public readonly string $userName;
// ❌ Bad - unclear abbreviation#[MapFrom('usr_nm')]public readonly string $usrNm;Document Mappings
Section titled “Document Mappings”/** * @property int $id Mapped from 'user_id' * @property string $name Mapped from 'full_name' */class UserDto extends SimpleDto{ public function __construct( #[MapFrom('user_id')] public readonly int $id,
#[MapFrom('full_name')] public readonly string $name, ) {}}Use for External APIs
Section titled “Use for External APIs”// ✅ Good - map external API to clean internal names#[MapFrom('external_api_field_name')]public readonly string $cleanPropertyName;Advanced Mapping with Templates
Section titled “Advanced Mapping with Templates”Using Template Property
Section titled “Using Template Property”For complex mappings, you can define a template property in your DTO:
use event4u\DataHelpers\SimpleDto;use event4u\DataHelpers\SimpleDtoMapperTrait;
class UserDto extends SimpleDto{ use SimpleDtoMapperTrait;
protected ?array $mapperTemplate = [ 'name' => '{{ user.name }}', 'email' => '{{ user.email }}', 'city' => '{{ user.address.city }}', ];
public function __construct( public readonly string $name, public readonly string $email, public readonly string $city, ) {}}
// Template is automatically applied$dto = UserDto::fromArray([ 'user' => [ 'name' => 'John Doe', 'email' => 'john@example.com', 'address' => [ 'city' => 'New York', ], ],]);
echo $dto->name; // 'John Doe'echo $dto->city; // 'New York'Using getMapperTemplate() Method
Section titled “Using getMapperTemplate() Method”Alternatively, you can override the getMapperTemplate() method:
use event4u\DataHelpers\SimpleDto;use event4u\DataHelpers\SimpleDtoMapperTrait;
class UserDto extends SimpleDto{ use SimpleDtoMapperTrait;
public function __construct( public readonly string $name, public readonly string $email, public readonly string $city, ) {}
public function getMapperTemplate(): array { return [ 'name' => '{{ user.name }}', 'email' => '{{ user.email }}', 'city' => '{{ user.address.city }}', ]; }}
// Template is automatically applied$dto = UserDto::fromArray([ 'user' => [ 'name' => 'John Doe', 'email' => 'john@example.com', 'address' => [ 'city' => 'New York', ], ],]);
echo $dto->name; // 'John Doe'echo $dto->city; // 'New York'Dynamic Template Changes with setMapperTemplate()
Section titled “Dynamic Template Changes with setMapperTemplate()”You can dynamically change the template on a DTO instance:
use event4u\DataHelpers\SimpleDto;use event4u\DataHelpers\SimpleDtoMapperTrait;
class UserDto extends SimpleDto{ use SimpleDtoMapperTrait;
protected ?array $mapperTemplate = [ 'id' => '{{ user.id }}', 'name' => '{{ user.name }}', ];
public function __construct( public readonly int $id = 0, public readonly string $name = '', ) {}}
// First mapping with initial template$dto1 = UserDto::fromArray([ 'user' => [ 'id' => 100, 'name' => 'Initial Name', ],]);
echo $dto1->id; // 100echo $dto1->name; // 'Initial Name'
// Change template on instance$instance = new UserDto();$instance->setMapperTemplate([ 'id' => '{{ customer.customer_id }}', 'name' => '{{ customer.full_name }}',]);
// Second mapping with new template$dto2 = UserDto::fromArray([ 'customer' => [ 'customer_id' => 200, 'full_name' => 'New Customer Name', ],], $instance->getMapperTemplate());
echo $dto2->id; // 200echo $dto2->name; // 'New Customer Name'Using mapperFilters()
Section titled “Using mapperFilters()”Apply filters to specific properties during mapping:
use event4u\DataHelpers\SimpleDto;use event4u\DataHelpers\SimpleDtoMapperTrait;use event4u\DataHelpers\Filters\TrimStrings;use event4u\DataHelpers\Filters\LowercaseStrings;
class UserDto extends SimpleDto{ use SimpleDtoMapperTrait;
public function __construct( public readonly string $name, public readonly string $email, ) {}
protected function mapperFilters(): array { return [ 'name' => new TrimStrings(), 'email' => [new TrimStrings(), new LowercaseStrings()], ]; }}
// Filters are automatically applied$dto = UserDto::fromArray([ 'name' => ' John Doe ', 'email' => ' JOHN@EXAMPLE.COM ',]);
echo $dto->name; // 'John Doe' (trimmed)echo $dto->email; // 'john@example.com' (trimmed and lowercased)Using mapperPipeline()
Section titled “Using mapperPipeline()”Apply global filters to all properties:
use event4u\DataHelpers\SimpleDto;use event4u\DataHelpers\SimpleDtoMapperTrait;use event4u\DataHelpers\Filters\TrimStrings;
class UserDto extends SimpleDto{ use SimpleDtoMapperTrait;
public function __construct( public readonly string $name, public readonly string $email, public readonly string $city, ) {}
protected function mapperPipeline(): array { return [new TrimStrings()]; }}
// Pipeline is automatically applied to all properties$dto = UserDto::fromArray([ 'name' => ' John Doe ', 'email' => ' john@example.com ', 'city' => ' New York ',]);
echo $dto->name; // 'John Doe' (trimmed)echo $dto->email; // 'john@example.com' (trimmed)echo $dto->city; // 'New York' (trimmed)Combining Template, Filters and Pipeline
Section titled “Combining Template, Filters and Pipeline”You can combine all three for powerful data transformation:
use event4u\DataHelpers\SimpleDto;use event4u\DataHelpers\SimpleDtoMapperTrait;use event4u\DataHelpers\Filters\TrimStrings;use event4u\DataHelpers\Filters\LowercaseStrings;
class UserDto extends SimpleDto{ use SimpleDtoMapperTrait;
protected ?array $mapperTemplate = [ 'name' => '{{ user.name }}', 'email' => '{{ user.email }}', 'city' => '{{ user.address.city }}', ];
public function __construct( public readonly string $name, public readonly string $email, public readonly string $city, ) {}
protected function mapperFilters(): array { return [ 'email' => new LowercaseStrings(), ]; }
protected function mapperPipeline(): array { return [new TrimStrings()]; }}
// All transformations are automatically applied$dto = UserDto::fromArray([ 'user' => [ 'name' => ' John Doe ', 'email' => ' JOHN@EXAMPLE.COM ', 'address' => [ 'city' => ' New York ', ], ],]);
echo $dto->name; // 'John Doe' (template + pipeline)echo $dto->email; // 'john@example.com' (template + filters + pipeline)echo $dto->city; // 'New York' (template + pipeline)Overriding at Runtime
Section titled “Overriding at Runtime”You can override DTO configuration at runtime:
// Override template$dto = UserDto::from($data, [ 'name' => '{{ custom.path }}',]);
// Override filters$dto = UserDto::from($data, null, [ 'name' => new CustomFilter(),]);
// Override pipeline (merged with DTO pipeline)$dto = UserDto::from($data, null, null, [ new AdditionalFilter(),]);Priority:
- Runtime parameters (highest priority)
- DTO configuration (
$mapperTemplateproperty orgetMapperTemplate()method,mapperFilters(),mapperPipeline()) #[MapFrom]attributes (only when no template is applied)- Auto-mapping (lowest priority)
Code Examples
Section titled “Code Examples”The following working examples demonstrate this feature:
- Basic Mapping - Property name mapping
All examples are fully tested and can be run directly.
Related Tests
Section titled “Related Tests”The functionality is thoroughly tested. Key test files:
- PropertyMappingTest.php - Property mapping tests
Run the tests:
# Run teststask test:unit -- --filter=PropertyMappingSee Also
Section titled “See Also”- Type Casting - Automatic type conversion
- Validation - Validate your data
- Creating Dtos - Creation methods