Building API Transformers with Multiple Interfaces in Laravel

πŸ“– 3 minutes read

Need to transform data between two different API formats? Implement both interfaces in a single service class. This pattern creates a bridge that speaks both languages.

The Problem

You’re building a mobile app that expects data in Format A (clean REST), but your data source provides Format B (legacy XML-RPC or a different REST structure). You need to translate between them without rewriting either side.

The Solution: Dual Interface Implementation

Create a transformer service that implements both interfaces:

<?php

namespace App\Services;

use App\Contracts\SourceApiInterface;  // What you're fetching from
use App\Contracts\TargetApiInterface;  // What clients expect

class DataTransformer implements SourceApiInterface, TargetApiInterface
{
    private HttpClient $client;
    
    public function __construct(HttpClient $client)
    {
        $this->client = $client;
    }

    // ===================================================
    // SOURCE API METHODS (fetch from external API)
    // ===================================================
    
    public function fetchProducts(array $filters): array
    {
        // Fetch from external API
        $response = $this->client->get('/v1/items', $filters);
        return $response->json();
    }
    
    public function fetchCategories(): array
    {
        $response = $this->client->get('/v1/taxonomies');
        return $response->json();
    }

    // ===================================================
    // TARGET API METHODS (transform + expose to clients)
    // ===================================================
    
    public function getItems(string $category, int $limit = 20): array
    {
        // Map category to source filter
        $sourceFilters = ['taxonomy_id' => $this->mapCategory($category)];
        
        // Fetch using source API method
        $sourceData = $this->fetchProducts($sourceFilters);
        
        // Transform to target format
        return $this->transformToItems($sourceData);
    }
    
    public function getItemDetails(string $id): array
    {
        $sourceData = $this->fetchProduct($id);
        return $this->transformToItemDetails($sourceData);
    }

    // ===================================================
    // PRIVATE TRANSFORMATION LOGIC
    // ===================================================
    
    private function transformToItems(array $sourceProducts): array
    {
        return array_map(function ($product) {
            return [
                'id' => $product['item_id'],
                'title' => $product['name'],
                'price' => $product['cost'] / 100,  // cents to dollars
                'stock' => $product['inventory']['available'],
            ];
        }, $sourceProducts);
    }
    
    private function mapCategory(string $targetCategory): int
    {
        // Map client-facing category names to source taxonomy IDs
        return match($targetCategory) {
            'electronics' => 15,
            'books' => 8,
            'clothing' => 23,
            default => 1,
        };
    }
}

How It Works

  1. Source interface methods (fetchProducts, fetchCategories) handle raw API calls to the external service
  2. Target interface methods (getItems, getItemDetails) expose the clean API your clients expect
  3. Transformation happens in private methods that map fields, rename keys, convert units, etc.

The transformer becomes a bidirectional adapter β€” it speaks the source API internally and the target API publicly.

When to Use This Pattern

  • Wrapping legacy APIs with modern REST interfaces
  • Multi-source data aggregation where you combine APIs into one unified interface
  • API versioning β€” v2 interface transforms data from v1 endpoints
  • SDK replacement β€” your app expects Reddit API format, but you’re pulling from WordPress

Benefits

Type safety: Both interfaces enforce contracts at compile time

Testability: Mock either interface independently

Single responsibility: One class, one job β€” transform between two formats

Swappability: Replace the source API without changing client code (dependency inversion)

Real-World Example

A mobile app needed a Reddit-style API (posts, comments, upvotes) but the content lived in WordPress. The transformer implemented both WordPressApiInterface and RedditApiInterface:

  • fetchPosts() called WordPress REST API
  • getHot() transformed WP posts into Reddit listing format
  • Mobile app saw clean Reddit JSON, never touched WordPress directly

Key insight: When formats overlap, the target format wins. The transformer hides complexity from clients.

Daryle De Silva

VP of Technology

11+ years building and scaling web applications. Writing about what I learn in the trenches.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *