-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #96 from jolicode/feat/provider
feat(mapper): add a way to instantiate object in mapper with providers
- Loading branch information
Showing
25 changed files
with
446 additions
and
95 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Provider | ||
|
||
> [!WARNING] | ||
> Providers are experimental and may change in the future. | ||
Providers are a way to instantiate the `target` during the mapping process. | ||
|
||
By default, the AutoMapper will try to instantiate the `target` object using the constructor, or without if not possible. | ||
However, in some cases you may want to use a custom provider. Like fetch the object from the database, or use a factory. | ||
|
||
In this case you can create a provider class that implements the `ProviderInterface` interface. | ||
|
||
```php | ||
use AutoMapper\Provider\ProviderInterface; | ||
|
||
class MyProvider implements ProviderInterface | ||
{ | ||
public function provide(string $targetType, mixed $source, array $context): object|null | ||
{ | ||
return new $targetType(); | ||
} | ||
} | ||
``` | ||
|
||
You have to register this provider when you create the `AutoMapper` object. | ||
|
||
```php | ||
use AutoMapper\AutoMapper; | ||
|
||
$autoMapper = AutoMapper::create(providers: [new MyProvider()]); | ||
``` | ||
|
||
> [!NOTE] | ||
> When using the Symfony Bundle version of the AutoMapper, you can use the `auto_mapper.provider` tag to register the provider. | ||
> If you have autoconfiguration enabled, you do not need to register the provider manually as the tag will be automatically added. | ||
Then you can use the `#[MapProvider]` attribute on top of the `target` class that you want to use this provider. | ||
|
||
```php | ||
use AutoMapper\Attribute\MapProvider; | ||
|
||
#[MapProvider(provider: MyProvider::class)] | ||
class Entity | ||
{ | ||
} | ||
``` | ||
|
||
> [!NOTE] | ||
> When using the Symfony Bundle version of the AutoMapper, the provider will be the service id, which may be different | ||
> from the class name. | ||
Now, every time the `AutoMapper` needs to instantiate the `Entity` class, it will use the `MyProvider` class. | ||
|
||
If you provider return `null`, the `AutoMapper` will try to instantiate the `target` object using the constructor, or without if not possible. | ||
|
||
> [!NOTE] | ||
> When using the `target_to_populate` option in the `context` array, the `AutoMapper` will use this object instead of the | ||
> one created by the provider. | ||
### Early return | ||
|
||
If you want to return the object from the provider without mapping the properties, you can return a | ||
`AutoMapper\Provider\EarlyReturn` object from the `provide` method with the object you want to return inside. | ||
|
||
```php | ||
use AutoMapper\Provider\EarlyReturn; | ||
use AutoMapper\Provider\ProviderInterface; | ||
|
||
class MyProvider implements ProviderInterface | ||
{ | ||
public function provide(string $targetType, mixed $source, array $context): object|null | ||
{ | ||
return new EarlyReturn(new $targetType()); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace AutoMapper\Attribute; | ||
|
||
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS)] | ||
final readonly class MapProvider | ||
{ | ||
/** | ||
* @param ?string $source from which source type this provider should apply | ||
* @param string $provider the provider class name or service identifier | ||
*/ | ||
public function __construct( | ||
public string $provider, | ||
public ?string $source = null, | ||
) { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace AutoMapper\EventListener; | ||
|
||
use AutoMapper\Attribute\MapProvider; | ||
use AutoMapper\Event\GenerateMapperEvent; | ||
use AutoMapper\Exception\BadMapDefinitionException; | ||
|
||
class MapProviderListener | ||
{ | ||
public function __construct() | ||
{ | ||
} | ||
|
||
public function __invoke(GenerateMapperEvent $event): void | ||
{ | ||
if (!$event->mapperMetadata->targetReflectionClass) { | ||
return; | ||
} | ||
|
||
$attributes = $event->mapperMetadata->targetReflectionClass->getAttributes(MapProvider::class); | ||
|
||
if (0 === \count($attributes)) { | ||
return; | ||
} | ||
|
||
$provider = null; | ||
$defaultMapProvider = null; | ||
|
||
foreach ($attributes as $attribute) { | ||
/** @var MapProvider $mapProvider */ | ||
$mapProvider = $attribute->newInstance(); | ||
|
||
if ($mapProvider->source === null) { | ||
if ($defaultMapProvider !== null) { | ||
throw new BadMapDefinitionException(sprintf('multiple default providers found for class "%s"', $event->mapperMetadata->targetReflectionClass->getName())); | ||
} | ||
|
||
$defaultMapProvider = $mapProvider->provider; | ||
} elseif ($mapProvider->source === $event->mapperMetadata->source) { | ||
if ($provider !== null) { | ||
throw new BadMapDefinitionException(sprintf('multiple providers found for class "%s"', $event->mapperMetadata->targetReflectionClass->getName())); | ||
} | ||
|
||
$provider = $mapProvider->provider; | ||
} | ||
} | ||
|
||
$event->provider ??= $provider ?? $defaultMapProvider; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.