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

Swissstreets For Filament Laravel Package

blemli/swissstreets-for-filament

Filament integration for the SwissStreets package, adding Swiss street and address data to your admin panel forms and tables. Search, select, and autocomplete official Swiss street names directly inside Filament resources.

View on GitHub
Deep Wiki
Context7

Getting Started

v0.8.0 is a pre-1.0 release, so expect minor-version breaking changes (this one removed a positional argument). Pin the version in composer.json ("vendor/package": "0.8.*") until 1.0 lands.

Minimal setup:

composer require vendor/laravel-address   # see package README for exact name
php artisan vendor:publish --tag="address-config"
php artisan vendor:publish --tag="address-migrations"
php artisan migrate

Where to look first:

  1. The Address facade / service class — cascade() is the entry point for form fields, and most of the package's behaviour hangs off the object it returns (AddressCascade).
  2. The published config — API endpoint, credentials, cache TTL, and locale/region settings all live there.
  3. The cascade options (nearMe(), nonresidential(), freetext()) — these read as small methods but each one changes both the UI flow and what gets written to storage, so skim all three before designing your form.

First use case — a checkout/signup address field:

use Vendor\Address\Facades\Address;

// In your form schema / Livewire component / Blade partial
public function fields(): array
{
    return [
        Address::cascade('address_id')
            ->nearMe()          // nearest towns first, on open and before typing
            ->label('Address'),
    ];
}

The field resolves to a single stored value (address_id) pointing at a register row, not to loose street/city/postcode columns. That is the key mental model: you persist an ID and read the address back through the model/relation.

The first realistic use case to build is a standard residential Dutch address: postcode + house number → street → town. Once that works end-to-end (form → address_id → rendering the address back), layer on nearMe() for UX and nonresidential() / freetext() for the messy tail of real addresses.

Implementation Patterns

Cascade as a single field, three selects underneath. Address::cascade('address_id') renders the town → street → house number chain but binds to one attribute. Keep your column as a nullable foreign key / integer and treat the cascade as an opaque widget:

Schema::table('orders', function (Blueprint $table) {
    $table->unsignedBigInteger('address_id')->nullable()->index();
});
protected $casts = ['address_id' => 'integer'];

public function address()
{
    return $this->belongsTo(AddressModel::class, 'address_id');
}

Chain the options rather than branching in your own code. All three modifiers return the same AddressCascade, so compose per context:

// Consumer checkout: closest first
Address::cascade('address_id')->nearMe();

// B2B / warehouse form: no house-number requirement
Address::cascade('address_id')->nonresidential();

// Anything-goes form (marketplace sellers, foreign customers)
Address::cascade('address_id')->freetext();

Because each returns the same type, you can factor a helper — e.g. a AddressFieldFactory that returns a differently configured cascade based on account type or shipping region — instead of duplicating form config in every component.

freetext() is a data-modelling decision, not just a UI toggle. It puts an add-address form on every step, and unlisted house numbers, streets, or foreign towns become manual register rows selected in all three selects. Plan for that:

  • Your register table now holds both imported and hand-entered rows — add a source / is_manual flag if you need to distinguish them for reporting or later reconciliation with an official dataset.
  • Manual rows are user-supplied text. Normalise and trim before insert, and expect near-duplicates ("Kerkstraat" vs "Kerkstraat "), so build a periodic dedupe/review job if volume justifies it.
  • Foreign towns have no postcode/street data behind them — make sure downstream logic (shipping-rate calculators, tax rules, label printers) tolerates partial or non-validated addresses rather than assuming a complete official record.

nearMe() needs a location source. It orders towns nearest-first, including on open before the user types anything. That means a geolocation signal has to be available at render time — IP lookup, a stored customer coordinate, or a browser-provided position. Resolve it server-side and cache it per session; do not re-geolocate on every keystroke. Fall back gracefully (no location → default alphabetical/near() ordering) so the field never blocks.

Validation and persistence. Validate address_id with exists: against the register table plus a nullable/required rule matching your UX. Validate before trusting the address for anything financial (shipping tier, VAT), and re-read the register row server-side rather than trusting any address text posted along with the ID.

Livewire / Filament / Inertia integration. The cascade is stateful (search-as-you-type across three dependent selects), so mount it in a component that can persist intermediate state. For Livewire, keep the cascade in a dedicated child component with wire:model on the single address_id; for Filament, wrap it in a custom field that returns the same underlying object. Server round-trips per keystroke are the failure mode here — debounce, and cache town/street lookups in the package's configured cache store.

Testing. Fake the upstream lookup in tests rather than hitting the live API; assert on the resolved address_id and on the number of selects rendered for each modifier, and add one test per modifier (nearMe, nonresidential, freetext) since each changes behaviour. Include a test that a manual row created via freetext() is selectable from all three selects afterwards — that is the behaviour most likely to regress.

Gotchas and Tips

Breaking change in v0.8.0: the second positional argument of cascade() is gone. Any call like Address::cascade('address_id', true) (or an int/enum flag) silently loses the intended behaviour or errors depending on strictness. Grep for every cascade( call and migrate to the fluent form:

// before
Address::cascade('address_id', true);
// after
Address::cascade('address_id')->nonresidential();

This is a mechanical but easy-to-miss change — calls buried in Blade partials or config arrays are the usual stragglers. Do the sweep in the same PR as the version bump; don't ship half-migrated forms.

Pre-1.0 churn. Two minor releases in a row have changed the public API surface (positional args, per-feature behaviour). Pin exact versions, read the changelog before every bump, and keep your own thin wrapper (AddressFieldFactory) around cascade() so a future rename touches one file instead of forty.

Silent behaviour mixing. nearMe() + freetext() means manually entered towns also participate in nearest-first ordering — and a hand-entered town has no coordinates unless you supply them. Verify what nearMe() does with manual rows; if they sort unpredictably (nulls first, or ignored), decide deliberately whether manual towns should be appended at the end rather than trusting the default.

nonresidential() removes the house-number step. Downstream code that assumes a house number exists (label formatting, "street + number" concatenation, duplicate detection) will produce nulls or throw. Audit any string-building helper that consumes the address before enabling this on a form.

Cache invalidation for manual rows. Manually created register rows need to be visible immediately in all three selects. If the package caches town/street lists, either bust the relevant cache key on insert or configure a short TTL for the "manual" lookup — otherwise users create an address and cannot select it, which looks like data loss.

Debugging order for "nothing shows up". (1) Are API credentials set and is the config cached (php artisan config:clear after editing .env)? (2) Does the base cascade work without modifiers — strip nearMe()/freetext() and retest? (3) Is the failure order-dependent (towns load, streets empty → usually a missing/mismatched parent ID)? (4) Is it a JS-side issue — the dependent selects not re-rendering — in which case the network tab, not the PHP logs, tells the story.

Extension points worth knowing. The AddressCascade object is the natural place to add project-specific defaults (label, required, help text, initial town). If you need behaviour the three modifiers don't cover (e.g. restrict to a service region), wrap rather than fork: build a decorator that configures the cascade and applies your own validation rule alongside it, keeping package upgrades cheap.

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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle