helgesverre/telefonkatalog
A Laravel package for searching Norwegian phone directory data via Telefonkatalogen. Look up people and businesses by name, phone number, or address and get structured results, making it easy to add contact and phone-number lookup to your app.
helgesverre/telefonkatalog is a Laravel wrapper for Norwegian phone directory lookups, aggregating scrapers (Gulesider, Opplysningen1890, 1881, etc.) behind a single API. The v1.3.0 release is the version you want on Laravel 11/12 — it's the first to support Laravel 12.
Setup:
composer require helgesverre/telefonkatalog
There's nothing to publish to get started; the package resolves through Laravel's container and (where present) exposes a facade. If your app is on Laravel 10, you must stay on v1.2.x — v1.3.0 drops Laravel 10 support entirely.
Where to look first:
src/ for the facade/entry class and the Scrapers/ directory — that's where provider-specific behaviour lives.README for the search method signature (phone number vs. name/query lookups).SanityTest.php) for real invocation examples — the Pest v3 sanity tests double as usage documentation.First use case: look up a phone number and get back a structured result (name, address, phone). Start in a tinker session or a throwaway route to confirm the scraper is returning data in your environment before wiring it into anything user-facing.
Single-provider lookup. Ask for the provider explicitly when you know which directory has the freshest data for your region. This makes failures attributable — if Gulesider's markup changes you know exactly which code path broke, rather than debugging an opaque aggregate result.
Fan-out / fallback chain. For a "best effort" lookup, try providers in order and return the first hit. Wrap each provider call defensively — as of v1.3.0 the Opplysningen1890 scraper handles results missing phone numbers gracefully, but earlier behaviour (and other providers) will vary. Treat "found a person but no phone" as a distinct outcome from "not found at all," not as a null.
Normalise before you persist. Scraper output shape is dictated by the source site, not by this package. Map results into your own DTO before storing them, so the next scraper fix (like the Gulesider __NEXT_DATA__ → JSON-LD migration in this release) doesn't ripple into your database schema.
Cache aggressively. These are live HTTP scrapes against third-party sites. Cache by normalised query key with a TTL measured in days, and use Cache::remember around the service call — not inside it — so you can bypass caching in tests.
Queue the lookups. Any user-triggered lookup should go through a job. It bounds latency for your users, gives you automatic retries, and keeps a burst of lookups from hammering a third-party site and getting you blocked.
Testing. Never hit the real sites in your test suite. Fake the underlying HTTP client (or bind a stub implementation of the scraper interface) and assert on your normalised DTO. The package's own edge-case tests for empty/non-existent search results (added in v1.3.0) are a good template — cover the empty-result and missing-phone branches in your integration too, since those are the paths that throw.
Scrapers break, and that's the main maintenance cost. v1.3.0 shipped a fix because Gulesider changed its page structure — the package moved from parsing __NEXT_DATA__ to JSON-LD. Budget for this: pin the version, watch the changelog, and have a test that fails loudly when a provider starts returning empty results for a known-good query.
Laravel 10 is gone. If you're mid-upgrade, composer require helgesverre/telefonkatalog:^1.2 and plan the framework bump. Don't try to force v1.3.0 onto Laravel 10 — Testbench ^9.0|^10.0 and the Pest v3 upgrade make the dev dependencies unresolvable.
Pest v3 removal of collision and pest-plugin-arch. If your project shares a composer constraint set with this package, don't re-add those — Pest 3 includes both. Conflicts here usually surface as dev-dependency resolution errors rather than runtime failures, but they'll still block composer update.
Rate limiting is your responsibility. The package doesn't manage politeness toward upstream sites. Add throttling (Laravel's RateLimiter or a queued job with ->delay()) and set a realistic User-Agent. Getting IP-blocked by a directory service is a silent, hard-to-debug failure mode — lookups just start returning empty.
Empty vs. error. v1.3.0's edge-case tests formalise this, but treat it as a contract: "no results" should return an empty collection/null, not throw. Catch exceptions from transport failures and distinguish them in your logging from genuine no-match results — otherwise you'll chase phantom bugs.
justfile is for contributors, not you. just test / just format / just analyse are conveniences for working on the package. Only relevant if you're forking to patch a broken scraper — which, given the scraping model, is a realistic scenario. Forking plus a version constraint pointing at your VCS repo is a legitimate escape hatch when upstream lags behind a site change.
Extension point. The Scrapers/ namespace is the seam to watch. If you need a provider the package doesn't cover, implementing a scraper against whatever interface the existing ones share is cleaner than shelling out to your own HTTP calls elsewhere in the app — you keep the aggregation and normalisation behaviour consistent.
How can I help you explore Laravel packages today?