Skip to content

feat(address): standardized address creation#5

Merged
jure-knezovic merged 4 commits into
mainfrom
feat/standardized-address-creation
Jun 19, 2026
Merged

feat(address): standardized address creation#5
jure-knezovic merged 4 commits into
mainfrom
feat/standardized-address-creation

Conversation

@jure-knezovic

@jure-knezovic jure-knezovic commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a standardized, type-safe, swappable way to create an address for a model. The package provides the mechanism and enforces no policy — no dedupe, no per-type uniqueness, no default/primary. An app imposes its own rules by rebinding the contract.

$user->addAddress(new AddressData(street: 'Pariser Platz 1', city: 'Berlin', country: 'de'), AddressType::Billing);
$user->addAddress(['city' => 'Berlin'], AddressType::Shipping);     // loose array accepted
$user->addAddress($placeDetails->toAddressData(AddressType::Home)); // bridge from a lookup result

// Customise creation globally with one bind:
$this->app->bind(CreatesAddresses::class, MyAddressCreator::class);

What's included

  • AddressData — canonical write DTO (fromArray / withType / toArray), plus PlaceDetails::toAddressData() and PostalAddress::toAddressData() bridges so the existing DTOs converge onto one shape.
  • CreatesAddresses contract + default AddressCreator service (bound singleton) — the swap point.
  • HasAddresses::addAddress(AddressData|array, AddressType|string|null) — the typed entry point.
  • Config::addressesRelation() — single source of truth for the polymorphic morphMany wiring (used by both HasAddresses::addresses() and AddressCreator).
  • AddressType roles are now Billing / Shipping / Home / Work (Primary dropped — it conflated a default flag with a role).

No migration change.

Testing

  • composer test119 passing (506 assertions)
  • composer stan — clean at level 9, zero suppressions

New: tests/Unit/AddressDataTest.php, tests/Feature/AddressCreationTest.php (covers DTO + array paths, country uppercasing, type casting, container rebinding, and enum-disabled mode).

jure-knezovic and others added 2 commits June 19, 2026 11:54
Introduce `PostalAddress` data transfer object to standardize schema.org postal address representation. Added `toArray` and `toJsonLd` methods for rendering address data as fragments or standalone JSON-LD. Updated the `Address` model with a `toSchemaOrg` method to generate `PostalAddress` instances. Includes tests and documentation updates.
Add a typed, swappable way to create an address for a model.

- AddressData: canonical write DTO (fromArray/withType/toArray), plus
  PlaceDetails::toAddressData() and PostalAddress::toAddressData() bridges
- CreatesAddresses contract + default AddressCreator service (bound singleton);
  rebind it to customise creation (dedupe, geocoding, uniqueness, defaults)
- HasAddresses::addAddress(AddressData|array, AddressType|string|null)
- Config::addressesRelation(): single source of truth for the morph wiring
- AddressType roles are now Billing/Shipping/Home/Work (Primary dropped)

The package enforces no cardinality policy (no dedupe/uniqueness/default).
No migration change. Includes the design spec under docs/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a standardized, swappable address-creation flow to the package by introducing a canonical write DTO (AddressData), a container-bound creation contract (CreatesAddresses), and a typed entry point on owner models (HasAddresses::addAddress). It also centralizes the polymorphic relation wiring and extends schema.org support with a PostalAddress DTO plus model mapping.

Changes:

  • Introduces AddressData (write DTO) plus bridges from existing DTOs (PlaceDetails, PostalAddress) to unify creation input.
  • Adds CreatesAddresses + default AddressCreator (container binding) and exposes HasAddresses::addAddress(...) as the standard persistence entry point.
  • Adds schema.org PostalAddress DTO + Address::toSchemaOrg(), updates AddressType roles, and expands docs/tests.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/Unit/PostalAddressTest.php Adds unit coverage for schema.org PostalAddress array/JSON-LD output behavior.
tests/Unit/AddressDataTest.php Adds unit coverage for AddressData construction, normalization, and bridge methods.
tests/Feature/AddressCreationTest.php Adds feature coverage for addAddress() DTO/array paths, type override, enum-disabled mode, and container rebinding.
tests/Feature/AddressableTest.php Extends feature coverage to include Address::toSchemaOrg() mapping.
src/App/Support/Config.php Centralizes polymorphic relation wiring via Config::addressesRelation().
src/App/Services/AddressCreator.php Adds default CreatesAddresses implementation that persists via the configured relation.
src/App/Models/Address.php Adds toSchemaOrg() mapping to PostalAddress DTO (and model docblock properties).
src/App/Enums/AddressType.php Updates built-in enum cases to Billing/Shipping/Home/Work (drops Primary).
src/App/Data/PostalAddress.php Adds schema.org PostalAddress DTO with fragment + JSON-LD rendering and bridge to AddressData.
src/App/Data/PlaceDetails.php Adds toAddressData() bridge to the canonical write DTO.
src/App/Data/AddressData.php Adds canonical write DTO with fromArray(), withType(), and column-ready toArray().
src/App/Contracts/CreatesAddresses.php Adds the contract used as the swap point for address creation.
src/App/Concerns/HasAddresses.php Switches addresses() to use centralized relation and adds addAddress() entry point.
src/AddressableServiceProvider.php Binds CreatesAddresses to the default AddressCreator implementation.
README.md Documents standardized creation (addAddress/AddressData) and schema.org PostalAddress emission.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/Feature/AddressCreationTest.php Outdated
Comment on lines +89 to +93
return new class ($sentinel, $called) implements CreatesAddresses {
public function __construct(
private readonly Address $sentinel,
private bool &$called,
) {}
Comment on lines +7 to +10
case Billing = 'billing';
case Shipping = 'shipping';
case Primary = 'primary';
case Home = 'home';
case Work = 'work';
jure-knezovic and others added 2 commits June 19, 2026 13:35
Replace Config::addressesRelation() (a relation builder living on the config
accessor) with an Addressable contract that the HasAddresses trait satisfies.
CreatesAddresses::create() now takes Model&Addressable $owner and calls
$owner->addresses() directly, so the morphMany wiring lives only in
HasAddresses::addresses(). Models using HasAddresses now declare
`implements Addressable`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address Copilot review feedback:
- README "Address type enum" still listed the dropped `primary` default;
  update to billing/shipping/home/work.
- Replace the by-reference promoted property in the container-rebinding
  test with a holder-object spy (cleaner, avoids the unusual by-ref pattern).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jure-knezovic jure-knezovic merged commit 765e8e7 into main Jun 19, 2026
28 checks passed
@jure-knezovic jure-knezovic deleted the feat/standardized-address-creation branch June 19, 2026 11:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants