DataTransfer
Library for patching destination data with source data only if destination data remains valid after that
Install / Use
/learn @Articus/DataTransferREADME
Data Transfer
This library provides a "validating hydrator", a service that patches destination data with source data only if destination data remains valid after that. Source and destination can be anything - scalars, arrays, objects... So either you want to make a partial update of ORM entity with parsed JSON from HTTP-request or produce a plain DTO from this entity to send in AMQP-message this library can help you to do that in a neat convenient way.
How it works?
Let's make few definitions:
- typed data - some complex, application-specific, rigidly structured data like object or array of objects. For example, DTO's or ORM entities.
- untyped data - the opposite of typed data - some simple, general-purpose, amorphous data like scalar or array of scalars or stdClass instance. For example, result of
json_decodeoryaml_parse. - extract - an algorithm to convert typed data to untyped data
- merge - an algorithm to patch one piece of untyped data with another piece of untyped data
- validate - an algorithm to check that untyped data is correct according some rules
- hydrate - an algorithm to patch typed data with untyped data
So if we have two pieces of typed data - A and B - this library does a rather simple thing to transfer A to B: it merges pieces of untyped data extracted from A and B, validates the result and hydrates B with untyped data extracted from A if validation is successful.
Why?
Personally I just needed something to easily update DTOs and Doctrine entities from untrusted sources (like request parsed body, request headers, request query parameters and etc). Something like request body converter from FOSRestBundle and JMS Serializer, but more flexible. The initial prototype was extremely useful for building APIs and after using it in several production projects I finally decided to make it a separate library. Hopefully, it will be useful for someone else.
How to install?
Just add "articus/data-transfer" to your composer.json and check packages suggested by the library for extra dependencies of optional components you may want to use.
How to use?
Library provides a single service Articus\DataTransfer\Service that allows transferring data in various ways. So first of all you need to register it in your PSR-11 container. You can use any PSR-11 implementation you like, but integration with Laminas Service Manager has slightly more features (to be precise - utilization of plugin managers and supports for Laminas validators). Here are two sample configurations:
// Full example configuration in YAML just for readability
$configContent = <<<'CONFIG'
# Required container services
dependencies:
factories:
# Service to inject wherever you need data transfer
Articus\DataTransfer\Service: Articus\DataTransfer\Factory
# ..and its dependencies
Articus\DataTransfer\MetadataProvider\Annotation: Articus\DataTransfer\MetadataProvider\Factory\Annotation
Articus\DataTransfer\Strategy\PluginManager: Articus\DataTransfer\Strategy\Factory\LaminasPluginManager
Articus\DataTransfer\Validator\PluginManager: Articus\DataTransfer\Validator\Factory\LaminasPluginManager
# Optional - only if you want to use validators from laminas/laminas-validator
Laminas\Validator\ValidatorPluginManager: Laminas\Validator\ValidatorPluginManagerFactory
# Default metadata provider service allows to get metadata both for classes and for class fields so two aliases for single service
aliases:
Articus\DataTransfer\ClassMetadataProviderInterface: Articus\DataTransfer\MetadataProvider\Annotation
Articus\DataTransfer\FieldMetadataProviderInterface: Articus\DataTransfer\MetadataProvider\Annotation
# Configure metadata provider
Articus\DataTransfer\MetadataProvider\Annotation:
# Configure directory to store cached class metadata
cache:
directory: ./data
# ... or use existing service implementing Psr\SimpleCache\CacheInterface (PSR-16)
#cache: MyMetadataCache
# Configure strategy plugin manager using options supported by Laminas\ServiceManager\AbstractPluginManager
Articus\DataTransfer\Strategy\PluginManager:
invokables:
MySampleStrategy: My\SampleStrategy
# Configure validator plugin manager using options supported by Laminas\ServiceManager\AbstractPluginManager
Articus\DataTransfer\Validator\PluginManager:
invokables:
MySampleValidator: My\SampleValidator
CONFIG;
$config = yaml_parse($configContent);
$container = new Laminas\ServiceManager\ServiceManager($config['dependencies']);
$container->setService('config', $config);
/** @var Articus\DataTransfer\Service $service */
$service = $container->get(Articus\DataTransfer\Service::class);
<?php
require_once __DIR__ . '/vendor/autoload.php';
// Full example configuration in YAML just for readability
$configContent = <<<'CONFIG'
# Required container services
dependencies:
factories:
# Service to inject wherever you need data transfer
Articus\DataTransfer\Service: Articus\DataTransfer\Factory
# ..and its dependencies
Articus\DataTransfer\MetadataProvider\Annotation: Articus\DataTransfer\MetadataProvider\Factory\Annotation
Articus\DataTransfer\Strategy\PluginManager: Articus\DataTransfer\Strategy\Factory\SimplePluginManager
Articus\DataTransfer\Validator\PluginManager: Articus\DataTransfer\Validator\Factory\SimplePluginManager
# Default metadata provider service allows to get metadata both for classes and for class fields so two aliases for single service
aliases:
Articus\DataTransfer\ClassMetadataProviderInterface: Articus\DataTransfer\MetadataProvider\Annotation
Articus\DataTransfer\FieldMetadataProviderInterface: Articus\DataTransfer\MetadataProvider\Annotation
# Configure metadata provider
Articus\DataTransfer\MetadataProvider\Annotation:
# Configure directory to store cached class metadata
cache:
directory: ./data
# ... or use existing service implementing Psr\SimpleCache\CacheInterface (PSR-16)
#cache: MyMetadataCache
# Configure strategy plugin manager, check Articus\PluginManager\Options\Simple for supported options
Articus\DataTransfer\Strategy\PluginManager:
invokables:
MySampleStrategy: My\SampleStrategy
# Configure validator plugin manager, check Articus\PluginManager\Options\Simple for supported options
Articus\DataTransfer\Validator\PluginManager:
invokables:
MySampleValidator: My\SampleValidator
CONFIG;
$config = yaml_parse($configContent);
$container = new Symfony\Component\DependencyInjection\ContainerBuilder();
$containerRef = new Symfony\Component\DependencyInjection\Reference('service_container');
foreach ($config['dependencies']['factories'] as $serviceName => $factoryClass)
{
$container->register($factoryClass);
$container->register($serviceName)
->setFactory(new Symfony\Component\DependencyInjection\Reference($factoryClass))
->setArguments([$containerRef, $serviceName])
->setPublic(true)
;
}
foreach ($config['dependencies']['aliases'] as $alias => $serviceName)
{
$container->setAlias($alias, $serviceName)->setPublic(true);
}
// Just to reduce sample code size - there should be a dedicated factory class for normal usage
$configFactory = new class ($config)
{
protected ArrayAccess $config;
public function __construct(array $config) { $this->config = new ArrayObject($config); }
public function getConfig(): ArrayAccess { return $this->config; }
};
$container->register('config', ArrayAccess::class)->setFactory([$configFactory, 'getConfig'])->setPublic(true);
$container->compile();
/** @var Articus\DataTransfer\Service $service */
$service = $container->get(Articus\DataTransfer\Service::class);
That is the only requirement to use Articus\DataTransfer\Service::transfer method that provides the most explicit and fine-grained control over data transfer.
If you provide some additional metadata for classes that you would like to use with data transfer service several more convenient methods will be available:
Articus\DataTransfer\Service::transferTypedDataArticus\DataTransfer\Service::transferToTypedDataArticus\DataTransfer\Service::transferFromTypedDataArticus\DataTransfer\Service::extractFromTypedData
Currently, the default way to declare metadata shown in code examples across this documentation is via Doctrine Annotations. If your project uses PHP 8+ you may declare metadata via attributes instead (just switch from Articus\DataTransfer\MetadataProvider\Annotation to Articus\DataTransfer\MetadataProvider\PhpAttribute). And you can create your own implementation for `Articus
Related Skills
node-connect
347.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.0kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
