Search code examples
phplaraveladapterapi-gateway

How to consume multiple services using same class


I need a way to handle multiple different services with the same class. Let's say I have a local data base where my local posts are stored. Each time I save a post to this local database I need to post with those services as well. So they will need a adapter pattern for sure so that I can always use the same methods. That is clear but I am not sure how I could make the main class interchangable so that I can load the needed platform and service on the fly.

The structure could be like:

App              App
 Adapters         Adapters
  ServiceA         ServiceB   
   Post.php         Post.php
   Category.php     Category.php
   ...              ...

By using the adapter pattern methods within Post.php will have adapted the same methods sendPost() and deletePost().

Now I will be able to send post for each platform using the same sendPost() method. I could do like this.

$post = Post::create($form_array);
$services = Service::get();

foreach ($services as $service){
    $class = '\\App\\Adapters\\'.$service->name.'\\Post';
    $service = new $class;
    $service->sendPost($post); 
}

or

$post = Post::where('id', 44);
$services = Service::get();

foreach ($services as $service){
    $class = '\\App\\Adapters\\'.$service->name.'\\Post';
    $service = new $class;
    $service->deletePost($post->id); 
}

Actually this aproach works but it doesn't seem (feel) to be the right way to go. Here for instance each time I need another service instead Post I would need to create another instance.

$class = '\\App\\Adapters\\'.$service->name.'\\Category';
$service = new $class;
$service->deletePost($post->id); 

I am looking more something like.

$service = new Service('ServiceA');
$service->post->sendPost($post);
$service->category->create($cat);

then I could

$post = Post::where('id', 44);
$services = Service::get();

foreach ($services as $service){
    $service = new Service($service->name)
    $service->post->deletePost($post->id); 
}

I know this is not a very detailed representation but you get the idea. I want to consume multiple services and being able to implement it with my local system. So what is the right and best way to do this?


Solution

  • I'm not an expert when it come to adding method to currently existing Class. This questions stuck in my head and I've been working on a solution that may be not the most clean, but I think you can like it.

    In the end, this is how I've been able to access a method inside each Post.php files.

    public function __construct()
    {
        $this->service = Service::boot();
    }
    
    public function test()
    {
        $service = Service::getService('ServiceB');
        return $service->post->testPost();
    }
    
    public function testGlobal()
    {
        return $this->service->serviceA->post->testPost();
    }
    
    public function testAllServices()
    {
        $service = Service::boot();
        return $service->serviceA->post->testPost();
    }
    

    To achieve this, I did not had your original Service file, so I made one at App/Adapters

    <?php
    
    namespace App\Adapters;
    
    class Service
    {
        public static function boot() {
            $directory = dirname(__DIR__).'/Adapters';
            $services = array_diff(scandir($directory), array('..', '.'));
    
            $container = new class {};
            
            foreach($services as $key => $service) {
                if (is_dir(dirname(__DIR__).'/Adapters/'.$service)) {
                    $container->{lcfirst($service)} = new class {};
                    $directory = dirname(__DIR__).'/Adapters/'.$service;
                    $classes = array_diff(scandir($directory), array('..', '.'));
                    foreach($classes as $secondary_key => $class) {
                        $class = str_replace('.php', '', $class);
                        $namespace = 'App\Adapters\\'.$service.'\\'.$class;
                        $container->{lcfirst($service)}->{lcfirst($class)} = new $namespace;
                    }
                }
            }
    
            return $container;
        }
    
        public static function getService($service) {        
            $container = new class {};
            $directory = dirname(__DIR__).'/Adapters/'.$service;
            $classes = array_diff(scandir($directory), array('..', '.'));
            foreach($classes as $secondary_key => $class) {
                $class = str_replace('.php', '', $class);
                $namespace = 'App\Adapters\\'.$service.'\\'.$class;
                $container->{lcfirst($class)} = new $namespace;
            }
    
            return $container;
        }
    }
    

    The main purpose is to scan everything within Adapters and creating method that implement the class depending on the parent directory.

    If you like that, I can provide more explanation if needed.