How to Integrate multiple external data sources in Laravel with DTOs

Lokman Musliu Founder and CEO of Lucky Media
Lokman Musliu

May 16, 2025 · 4 min read

How to integrate multiple external data sources in Laravel with DTOs

As developers, we often face the challenge of integrating data from multiple sources into a unified structure. In the world of Laravel, this can be particularly daunting when your application needs to handle reviews coming from various platforms like Google, Tripadvisor, and Facebook. Today, we’re going to explore a streamlined approach to tackle this using Laravel’s Model and the Spatie Laravel Data package. Grab your favorite cup of coffee, and let’s dive in!

A note before we start:

In our requirement, we had to store reviews from different sources in a normalized Laravel model. Your requirement might be different but the same rules apply.

This approach works best when integrating with up to five services. If you need to handle data from more than ten sources, this method might not be the most effective. We’ll explore more advanced solutions in a future tutorial.

The Challenge: Multiple Integrations, One Model

Imagine you have a Laravel application with a Review model. Your client wants to import reviews from different platforms and display them under this single model. Sounds simple, right? But here’s the twist: each integration has its own data structure. For instance, Google might have an user.username field, while Tripadvisor has it as author.name.

We’ll use the Spatie Laravel Data package to create Data Transfer Objects (DTOs). These DTOs will help us map data from different platforms into a consistent format.

Step 1: Creating the Laravel Model

We create a Review model to store all the reviews. This model will have generic columns to accommodate data from various sources.

Run this Artisan command to generate the model and migration:

php artisan make:model Review -m

Next, in the migration file (database/migrations/xxxx_xx_xx_create_reviews_table.php), define the columns:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('reviews', function (Blueprint $table) {
            $table->id();
            $table->string('platform_review_id')->unique();
            $table->string('author');
            $table->tinyInteger('rating');
            $table->dateTime('date');
            $table->string('platform');
            $table->text('text')->nullable();
            $table->timestamps();
        });
    }
};

Now run php artisan migrate to make the necessary changes.

Step 2: Installing the Spatie Laravel Data Package

The Spatie Laravel Data package is like a Swiss Army knife for managing and validating data structures.

composer require spatie/laravel-data

# Optionally, publish the configuration file if you’re into that sort of thing:
php artisan vendor:publish --tag=data-config

Step 3: Building the Base Review Data Transfer Object (DTO)

Create a base DTO to map to the Review model, think of it as the blueprint for our review data.

php artisan make:data ReviewData

Here is the code for the ReviewData DTO:

namespace App\Data;

use Carbon\CarbonImmutable;
use Spatie\LaravelData\Data;

class ReviewData extends Data
{
    public function __construct(
        public string $platform_review_id,
        public string $author,
        public int $rating,
        public CarbonImmutable $date,
        public string $platform,
        public ?string $text = null,
    ) {}
}

This DTO includes all the fields that match the Review model’s columns.

Step 4: Tailoring the DTO for Each Platform

We need to extend our base ReviewData DTO with methods for each platform.

For example, let’s handle Google and TripAdvisor. Update your ReviewData DTO:

namespace App\Data;

use Carbon\CarbonImmutable;
use Spatie\LaravelData\Data;

class ReviewData extends Data
{
    public function __construct(
        public string $platform_review_id,
        public string $author,
        public float $rating,
        public CarbonImmutable $date,
        public string $platform,
        public ?string $text = null,
    ) {}

    public static function fromGoogle(array $data): self
    {
        return new self(
            platform_review_id: $data['id'],
            author: data_get($data, 'user.username', 'anonymous'),
            rating: $data['rating'],
            date: CarbonImmutable::parse($data['publishedDate']),
            platform: 'google', // this could be an Enum
            text: data_get($data, 'text', null),
        );
    }

    public static function fromTripadvisor(array $data): self
    {
        return new self(
            platform_review_id: $data['reviewId'],
            author: data_get($data, 'author.name', 'anonymous'),
            rating: $data['stars'],
            date: CarbonImmutable::parse($data['date']),
            platform: 'tripadvisor', // this could be an Enum
            text: data_get($data, 'text', null),
        );
    }
    // Add more methods for other platforms as needed
}

Explanation:

  • fromGoogle Method: Maps Google review data to the ReviewData DTO, defaulting to ‘anonymous’ if the author’s name is missing.

  • fromTripadvisor Method: Does the same for TripAdvisor, adjusting for field differences like reviewId and stars.

Feeling adventurous? Add more methods like fromFacebook to handle other platforms.

Step 5: Mapping Data with Match Statements

Once your DTOs are ready, converting incoming data into a standardized ReviewData format is a breeze. Here’s how to do it with a match statement.

Assume you receive a review from a specific platform. Use this logic to map and store it:

namespace App\Actions;

use App\Models\Review;
use App\Data\ReviewData;
use InvalidArgumentException;

class StoreReview
{
    public function handle(string $platform, array $review)
    {
        $data = match ($platform) {
            'google' => ReviewData::fromGoogle($review),
            'tripadvisor' => ReviewData::fromTripadvisor($review),
            default => throw new InvalidArgumentException("Unsupported platform: $platform"),
        };

        Review::firstOrCreate(
            [
                'platform_review_id' => $data->platform_review_id,
            ],
            [
                'author' => $data->author,
                'rating' => $data->rating,
                'date' => $data->date,
                'platform' => $data->platform,
                'text' => $data->text,
            ]
        );
    }
}

Explanation:

  • match Statement: Decides which DTO method to use based on the platform.

  • Creating the Review: Maps the standardized data to create a new Review record.

This method ensures consistency, regardless of where the review originates.

Conclusion

Handling data from multiple integrations isn’t just possible, it’s efficient and scalable with the right tools. By using Laravel’s features and the Spatie Laravel Data package, you can normalize and store data from various platforms like a pro. Your Laravel application will stay organized and maintainable, even as you bring more platforms into the fold.

FAQs

Why use DTOs in Laravel?

DTOs help separate business logic from data presentation, making your code cleaner, more organized, and easier to maintain. We have written an article about Laravel Typesafe AI responses with Prism and DTOs, if you want to read more about this topic.

What’s the benefit of using the Spatie Laravel Data package?

It simplifies data management and validation, allowing you to handle complex data structures effortlessly.

Can I add more platforms to this setup?

Absolutely! Just extend the ReviewData DTO with methods for each new platform you want to integrate.

How do I handle missing data fields?

Use default values in your DTO methods, like we did with the author field, defaulting to ‘anonymous.’

Is it necessary to publish the Spatie package configuration?

No, it’s optional. But publishing it allows you to customize the package settings if needed.


Bring Your Ideas to Life 🚀

If you need help with a Laravel project let’s get in touch.

Lucky Media is proud to be recognized as a leading Laravel Development Agency

Technologies

Laravel
Lokman Musliu Founder and CEO of Lucky Media
Lokman Musliu

Founder and CEO of Lucky Media

Stay up-to-date

Be updated with all news, products and tips we share!

Let’s chat

We partner with a limited number of brands each quarter to ensure senior-level attention on every project.

lokman and arlind headshots
Teamwork