This article shows you 12 real-world examples of the adapter design pattern.
Most of them are taken from the projects I faced in my daily work.
All examples are coded in PHP, but it is not a problem for you to understand them if you program in Java, C#, Typescript, C++, or any other OOP language.
The adapter pattern helps two incompatible interfaces interact with each other.

The client is any code using the adapter (e.g. UserService, PaymentProcessor etc).
The adapter is a class that adapts the code in the adaptee to some interface (e.g. TwitterApiAdapter).
The adaptee is an interface we adapt to use in our code.
We’ll cover the following examples of the Adapter pattern in the article:
- Getting News From an API
- Slug generation and the adapter pattern
- Searching images
- Fetching the price of Bitcoin
- Sending emails [why the Adapter pattern matters]
- Generating invoices
- Ip geolocation
- Converting markdown
- Getting a proxy IP
- Thumbnail creation
- URLs shortening
- Fetching weather data
1. Getting News From an API
Your manager comes to you and says:
“We need to show news in our site for the last 3 days. The topics are: Bitcoin, Ethereum, Litecoin.”
For this purpose you need to use a third-party API that provides news.

As there are lots of such services, you need to design your code with the following details in mind:
You should be able to replace a news API with any other service without modifying your code in all places this API is used.
This is a good case for the Adapter pattern.
So first you need to design a method that searches the latest news and has the following parameters:
- a topic news belongs to
- the date from which news is taken
- the number of news in one request
In the end this is what you have:
public function search( string $query, // this is where you define a topic DateTime $fromDate, int $limit // the max number of news in one request ): array;
We also need to place the method search()
into an interface to make it possible to pass it as a dependency for other classes:
<?php namespace App\Adapters\News; use RuntimeException; use DateTime; interface NewsClientInterface { /** * @param string $query * @param DateTime $fromDate * @param int $limit * @return NewsDto[] * * @throws RuntimeException */ public function search( string $query, DateTime $fromDate, int $limit ): array; }
But it is not enough.
Now we need to create a class that implements NewsClientInterface
.
This is gonna be a class that fetches news from a third-party API.
After some googling you could find a news API that suits your needs:
newsapi.org
You are happy – the task is complete (actually not)!
Looking at the docs of newsapi.org you find that its endpoint has a few parameters your code does not need.
This is what its NewsApiClientInterface looks like:
<?php namespace App\Adapters\News\NewsApi; use GuzzleHttp\Exception\GuzzleException; use DateTime; interface NewsApiClientInterface { /** ...................... * @throws GuzzleException */ public function getEverything( string $query, DateTime $fromDate, DateTime $toDate, // you don't need this param int $pageSize, int $page // and this too ): array; }
Our NewsClientInterface
needs to use NewsApiClientInterface
, but their methods for fetching news are incompatible.

What is the way out?
The adapter design pattern to the rescue!
Let’s create a NewsApiClientAdapter
(I will explain the code line by line below):
<?php namespace App\Adapters\News; use App\Adapters\News\NewsApi\NewsApiClientInterface; use DateTime; use Exception; use RuntimeException; class NewsApiClientAdapter implements NewsClientInterface { private NewsApiClientInterface $newsApiClient; public function __construct(NewsApiClientInterface $newsApiClient) { $this->newsApiClient = $newsApiClient; } public function search( string $query, DateTime $fromDate, int $limit ): array { $toDate = (new DateTime())->modify('+ 100 years'); try { $newsArr = $this->newsApiClient->getEverything( $query, $fromDate, $toDate, $limit, 1 ); $newsDtosArr = []; foreach ($newsArr as $newsItem) { $newsDtosArr[] = new NewsDto( $newsItem['source']['name'], $newsItem['author'] ?? 'No author', $newsItem['title'], $newsItem['description'], $newsItem['content'], $newsItem['url'], new DateTime($newsItem['publishedAt']), ); } return $newsDtosArr; } catch (Exception $e) { throw new RuntimeException($e->getMessage()); } } }
On line 10 we declare NewsApiClientAdapter
that implements NewsClientInterface
:
class NewsApiClientAdapter implements NewsClientInterface
On lines 14-17 NewsApiClientInterface
is injected through the constructor:
public function __construct(NewsApiClientInterface $newsApiClient) { $this->newsApiClient = $newsApiClient; }
On lines 28-34 the most important thing happens: we “adapt” NewsApiClientInterface
to NewsClientInterface
.
$newsArr = $this->newsApiClient->getEverything( $query, $fromDate, $toDate, $limit, 1 );

On lines 36-50 we adapt return types of our interfaces:
$newsDtosArr = []; foreach ($newsArr as $newsItem) { $newsDtosArr[] = new NewsDto( $newsItem['source']['name'], $newsItem['author'] ?? 'No author', $newsItem['title'], $newsItem['description'], $newsItem['content'], $newsItem['url'], new DateTime($newsItem['publishedAt']), ); } return $newsDtosArr;
The search()
method is supposed to return an array of NewsDto
, but the response of newsapi.org is different.
So, we have to transform (adapt) the return type too.
The full source code for the news API you can find here.
2. Slug generation
Generating slugs is a common task for web applications.

You can install a package that can solve this problem like this:
composer require ausi/slug-generator
This package even provides its own interface SlugGeneratorInterface
and our code may depend on an interface instead of a concrete implementation.
It allows us to make our code SOLID and keep the Dependency Inversion Principle (our modules (e.g. UserService) should depend on abstractions).

So let’s use SlugGeneratorInterface
from the ausi/slug-generator package!
public function makePostUrl( string $postTitle, SlugGeneratorInterface $slugGenerator ): string { $slug = $slugGenerator->generate($postTitle); return 'https://webcodingo.com/' . $slug; }
Notice
It is better to pass dependencies in the construstor, but to keep things simple, we pass SlugGeneratorInterface
directly to the makePostUrl()
method.
But there are still some problems in our code:
- we may decide to replace the ausi/slug-generator with some other package
- the ausi/slug-generator may change its interface after another update
- we may decide to make our own implementation of the
slug()
method
How do we deal with them?
Again, the Adapter design pattern helps us!

Let’s create SlugInterface
:
<?php namespace App\Adapters\Slug; interface SlugInterface { public function generate(string $text): string; }
Now let’s replace SlugGeneratorInterface
from the ausi/slug-generator package with our own SlugInterface
:
public function makePostUrl( string $postTitle, SlugInterface $slugGenerator ): string { $slug = $slugGenerator->generate($postTitle); return 'https://webcodingo.com/' . $slug; }
The only thing we need to do is to create an implementation of SlugInterface we’ve just created.
Let’s create AusiLibraryAdapter
:
<?php namespace App\Adapters\Slug; use Ausi\SlugGenerator\SlugGeneratorInterface; class AusiLibraryAdapter implements SlugInterface { private SlugGeneratorInterface $slugGenerator; public function __construct(SlugGeneratorInterface $slugGenerator) { $this->slugGenerator = $slugGenerator; } public function generate(string $text): string { return $this->slugGenerator->generate($text); } }
In AusiLibraryAdapter
we adapt SlugGeneratorInterface from the ausi/slug-generator package to our SlugInterface
.
Great!
Now we can be sure that our code won’t break if we update the ausi/slug-generator package or replace it with something else.
Notice
The Adapter pattern does not always adapt two incompatible interfaces. In the example above AusiLibraryAdapter
acts like a wrapper around the third-party library. Besides, our two interfaces are compatible.
The full source code for the slug generator you can find here.
3. Searching images
Searching images may be needed if you create:
- a service for generating memes
- a blogging platform that allows you to find images for posts
- an app for generating the previews of youtube videos

Let’s create an interface we’ll use for searching images:
<?php namespace App\Adapters\Image; use RuntimeException; interface ImageSearchInterface { /** * @param string $query * @param int $maxResults * @return ImageDto[] * * @throws RuntimeException */ public function search(string $query, int $maxResults): array; }
It’s time to create an implementation for ImageSearchInterface
.
Before that we need to think about two things:
- what third-party API for searching images we’re gonna use
- can this service be replaced by another one for some reason?
Pixabay provides one of the best free APIs for finding images.

Regarding the second question, there’s no doubt that we may decide to replace the Pixabay API for some reason, e.g.:
- Pixabay may change their license in the future
- we will have topics on which Pixabay does not provide images
Now we have no doubt that we need to use the Adapter design pattern for this case.
This is what PixabayApiInterface
looks like:
<?php namespace App\Adapters\Image\Pixabay; interface PixabayApiInterface { public function search( string $query, int $perPage, bool $safeSearch ): ?array; }
Our ImageSearchInterface
and PixabayApiInterface
are not compatible.

Let’s solve this problem by creating PixabayApiAdapter
that implements ImageSearchInterface
:
<?php namespace App\Adapters\Image; use App\Adapters\Image\Pixabay\PixabayApiInterface; use RuntimeException; class PixabayApiAdapter implements ImageSearchInterface { private PixabayApiInterface $pixabayApi; public function __construct(PixabayApiInterface $pixabayApi) { $this->pixabayApi = $pixabayApi; } public function search(string $query, int $maxResults): array { $pixabayImages = $this->pixabayApi->search($query, $maxResults, true); if($pixabayImages === null) { throw new RuntimeException('A server error occurred fetching images'); } /** @var ImageDto[] $resCollection */ $resCollection = []; foreach ($pixabayImages['hits'] as $pixabayImage) { $resCollection[] = new ImageDto($pixabayImage['webformatURL']); } return $resCollection; } }
On lines 12-15 we inject PixabayApiInterface
into the constructor:
public function __construct(PixabayApiInterface $pixabayApi) { $this->pixabayApi = $pixabayApi; }
On line 19 we call the search()
method of PixabayApiInterface
:
$pixabayImages = $this->pixabayApi->search($query, $maxResults, true);
The search()
method of PixabayApiInterface
has the third boolean parameter $safeSearch
the ImageSearchInterface
in its proper method is missing,
This is where adapting takes place: we complement the PixabayApiInterface->search()
method with parameters our interface’s method is missing.
There is also one more act of adapting the two interfaces (making them compatible) on lines 21-23:
if($pixabayImages === null) { throw new RuntimeException('A server error occurred fetching images'); }
Our ImageSearchInterface
is supposed to throw the RuntimeException
.
This exception is thrown when something went wrong with the third-party API
PixabayApiInterface
does not throw any exceptions.
So in PixabayApiAdapter
we throw RuntimeException
if $pixabayImages === null
(it means that some error in the Pixabay API occurred).
On lines 25-32 we adapt the returned values of the two interfaces:
/** @var ImageDto[] $resCollection */ $resCollection = []; foreach ($pixabayImages['hits'] as $pixabayImage) { $resCollection[] = new ImageDto($pixabayImage['webformatURL']); } return $resCollection;
That’s it for searching images.
The source code is here.
4. Fetching the price of Bitcoin
As you know, Bitcoin’s price skyrocketed in 2021.
It’s possible that you want to create a service that provides analytics for Bitcoin traders.
A third-party API for fetching the price of Bitcoin is needed for that.

First, let’s create an interface other classes will depend on when they need to get the price of Bitcoin:
<?php namespace App\Adapters\Bitcoin; interface BitcoinPriceInterface { public function fetchPrice(): ?float; }
Notice
The Adapter pattern is not always used to make two interfaces compatible in real projects.
In this example we’ll place all the code for getting Bitcoin’s price in CoinmarketcapAdapter
and this code acts as the second interface we adapt to BitcoinPriceInterface
.
Now we create CoinmarketcapAdapter
:
<?php namespace App\Adapters\Bitcoin; class CoinmarketcapAdapter implements BitcoinPriceInterface { private string $apiKey; public function __construct(string $apiKey) { $this->apiKey = $apiKey; } public function fetchPrice(): ?float { $resultObj = $this->requestLatestData(); if($resultObj->status->error_code === 0) { return $resultObj->data[0]->quote->USD->price; } return null; } // other private methods go here // we ommit them for simplicity // the link to the full code is below
The full source code is here.
The example of usage is here.
5. Sending emails [why the Adapter pattern matters]
Though most frameworks, including Laravel, has a built-in mechanism of sending emails, you may want to implement your own.
Creating your own mechanism of sending emails with the Adapter pattern makes your code framework-agnostic.

This is how it is gonna work:
public function mail(EmailClientInterface $emailAdapter) { $emailAdapter->send( 'username@emailservice.com', 'Hi! Thanks for signing up!', 'Here is your verification code 12345' ); // other code here }
Here is our EmailClientInterface
:
<?php namespace App\Adapters\Email; interface EmailClientInterface { public function send( string $toAddress, string $subject, string $message ): void; }
Let’s pick the Mailgun service for sending emails.

We’ll use the official PHP SDK.
This is how it works:
<?php use Mailgun\Mailgun; $mg = Mailgun::create('key-example');
Unfortunately, Mailgun’s official SDK does not provide an object interface.
Instead, it gives us the Mailgun
class with the static method create()
.
So, we’ll adapt the Mailgun
class (that acts like an interface) to the EmailClientInterface
you can see above.
<?php namespace App\Adapters\Email; use Mailgun\Mailgun; class MailgunEmailAdapter implements EmailClientInterface { private string $apiKey; private string $domain; private string $fromAddress; public function __construct( string $apiKey, string $domain, string $fromAddress ) { $this->apiKey = $apiKey; $this->domain = $domain; $this->fromAddress = $fromAddress; } public function send(string $toAddress, string $subject, string $message): void { $mg = Mailgun::create($this->apiKey); $mg->messages()->send($this->domain, [ 'from' => $this->fromAddress, 'to' => $toAddress, 'subject' => $subject, 'text' => $message, ]); } }
The full source code is here.
6. Generating invoices
Generating invoices is a very common task in any business.
We can switch between different libraries and APIs for this task.
The Adapter design pattern saves us again here.

This is the InvoiceGeneratorInterface
for that purpose:
<?php namespace App\Adapters\Invoice; use RuntimeException; interface InvoiceGeneratorInterface { /** ... omitting other params here to be short * * @throws RuntimeException */ public function generate( PartyDto $sellerDto, PartyDto $buyerDto, array $invoiceItemDtos, string $currencyCode, string $currencySymbol, string $fileName ): void; }
Here we have a bit more params than in the previous examples.
The example of usage:
public function invoice(InvoiceGeneratorInterface $invoiceGenerator) { $sellerDto = new PartyDto('Andrew', '202-555-0193'); $buyerDto = new PartyDto('Mark', '308-158-0173'); $invoiceItemDtos = [ new InvoiceItemDto('Apples', 5, 7, 3), new InvoiceItemDto('Oranges', 8, 5, 7), ]; $fileName = 'Invoice_0001'; $invoiceGenerator->generate( $sellerDto, $buyerDto, $invoiceItemDtos, 'USD', '$', $fileName ); // some other code }
In our project we’ll use awesome package LaravelDaily/laravel-invoices.
It provides the Invoice
class.
Let’s adapt this to InvoiceGeneratorInterface
to produce a PDF invoice.
It takes a lot of code.
You may skip to the line 60 as this is where this final adaptation takes place:
<?php namespace App\Adapters\Invoice; use LaravelDaily\Invoices\Classes\InvoiceItem; use LaravelDaily\Invoices\Classes\Party; use LaravelDaily\Invoices\Invoice; use RuntimeException; use Exception; class LaravelInvoicesAdapter implements InvoiceGeneratorInterface { private Invoice $invoice; public function __construct(Invoice $invoice) { $this->invoice = $invoice; } /** * {@inheritDoc} */ public function generate( PartyDto $sellerDto, PartyDto $buyerDto, array $invoiceItemDtos, string $currencyCode, string $currencySymbol, string $fileName ): void { $buyer = new Party([ 'name' => $buyerDto->name(), 'phone' => $buyerDto->phone(), ]); $seller = new Party([ 'name' => $sellerDto->name(), 'phone' => $sellerDto->phone(), ]); $invoice = $this->invoice ->buyer($buyer) ->seller($seller); foreach ($invoiceItemDtos as $invoiceItemDto) { try { $item = (new InvoiceItem()) ->title($invoiceItemDto->title()) ->pricePerUnit($invoiceItemDto->pricePerUnit()) ->quantity($invoiceItemDto->quantity()) ->discount($invoiceItemDto->discount()); } catch (Exception $e) { throw new RuntimeException($e->getMessage()); } $invoice->addItem($item); } try { $invoice->currencyCode($currencyCode) ->currencySymbol($currencySymbol) ->filename($fileName) ->save('public'); } catch (Exception $e) { throw new RuntimeException($e->getMessage()); } } }
The resulting code is in this link.
7. Ip geolocation
You can use geolocation by an IP address for a few reasons:
- detecting a user’s language
- showing a user if your service works in their country

There are lots of services providing IP geolocation services.
That’s why we need to use the Adapter pattern for them.
It allows to replace such services with new ones easily.
This is how it could work:
public function ip(IpGeolocationInterface $ipGeolocation) { $geoData = $ipGeolocation->geoDataFromIp('1.32.239.255'); // some other code that uses $geoData }
Here is the IpGeolocationInterface
used above:
<?php namespace App\Adapters\IpGeolocation; interface IpGeolocationInterface { public function geoDataFromIp(string $ip): ?GeoDataDto; }
The ipstack.com API was selected as a provider of IP geolocation services.
This is the IpStackInterface
that should be adapted to IpGeolocationInterface
from above:
<?php namespace App\Adapters\IpGeolocation\IpStack; interface IpStackInterface { public function getUserDataByIp(string $ip): ?array; }
Here is the IpStackAdapter
that adapts IpStackInterface
to IpGeolocationInterface
:
<?php namespace App\Adapters\IpGeolocation; use App\Adapters\IpGeolocation\IpStack\IpStackInterface; class IpStackAdapter implements IpGeolocationInterface { private IpStackInterface $ipStack; public function __construct(IpStackInterface $ipStack) { $this->ipStack = $ipStack; } public function geoDataFromIp(string $ip): ?GeoDataDto { $userData = $this->ipStack->getUserDataByIp($ip); if($userData === null) { return null; } return new GeoDataDto( $userData['continent_name'], $userData['country_name'], $userData['city'], $userData['latitude'], $userData['longitude'], ); } }
The full code is here.
8. Converting markdown
Markdown is often used in blogging platforms or content management systems.
You can alllow users to write their posts as markdown.
When you show their posts to readers, you need to convert markdown to HTML.

Again we have a problem:
There are lots of libraries and services for that purpose.
Replacing a library converting markdown to HTML may break our code.
To prevent that let’s define a MarkdownParserInterface
and later create an adapter for that:
<?php namespace App\Adapters\Markdown; interface MarkdownParserInterface { public function toHtml(string $markdown, bool $safeMode): string; }
For converting markdown to HTML we select erusev/parsedown package.
This package does not provide an interface so we’ll adapt its Parsedown
class to MarkdownParserInterface
:
<?php namespace App\Adapters\Markdown; use Parsedown; class ParsedownAdapter implements MarkdownParserInterface { private Parsedown $parsedown; public function __construct(Parsedown $parsedown) { $this->parsedown = $parsedown; } public function toHtml(string $markdown, bool $safeMode): string { $this->parsedown->setSafeMode($safeMode); return $this->parsedown->text($markdown); } }
The source code is here.
9. Getting a proxy IP
You may need a proxy IP if you parse websites to get some data from them.
We have a problem here again: there are lots of services for obtaining a proxy IP.

And the same as in the examples above using a concrete class instead of an interface causes trouble for our code.
We solve the problem by defining the ProxyIpInterface
other classes will depend on:
<?php namespace App\Adapters\ProxyIp; interface ProxyIpInterface { /** * Get a random proxy IP * * @return string */ public function random(): string; }
Though there are services that provide proxy IPs, this example stands out:
We’ll not use any third-party API.
Instead, we’ll use just an array of proxies we obtained somewhere.

This is how the interface for this looks like:
<?php namespace App\Adapters\ProxyIp\ArrayProxyIp; interface ArrayProxyIpInterface { /** * @return string[] */ public function all(): array; }
Its implementation does a very simple thing:
It returns an array of proxy IPs:
<?php namespace App\Adapters\ProxyIp\ArrayProxyIp; class ArrayProxyIpService implements ArrayProxyIpInterface { private array $proxyIpList; /** * ArrayProxyIpService constructor. * @param string[] $proxyIpList */ public function __construct(array $proxyIpList) { $this->proxyIpList = $proxyIpList; } public function all(): array { return $this->proxyIpList; } }
But the random()
method of ProxyIpInterface
is supposed to return one random proxy IP.
That’s why we need to adapt these two interfaces:
<?php namespace App\Adapters\ProxyIp; use App\Adapters\ProxyIp\ArrayProxyIp\ArrayProxyIpInterface; class ArrayProxyIpAdapter implements ProxyIpInterface { private ArrayProxyIpInterface $arrayProxyIp; public function __construct(ArrayProxyIpInterface $arrayProxyIp) { $this->arrayProxyIp = $arrayProxyIp; } public function random(): string { $allProxyIps = $this->arrayProxyIp->all(); $randomProxyIpKey = array_rand($allProxyIps); return $allProxyIps[$randomProxyIpKey]; } }
ArrayProxyIpAdapter
first gets an array of proxies by calling this:
allProxyIps = $this->arrayProxyIp->all();
After that it selects a random proxy IP from that array.
The full source code is here.
10. Thumbnail creation
Creating thumbnails from uploaded images is another case where the Adapter pattern does well.

The reason for this is still the same:
There are lots of things that can generate thumbnails: libraries, third-party APIs, your own implementations.
Some day you may decide to replace your existing service that makes thumbnails.
The Adapter pattern helps you with replacing that without breaking other code.
This is how it could work:
public function makeThumbnail(ThumbnailMakerInterface $thumbnailMakerAdapter) { $file = new UploadedFile( public_path('montblanc.jpg'), 'montblanc.jpg' ); $thumbnailUrl = $thumbnailMakerAdapter->make($file); // other code here using $thumbnailUrl }
We pass ThumbnailMakerInterface
into makeThumbnail()
and generate a thumbnail by doing the following:
$thumbnailUrl = $thumbnailMakerAdapter->make($file);Now
The ThumbnailMakerInterface
from the code above looks like this:
<?php namespace App\Adapters\Thumbnail; use Illuminate\Http\UploadedFile; interface ThumbnailMakerInterface { /** * @param UploadedFile $uploadedFile * @return string a URL with a thumbnail */ public function make(UploadedFile $uploadedFile): string; }
Now it’s time to decide what service we are going to use for producing thumbnails.
Let’s pick the cloudinary.com service.

It offers a free plan and SDK for Laravel and PHP.
Here is CloudinaryApiAdapter
:
<?php namespace App\Adapters\Thumbnail; use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary; use Illuminate\Http\UploadedFile; class CloudinaryApiAdapter implements ThumbnailMakerInterface { public function make(UploadedFile $uploadedFile): string { $thumbnailUrl = Cloudinary::upload($uploadedFile->getRealPath())->getSecurePath(); return $this->insertResizingParameter($thumbnailUrl); } private function insertResizingParameter($thumbnailUrl): string { return str_replace('/image/upload/', '/image/upload/w_150,c_scale/', $thumbnailUrl); } }
The source code is here.
11. URLs shortening
There are many services for shortening URLs.
Still we have the same problems connected with that:
- you don’t want to stick with one of them
- all these services have different APIs (an object interface in terms of code)

For that reason we create UrlShortenerInterface
that is not supposed to change.
All services in our code will depend on that.
This is the UrlShortenerInterface
:
<?php namespace App\Adapters\UrlShortener; use RuntimeException; interface UrlShortenerInterface { /** * @param string $url * @return string * * @throws RuntimeException */ public function makeShortUrl(string $url): string; }
We pick bitly.com as that service for shortening URLs.
Let’s use phplicengine/bitly library for making requests to bitly.com.
Now we create BitlyApiAdapter
:
<?php namespace App\Adapters\UrlShortener; use PHPLicengine\Api\Api; use PHPLicengine\Service\Bitlink; use RuntimeException; class BitlyApiAdapter implements UrlShortenerInterface { private Bitlink $bitlink; private Api $api; public function __construct( Api $api, Bitlink $bitlink ) { $this->bitlink = $bitlink; $this->api = $api; } public function makeShortUrl(string $url): string { $result = $this->bitlink->createBitlink(['long_url' => $url]); if ( $this->api->isCurlError() || $result->isError() || $result->isSuccess() === false ) { throw new RuntimeException('Bitly error occurred'); } return $result->getResponseArray()['link']; } }
The interesting thing here is:
The makeShortUrl()
method is supposed to throw RuntimeException
.
But phplicengine/bitly does not throw that exception.
In the lines 27-33 we check if there is some error and if there we throw RuntimeException
.
The full code is in this link.
12. Fetching weather data
You may need to fetch weather data from a third-party API in the following cases:
- showing the current weather in your news website
- creating a weather provider app
It could work like this:
public function weather(WeatherProviderInterface $weatherProviderAdapter) { $weatherDto = $weatherProviderAdapter->currentWeather(53.893009,27.567444); // process the obtained $weatherDto in some way here }
The WeatherProviderInterface
has the following code:
<?php namespace App\Adapters\Weather; use InvalidArgumentException; interface WeatherProviderInterface { /** * @param float $lat * @param float $lon * @return WeatherDataDto|null * * @throws InvalidArgumentException */ public function currentWeather(float $lat, float $lon): ?WeatherDataDto; }
To implement this interface, let’s pick a weather provider API.
The openweathermap.org suits well.

Here is the OpenweathermapAdapter
where we adapt the openweathermap.org API to our WeatherProviderInterface
:
<?php namespace App\Adapters\Weather; use GuzzleHttp\ClientInterface; use InvalidArgumentException; class OpenweathermapAdapter implements WeatherProviderInterface { private ClientInterface $client; private string $apiKey; public function __construct( ClientInterface $client, string $apiKey ) { $this->client = $client; $this->apiKey = $apiKey; } public function currentWeather(float $lat, float $lon): ?WeatherDataDto { $response = $this->client->request( 'GET', "https://api.openweathermap.org/data/2.5/weather?units=metric&lat={$lat}&lon={$lon}&appid={$this->apiKey}", ['http_errors' => false] ); if ($response->getStatusCode() === 400) { throw new InvalidArgumentException('Lat or lng are wrong'); } if ($response->getStatusCode() !== 200) { return null; } $data = json_decode($response->getBody()->getContents()); return new WeatherDataDto( $data->weather[0]->description, $data->main->temp, $data->main->pressure, $data->main->humidity, ); } }
The full code you can find here.
Summary
I hope this article is useful for you and you learnt something new from that.
I would like to hear your opinion on these examples of the Adapter pattern.
Please write any questions in the comments.
Also, it would be great to learn when you used the Adapter pattern in your projects over your programmer’s career.
There is also a video version for this article in my youtube channel: