gnahotelsolutions/laravel-weather
Laravel Weather offers a simple, configurable way to fetch current conditions and forecasts in Laravel apps. Integrate weather data with a clean API, caching support, and flexible settings for your preferred weather service. Ideal for dashboards, travel sites, and location-aware features.
This is a thin, single-purpose wrapper around OpenWeatherMap's current weather endpoint (/data/2.5/weather). It is a small package (38★), so there is no dashboard, no DTO layer, and no polling — just a config file, a facade, and an array response.
Install and wire it up in three steps:
composer require gnahotelsolutions/laravel-weather
php artisan vendor:publish --tag=weather-config # publishes config/weather.php
Then add the key from openweathermap.org to .env:
OPEN_WEATHER_MAP_API_KEY=your_key_here
Where to look first:
config/weather.php — this is the whole contract: the endpoint URL, the api_key, and the query defaults (units, lang). If something is wrong, it is almost always here.Gnahotelsolutions\LaravelWeather\Facades\Weather — this is the entire public surface. You will not find much else.First use case — show current weather in a controller:
use Gnahotelsolutions\LaravelWeather\Facades\Weather;
Route::get('/weather/{city}', function (string $city) {
$weather = Weather::get($city); // returns the decoded JSON array
return view('weather', [
'city' => $weather['name'],
'temperature' => $weather['main']['temp'],
'description' => $weather['weather'][0]['description'],
]);
});
That is the entire happy path. The package does one thing, so if the task grows beyond "current weather in a city," you will outgrow it quickly.
Note: confirm the exact method name (
get,find, etc.) against the repo's README for the version you install — the API is small and the package has not changed since 2021.
Do not scatter Weather::get(...) across controllers. Because the response is a raw array and the package is unmaintained, isolate it so you can swap in a maintained client (or Laravel's Http facade directly) later without touching callers.
namespace App\Services;
use Gnahotelsolutions\LaravelWeather\Facades\Weather;
class CurrentWeather
{
public function forCity(string $city): array
{
return Weather::get($city);
}
}
OpenWeatherMap's free tier is rate-limited, and current weather changes slowly. Cache by city (or by coordinates) for 5–15 minutes:
use Illuminate\Support\Facades\Cache;
$weather = Cache::remember("weather:{$city}", now()->addMinutes(10), function () use ($city) {
return Weather::get($city);
});
Key on the inputs (city + units + lang), not on the current time, or you will get a cache miss every minute.
Http facade for non-trivial needsIf you need hourly/daily forecasts, multiple cities in one call, geocoding, or error-code handling, skip the wrapper and call the API directly — it will be less code than fighting the package's limited surface:
use Illuminate\Support\Facades\Http;
$response = Http::get('https://api.openweathermap.org/data/2.5/weather', [
'q' => $city,
'appid' => config('weather.api_key'),
'units' => 'metric',
])->throw()->json();
This is the pattern most teams eventually migrate to. The wrapper is a reasonable starting point for a single widget; the Http facade is the sustainable pattern.
If you show weather on a dashboard, do not hit the API during the request. Fetch on a schedule and expose it from cache or the DB:
// app/Console/Kernel.php
$schedule->call(function () {
foreach (config('weather.cities', []) as $city) {
Cache::put("weather:{$city}", Weather::get($city), now()->addMinutes(15));
}
})->everyTenMinutes();
The OpenWeatherMap endpoint accepts q (city name), id (city id), lat/lon, and zip. Whatever the package exposes, map your domain inputs to those parameters and centralise the mapping — city-name lookups are ambiguous ("Springfield") and you will want to change strategy later.
Because the package ultimately makes an HTTP call, mock it at the HTTP layer with Http::fake() (if it uses the Laravel HTTP client) or a Guzzle handler stack (if it uses Guzzle directly — inspect the source). Then assert against a fixture array so tests do not depend on the network:
Http::fake([
'api.openweathermap.org/*' => Http::response(['name' => 'Milan', 'main' => ['temp' => 21.5]]),
]);
If the package bypasses Http, wrap it behind your service and mock your service in feature tests; keep one integration test that hits a recorded fixture.
The package is unmaintained (last release Sept 2021). Treat it as a vendored snippet, not infrastructure. Pin the exact version in composer.json, and assume you will fork or replace it. Do not let it get pulled anywhere you need guaranteed compatibility with a future Laravel/PHP release.
401 Unauthorized right after signup. New OpenWeatherMap API keys can take minutes to hours to activate. A brand-new key returning 401 is almost always activation, not your config.
Failures are not your domain errors. The wrapper does not give you typed exceptions — a bad city, an inactive key, and a network timeout may all surface differently. Wrap calls in try/catch and map the API's cod/message to your own response codes:
try {
$weather = Weather::get($city);
} catch (\Throwable $e) {
report($e);
abort(502, 'Weather unavailable');
}
City names are ambiguous. London resolves fine; Springfield, Paris (Texas vs France), and any name with diacritics can resolve to the wrong place. If accuracy matters, use the OpenWeatherMap geocoding API to get a city id, then query by id — the wrapper's name lookup is fine for demos and unreliable for production.
Units and language are config/query defaults. temperature is Kelvin unless you set units=metric (Celsius) or imperial (Fahrenheit). Displaying 293.15 to a user is the classic first bug. Same for description — it defaults to English and needs lang for localization.
The response is an untyped array. There is no model or DTO, so $weather['main']['temp'] and friends can be missing (e.g., an error payload that did not throw) and you get a PHP warning. Guard with data_get() or the null-coalescing operator in views.
Cache keys must include units and lang. Otherwise a metric request can be served to a Fahrenheit page and vice versa.
Respect the free-tier quota. 60 calls/min and ~1M/month sounds generous, but a naive per-request call on a popular page will blow through it. Cache, batch in a scheduled job, and consider putting a short circuit breaker in front of the API.
Timezone trap. The dt field is UTC and the response's timezone is an offset in seconds, not a name. Do not Carbon::parse($weather['dt']) and assume local time; convert explicitly with the offset.
Extension point is limited. There is essentially one place to customise: config/weather.php. If you need retries, logging, custom exceptions, or a second endpoint, add that logic in your own service around the facade — not by editing the vendor package.
Migrate when it hurts. The moment you need forecasts, history, air quality, or reliable error handling, you have outgrown this package. A ~20-line Http-based client with a Cache::remember wrapper usually replaces it with less code and fewer surprises.
How can I help you explore Laravel packages today?