Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Lcn Weather Forecast Bundle Laravel Package

locaine/lcn-weather-forecast-bundle

A Laravel bundle for adding weather forecasts to your app. It offers configurable API integration, cached forecast data, and simple Blade helpers, so you can display current conditions and multi-day outlooks without building your own weather service layer.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check before you composer require: this is a Symfony 2 bundle, not a Laravel package. Installing it into a Laravel app will try to pull Symfony 2.x components alongside Laravel's own Symfony 5/6/7 dependencies (HttpFoundation, Console, Cache, EventDispatcher) and Composer will either fail hard or pin your framework back years. Treat it as reference material to port, not a dependency to install.

The three files worth reading first, in order:

  1. README.md — the service ID to fetch from the container and the config keys (API key, units, cache lifetime).
  2. DependencyInjection/Configuration.php — the authoritative list of config options; this is your spec for config/weather.php.
  3. Resources/config/services.xml — shows how the HTTP client, normalizer, and cache are wired. The class behind the client service is the only thing you actually want.

The bundle's real value is the two things in its tagline: API request normalization (turning Dark Sky's nested, unit-inconsistent JSON into a flat predictable structure) and caching (avoiding a billable call per page view). You are going to reimplement both in ~150 lines of Laravel code.

Minimal first use case — 7-day forecast for a fixed location, rendered on a dashboard:

// config/weather.php
return [
    'key' => env('WEATHER_KEY'),
    'ttl' => 1800,
    'units' => 'si',
    'endpoint' => 'https://api.open-meteo.com/v1/forecast',
];
// app/Services/ForecastService.php
public function forCoordinates(float $lat, float $lon): array
{
    $key = sprintf('forecast:%s:%s:%s', round($lat, 3), round($lon, 3), config('weather.units'));

    return Cache::remember($key, config('weather.ttl'), function () use ($lat, $lon) {
        $response = Http::timeout(5)
            ->retry(2, 200)
            ->get(config('weather.endpoint'), [
                'latitude' => $lat,
                'longitude' => $lon,
                'daily' => 'temperature_2m_max,temperature_2m_min,precipitation_sum',
                'timezone' => 'auto',
            ]);

        $response->throw();

        return $this->normalize($response->json());
    });
}

If you genuinely need to run the original bundle (e.g. a legacy Symfony 2 app you're maintaining), scaffold a throwaway Symfony 2.8 project with the matching PHP 5.6/7.0 runtime, register the bundle in AppKernel, and hit the service by ID. Do not try to run it under PHP 8 — the bundle predates return types and strict signatures.

Implementation Patterns

Port the normalizer as a pure class. The one genuinely reusable piece is the JSON→array mapping. Keep it framework-free (no container, no facade) so it's unit-testable and swappable:

final class DarkSkyNormalizer implements ForecastNormalizer
{
    public function normalize(array $raw): Forecast
    {
        return new Forecast(
            current: $raw['currently'] ?? null,
            daily: collect($raw['daily']['data'] ?? [])
                ->map(fn (array $d) => new ForecastDay(
                    date: CarbonImmutable::createFromTimestamp($d['time'], 'UTC'),
                    high: $d['temperatureMax'] ?? null,
                    low: $d['temperatureMin'] ?? null,
                    summary: $d['summary'] ?? '',
                ))
                ->all(),
        );
    }
}

Bind via an interface in a service provider, so the provider is a one-line swap when you change vendors (which you will):

$this->app->singleton(ForecastNormalizer::class, DarkSkyNormalizer::class);
$this->app->bind(ForecastProvider::class, OpenMeteoProvider::class);

Cache the normalized payload, not the raw response. Raw responses are large and vendor-specific; normalized data is small, stable, and survives a provider migration with no cache-busting.

Round coordinates into the cache key (3 decimals ≈ 110 m). Exact floats produce near-unique keys and destroy the hit rate; too coarse and neighbouring cities collide.

Warm the cache out-of-band when the data is on a high-traffic page. A scheduled job for each of your known locations means the request path never waits on an external API:

// app/Console/Kernel.php
$schedule->call(fn () => Location::pluck('id')
    ->each(fn ($id) => ForecastService::forLocation($id)))
    ->everyThirtyMinutes();

Degrade, don't 500. Weather is decorative on most pages. Wrap the call and return the last-known value from a longer-lived cache key, or null, so a provider outage never becomes an application outage:

public function safeForecast(float $lat, float $lon): ?Forecast
{
    try {
        return $this->forCoordinates($lat, $lon);
    } catch (Throwable $e) {
        report($e);
        return Cache::get("forecast:stale:{$this->bucket($lat, $lon)}");
    }
}

Test with Http::fake(). Assert on the normalized output, not on the vendor payload shape; then when the provider changes, only one test needs rewriting.

Http::fake(['*' => Http::response($fixture, 200)]);
$this->assertSame(12.4, app(ForecastService::class)->forCoordinates(55.6, 13.0)->daily[0]->high);

Gotchas and Tips

The API is dead. Forecast.io became Dark Sky, and Dark Sky's public API was retired at the end of 2022. Every request this bundle makes will fail — this is almost certainly why the score is 0.03 and why the package is unmaintained. Do not build on it expecting data. The migration target is a live provider: Open-Meteo (free, no key, generous limits — the best default), OpenWeatherMap, Visual Crossing, or Meteostat for historical data. If you're being asked to "integrate the weather forecast bundle," the real deliverable is a provider-agnostic adapter with the bundle's normalization contract.

Symfony 2 is EOL and PHP-incompatible. Symfony 2.8 reached end of life in 2018 and the bundle's composer.json will likely declare php: >=5.3 plus Symfony 2 constraints. Modern Laravel requires Symfony 6/7 components and PHP 8.1+. There is no compatibility shim; the only options are extraction or isolation.

Cache stampede on cold keys. Cache::remember doesn't lock. On a popular location with an expired key, a burst of requests all call the provider simultaneously. Use Cache::lock() around the refresh, or Cache::flexible() on Laravel 11+ for stale-while-revalidate semantics.

Units and timezone are part of your cache key. si vs us and auto vs fixed timezone produce different payloads. If they're not in the key, you will serve Celsius to US users and vice versa — a bug that only shows up as a support ticket. Store the raw units alongside the data if you can't enumerate them in the key.

Store timestamps as UTC, convert at the edges. Dark Sky returns Unix timestamps in UTC with a separate timezone field. Doing the conversion inside the normalizer means the rendering layer never has to think about DST.

Watch the rate-limit headers. Free tiers typically cap calls per minute and per day. A scheduled warm-up plus a sane TTL is the whole mitigation; the bundle's config may let you set a TTL of 0, which will silently hammer the provider until you're banned.

Extension points, if you do extract the bundle: the HTTP client (swap Guzzle for Laravel's Http), the normalizer (implement its interface for a new vendor), and the cache adapter (drop Symfony's CacheInterface, use Laravel's Cache). Those three seams are where all the coupling lives — extract them cleanly and the rest of the bundle has nothing you need.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource