Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add dynamic interface resolution #560

Closed
wants to merge 3 commits into from
Closed

add dynamic interface resolution #560

wants to merge 3 commits into from

Conversation

mastir
Copy link

@mastir mastir commented Sep 9, 2024

Made interface resolvers implementation for #558 . This implementation allows to add discriminator properties to normalized array and resove actual class based on descriminator props

Example:

interface Container{}

class BoxContainer implements Container{
    function __construct(int $width, int $height){}
}

class SquereContainer implements Container{
    function __construct(int $width, int $height){}
}

class CircleContainer implements Container{
    function __construct(int $radius){}
}

class MyResolver(){
    public function resolve(string $interface, ?array $props) : ?string
    {
        if ($interface === 'Container'){
            return match($props['type']){
                'box' => BoxContainer::class,
                'squere' => SquereContainer::class,
                'circle' => CircleContainer::class,
            }
        }
    }
    
    public function getResolverProps(string $interface) : array
    {
        return ['type'];
    }
    
    public function transform(object $input, callable $next) : array
    {
         $result = $next($input);
         if ($input instanceOf ContainerInterface){
              $result['type'] = match(get_class($input)){
                 BoxContainer::class => 'box',
                SquereContainer::class => 'squere',
                CircleContainer::class => 'circle',
             }
         }
         return  $result;
    }
}

$mapperBuilder->withInterfaceResolver(new MyResolver());
$normalized  = $normalizer->normalize(new BoxContainer(3,5)); // ['type'=>'box', 'width'=>3, 'height'=>5]
$object = $mapper->map(Container::class, $normalized); //new BoxContainer(3,5)

This can partialy be done with infer / transform, but some validations faild due too extra fields and dynamic interface reading is not possible. In my project i use Attribute on interfaces to declare type mapping and discriminator field:

#[TypedInterface([
    'box' => BoxContainer::class,
    'squere' => SquereContainer::class,
    'circle' => CircleContainer::class,
], 'conteiner_type')]
interface Container{
...

class InterfaceObjectResolver implements \CuyZ\Valinor\InterfaceResolver
{

    public function resolve(string $interface, ?array $props) : ?string
    {
        [$type_map, $discriminator] = $this->readAttributes($interface);
        if (!$type_map){
            return null;
        }
        return $type_map[$props[$discriminator]];
    }
...

@romm
Copy link
Member

romm commented Sep 18, 2024

This feature already exists in the library, see #558 (comment)

Thanks for the effort, though! I'll have to think about making documentation more precise about it.

@romm romm closed this Sep 18, 2024
@mastir
Copy link
Author

mastir commented Oct 3, 2024

Thanks @romm for reply, but its not solving real problem: Mapper is a service, services are loaded on bootstrap. At the bootstrap we do not have interfaces, we cannot use functions with not yet existing classes and interfaces. Preloading whole project at bootstrap to implement interface resolution is not a solution. This is fine for test cases, but not for real applications.

So there is only 2 ways:

  1. On every module load generate resolve functions to infer into mapper builder and recreate mapper, just in case we will ever need em later (wich is not in 99% cases). So we have a lots of unused generated function and preloaded interfaces/classes in memory and lost caching.
  2. add dynamic interface resolution.

Most php framworks use DI and/or dynamic class loaders (since php 5.0). Spliting bootstrap and resolve process is required to integrate with any of them

@mastir mastir mentioned this pull request Nov 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants