Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,13 @@ The mapper comes with a few configuration options that can be set using the [`Ma
object and passed to the mappers' constructor. This is not required, if no configuration is passed, the default config
is used.

| Option | Type | Default | Description |
|------------------------|----------|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `classMapperDirectory` | `string` | `/tmp/mappers` | This is the location the mapper will create cached mapper functions for objects.<br />The default location is a mappers function in the operating system temporary folder. |
| `debug` | `bool` | `false` | Enabling debug will clear all cached mapper functions after mapping has completed. |
| `enumTryFrom` | `bool` | `false` | Enabling this will use the `::tryFrom()` method instead of `::from()` to parse strings to enums. |
| `strictNullMapping` | `bool` | `true` | If enabled, the mapper will throw an error when a `null` value is passed for a property that was not typed as nullable. |
| Option | Type | Default | Description |
|----------------------------|----------|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `allowUninitializedFields` | `bool` | `true` | If disabled, the mapper will fail if one of the class properties that does not have a default value was not present in the data array. |
| `classMapperDirectory` | `string` | `/tmp/mappers` | This is the location the mapper will create cached mapper functions for objects.<br />The default location is a mappers function in the operating system temporary folder. |
| `debug` | `bool` | `false` | Enabling debug will clear all cached mapper functions after mapping has completed. |
| `enumTryFrom` | `bool` | `false` | Enabling this will use the `::tryFrom()` method instead of `::from()` to parse strings to enums. |
| `strictNullMapping` | `bool` | `true` | If enabled, the mapper will throw an error when a `null` value is passed for a property that was not typed as nullable. |

## Under the hood
For simple native types, the mapper will use casting to convert the data to the correct type.
Expand Down
15 changes: 7 additions & 8 deletions src/MapperConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,34 @@

class MapperConfig
{
/**
* If disabled, all properties of an object must either be present in the data array or have a default value for
* mapping to succeed.
* If enabled, properties that are not present in the data array will be left uninitialized.
*/
public bool $allowUninitializedFields = true;

/**
* This is the directly where generated mappers will be stored.
* It is recommended to prune this directory on every deploy to prevent old mappers from being used.
* The prefix `{$TMP}` is replaced with the system's temporary directory.
*
* @var string
*/
public string $classMapperDirectory = '{$TMP}' . \DIRECTORY_SEPARATOR . 'mappers';

/**
* In debug mode, the generated mapper files are deleted as soon as mapping is done.
* This let you edit mapped classes without having to worry about the mapper cache.
*
* @var bool
*/
public bool $debug = false;

/**
* If true, enums will be mapped using the `tryFrom` method instead of the `from` method.
* This might result in null values being mapped to non-nullable fields.
*
* @var bool
*/
public bool $enumTryFrom = false;

/**
* If true, mapping a null value to a non-nullable field will throw an UnexpectedNullValueException.
*
* @var bool
*/
public bool $strictNullMapping = true;
}
21 changes: 20 additions & 1 deletion src/Objects/ObjectMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,13 @@ private function createObjectMappingFunction(ClassBluePrint $blueprint, string $
$propertyMap = $this->wrapDefault($propertyMap, $name, $property['default']);
}

$content .= \PHP_EOL . $tab . $tab . '$x->' . $name . ' = ' . $propertyMap . ';';
$propertySet = \PHP_EOL . $tab . $tab . '$x->' . $name . ' = ' . $propertyMap . ';';

if ($this->mapper->config->allowUninitializedFields && ! \array_key_exists('default', $property)) {
$propertySet = $this->wrapArrayKeyExists($propertySet, $name);
}

$content .= $propertySet;
}

// Post mapping functions?
Expand Down Expand Up @@ -184,6 +190,15 @@ private function wrapDefault(string $value, string $arrayKey, mixed $defaultValu
return "(\\array_key_exists('{$arrayKey}', \$data) ? {$value} : " . \var_export($defaultValue, true) . ')';
}

private function wrapArrayKeyExists(string $expression, string $arrayKey): string
{
$content = \PHP_EOL . \str_repeat(' ', 2) . "if (\\array_key_exists('{$arrayKey}', \$data)) {";
$content .= \str_replace(\PHP_EOL, \PHP_EOL . ' ', $expression) . \PHP_EOL;
$content .= \str_repeat(' ', 2) . '}';

return $content;
}

public function __destruct()
{
if ($this->mapper->config->debug) {
Expand All @@ -209,6 +224,10 @@ private function buildPropertyForeachMapping(string $propertyName, array $proper
$foreach .= \PHP_EOL . \str_repeat(' ', 2) . '}';
}

if ($this->mapper->config->allowUninitializedFields && ! \array_key_exists('default', $property)) {
$foreach = $this->wrapArrayKeyExists($foreach, $propertyName);
}

return $foreach;
}
}
8 changes: 8 additions & 0 deletions tests/MapperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,13 @@ public static function objectValuesDataProvider(): Generator
],
$dto,
];

// Allow uninitialized properties
$dto = new Aliases();
yield [
Aliases::class,
[],
$dto,
];
}
}