laravel-addresses/docs/has-address-assignments.md

6.8 KiB

HasAddressAssignments Trait

Add the HasAddressAssignments trait to any Eloquent model that references addresses owned by other models.

use Blax\Addresses\Traits\HasAddressAssignments;

class Job extends Model
{
    use HasAddressAssignments;
}

class Order extends Model
{
    use HasAddressAssignments;
}

When to use this

Use HasAddressAssignments when a model needs to reference an address that it does not own. Instead of duplicating address data, the model creates an assignment that points to an existing AddressLink.

Example: A transport job needs a pickup address and a delivery address. Those addresses belong to customers. The job assigns the customers' address links to itself with context-specific roles.

// Customer owns the address
$pickupLink   = $customer->addAddress(['city' => 'Vienna'], AddressLinkType::Office);
$deliveryLink = $recipient->addAddress(['city' => 'Berlin'], AddressLinkType::Home);

// Job references them
$job->assignAddressLink($pickupLink, 'pickup');
$job->assignAddressLink($deliveryLink, 'delivery');

A model can use both HasAddresses and HasAddressAssignments if it both owns and references addresses.


Relationship

addressAssignments()

Returns all AddressAssignment records for this model.

$assignments = $job->addressAssignments;

foreach ($assignments as $assignment) {
    echo $assignment->role;                     // "pickup"
    echo $assignment->addressLink->type->label(); // "Office"
    echo $assignment->addressLink->address->city; // "Vienna"
}

Return: MorphMany of AddressAssignment


Assigning

Assign an existing address link to this model.

use Blax\Addresses\Enums\AddressLinkType;

$link = $user->addAddress([
    'street'       => 'Kärntner Straße 21',
    'city'         => 'Vienna',
    'country_code' => 'AT',
], AddressLinkType::Office);

// Assign with a role
$assignment = $job->assignAddressLink($link, 'pickup');

// Assign with extra attributes
$assignment = $job->assignAddressLink($link, 'delivery', [
    'label' => 'Customer Office Delivery',
    'meta'  => ['priority' => 'express', 'time_window' => '09:00-12:00'],
]);

// Assign by link ID
$assignment = $job->assignAddressLink($link->id, 'origin');

Parameters:

  • $addressLinkAddressLink model or its ID
  • $role — Context-specific purpose string (e.g. "pickup", "delivery", "origin", "billing")
  • $extra — Additional attributes: label, meta

Returns: The created AddressAssignment with addressLink.address loaded.


Removing

removeAddressAssignment(int $assignmentId): bool

Remove a specific assignment by its ID.

$assignment = $job->assignAddressLink($link, 'pickup');
$job->removeAddressAssignment($assignment->id); // true

removeAssignmentsForRole(string $role): int

Remove all assignments for a specific role.

// Remove all "pickup" assignments
$removed = $job->removeAssignmentsForRole('pickup'); // 1

Returns: Number of assignments removed.

removeAllAddressAssignments(): int

Remove all address assignments from this model.

$removed = $job->removeAllAddressAssignments(); // 3

Returns: Number of assignments removed.


Querying

addressAssignmentForRole(string $role): ?AddressAssignment

Get the first assignment for a specific role (eager-loads the address link and address).

$assignment = $job->addressAssignmentForRole('pickup');

if ($assignment) {
    echo $assignment->role;                       // "pickup"
    echo $assignment->addressLink->address->city; // "Vienna"
}

addressAssignmentsForRole(string $role): Collection

Get all assignments for a specific role. Useful when a model has multiple addresses for the same role.

// A job with multiple stops
$job->assignAddressLink($linkA, 'stop');
$job->assignAddressLink($linkB, 'stop');
$job->assignAddressLink($linkC, 'stop');

$stops = $job->addressAssignmentsForRole('stop'); // Collection of 3

assignedAddressForRole(string $role): ?Address

Convenience shortcut — returns the Address model directly for a role.

$pickupAddress = $job->assignedAddressForRole('pickup');
echo $pickupAddress->formatted; // "Kärntner Straße 21, 1010, Vienna, AT"

Returns: Address or null.

assignedAddresses(): Collection

Get all addresses assigned to this model (through their links).

$addresses = $job->assignedAddresses();

foreach ($addresses as $address) {
    echo $address->city;
}

Returns: Collection of Address models.

hasAddressAssignments(): bool

Check whether this model has any address assignments.

if ($job->hasAddressAssignments()) {
    // ...
}

hasAssignmentForRole(string $role): bool

Check whether this model has an assignment for a specific role.

if (! $job->hasAssignmentForRole('delivery')) {
    // prompt to assign a delivery address
}

Cascade Behaviour

When an AddressLink is deleted (e.g. because the user removes their office address), all AddressAssignment rows referencing that link are cascade-deleted at the database level.

// User removes their office address link
$user->removeAddressLink($officeLink->id);

// All jobs that referenced this link automatically lose their assignment
$job->hasAssignmentForRole('pickup'); // false

Full Example

use App\Models\User;
use App\Models\Job;
use Blax\Addresses\Enums\AddressLinkType;

// Setup: users own addresses
$sender = User::create(['name' => 'Alice']);
$receiver = User::create(['name' => 'Bob']);

$senderOffice = $sender->addAddress([
    'street'       => 'Stephansplatz 1',
    'city'         => 'Vienna',
    'country_code' => 'AT',
], AddressLinkType::Office);

$receiverHome = $receiver->addAddress([
    'street'       => 'Unter den Linden 77',
    'city'         => 'Berlin',
    'country_code' => 'DE',
], AddressLinkType::Home);

// Job references both addresses
$job = Job::create(['title' => 'Piano Transport #42']);

$job->assignAddressLink($senderOffice, 'pickup', [
    'label' => "Alice's Office",
]);

$job->assignAddressLink($receiverHome, 'delivery', [
    'label' => "Bob's Home",
    'meta'  => ['floor' => 3, 'elevator' => false],
]);

// Query
$job->assignedAddressForRole('pickup')->city;   // "Vienna"
$job->assignedAddressForRole('delivery')->city;  // "Berlin"
$job->assignedAddresses()->count();              // 2
$job->hasAssignmentForRole('pickup');             // true
$job->hasAssignmentForRole('billing');            // false

// Clean up a single assignment
$job->removeAssignmentsForRole('delivery');
$job->assignedAddresses()->count(); // 1

// Clean up everything
$job->removeAllAddressAssignments();