Install via Composer
composer require dwr/openweather-bundle
Enable the Bundle
Add to config/bundles.php:
Dwr\OpenWeatherBundle\DwrOpenWeatherBundle::class => ['all' => true],
Configure API Key
Add to config/packages/dwr_openweather.yaml:
dwr_openweather:
api_key: '%env(OPENWEATHER_API_KEY)%'
Ensure OPENWEATHER_API_KEY is set in your .env file.
First Use Case: Fetch Current Weather
Inject the Dwr\OpenWeatherBundle\Service\OpenWeatherService into a controller or service:
use Dwr\OpenWeatherBundle\Service\OpenWeatherService;
class WeatherController extends AbstractController
{
public function __construct(private OpenWeatherService $weatherService) {}
public function showCurrentWeather(string $city): Response
{
$weather = $this->weatherService->getCurrentWeather($city);
return $this->json($weather);
}
}
Call via route:
# config/routes.yaml
weather_current:
path: /weather/current/{city}
controller: App\Controller\WeatherController::showCurrentWeather
Service Integration
OpenWeatherService in controllers, command handlers, or other services.use Dwr\OpenWeatherBundle\Service\OpenWeatherService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class WeatherCommand extends Command
{
protected static $defaultName = 'app:weather';
public function __construct(private OpenWeatherService $weatherService) {}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$weather = $this->weatherService->getCurrentWeather('Paris');
$output->writeln(sprintf('Weather in Paris: %s', $weather['weather'][0]['description']));
return Command::SUCCESS;
}
}
Caching Responses
OpenWeatherService to cache API responses (e.g., using Symfony’s CacheInterface):
use Psr\Cache\CacheItemPoolInterface;
use Dwr\OpenWeatherBundle\Service\OpenWeatherService;
class CachedOpenWeatherService
{
public function __construct(
private OpenWeatherService $decorated,
private CacheItemPoolInterface $cache
) {}
public function getCurrentWeather(string $city): array
{
$cacheKey = 'weather_' . md5($city);
$item = $this->cache->getItem($cacheKey);
if (!$item->isMiss()) {
return $item->get();
}
$weather = $this->decorated->getCurrentWeather($city);
$item->set($weather)->expiresAfter(3600); // Cache for 1 hour
$this->cache->save($item);
return $weather;
}
}
Unit Testing
OpenWeatherService to test logic without hitting the API:
use Dwr\OpenWeatherBundle\Service\OpenWeatherService;
use PHPUnit\Framework\TestCase;
class WeatherServiceTest extends TestCase
{
public function testGetCurrentWeather()
{
$mockService = $this->createMock(OpenWeatherService::class);
$mockService->method('getCurrentWeather')
->with('London')
->willReturn(['weather' => ['description' => 'cloudy']]);
$this->assertEquals(
['weather' => ['description' => 'cloudy']],
$mockService->getCurrentWeather('London')
);
}
}
Twig Integration
{# templates/weather/index.html.twig #}
<h1>Weather in {{ city }}</h1>
<p>Conditions: {{ weather.weather[0].description }}</p>
<p>Temperature: {{ (weather.main.temp - 273.15)|round(1) }}°C</p>
public function showWeather(string $city): Response
{
$weather = $this->weatherService->getCurrentWeather($city);
return $this->render('weather/index.html.twig', [
'city' => $city,
'weather' => $weather,
]);
}
API Key Management
config/packages/dwr_openweather.yaml violates security best practices.%env(OPENWEATHER_API_KEY)%) and restrict .env to local development.Rate Limiting
429 Too Many Requests.5-minute forecast API instead of current for less frequent updates if possible.Error Handling
404 Not Found or 401 Unauthorized) as exceptions.use Dwr\OpenWeatherBundle\Exception\OpenWeatherException;
try {
$weather = $this->weatherService->getCurrentWeather($city);
} catch (OpenWeatherException $e) {
if ($e->getCode() === 404) {
throw $this->createNotFoundException('City not found');
}
throw new \RuntimeException('Weather service unavailable', 0, $e);
}
Geocoding Dependencies
getWeatherByCoordinates) require valid latitude/longitude. Invalid inputs return 400 Bad Request.private function isValidCoordinate(float $coord): bool
{
return $coord >= -90 && $coord <= 90; // Latitude
// OR $coord >= -180 && $coord <= 180; // Longitude
}
Unit vs. Imperial Units
$weather['main']['temp_celsius'] = $weather['main']['temp'] - 273.15;
$weather['main']['temp_fahrenheit'] = ($weather['main']['temp'] - 273.15) * 9/5 + 32;
Custom API Endpoints
use Dwr\OpenWeatherBundle\Service\OpenWeatherService;
class ExtendedOpenWeatherService extends OpenWeatherService
{
public function getAirPollution(float $lat, float $lon): array
{
return $this->callApi('air_pollution', ['lat' => $lat, 'lon' => $lon]);
}
}
services.yaml:
services:
App\Service\ExtendedOpenWeatherService:
decorates: 'dwr_openweather.service.open_weather'
arguments:
$decorated: '@.inner'
Logging API Calls
use Psr\Log\LoggerInterface;
use Dwr\OpenWeatherBundle\Service\OpenWeatherService;
class LoggingOpenWeatherService
{
public function __construct(
private OpenWeatherService $decorated,
private LoggerInterface $logger
) {}
public function getCurrentWeather(string $city): array
{
$this->logger->info('Fetching weather for city', ['city' => $city]);
$weather = $this->decorated->getCurrentWeather($city);
$this->logger->debug('Weather response', ['response' => $weather]);
return $weather
How can I help you explore Laravel packages today?