Search by

lasaas / address

runphp

租户级地址模块,基于 commerceguys/addressing,支持中国省市区三级联动与国外地址格式。

Package info

gitee.com/lasaas/lasaas-address

Type:lasaas-module

pkg:composer/lasaas/address

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

dev-main 2026-08-11 05:26 UTC

This package is auto-updated.

Last update: 2026-09-11 05:38:03 UTC


README

租户级地址模块,基于 commerceguys/addressing,支持中国省市区三级联动与国外地址格式。

  • 包名:lasaas/addresstype: lasaas-module
  • 命名空间:Lasaas\Address\
  • 适用区域:central + tenant

功能

  • 多态挂接:地址通过 addressables 中间表挂接到任意业务实体(User、Order 等),业务模型零改动,无需引入 trait 或关系。
  • 中国省市区三级级联:按行政区层级 Select 联动;直辖市(京津沪渝)自动跳过市级;国外地址同字段回退为自由文本输入。
  • 本地化:国家与行政区名称跟随当前应用语言输出。
  • 地址格式化:基于 commerceguys DefaultFormatter,按国家格式输出完整地址,支持指定语言与 HTML 标记。
  • 业务联系信息contact_name(收件人姓名)、phone(手机号)、phone_country_code(国际区号)存于主表,不进入 commerceguys 地理地址格式化;formattedWithContact() 用于打印/物流面单。
  • 主体私有属性:默认地址、标签、备注等存入 address_metas 表(多态),不污染 addresses 主表。
  • 地址用途类型billing / shipping / home / work / other,支持平台级与租户级定制。
  • 前台个人后台:Livewire 页面管理当前用户地址(增删改、设默认),中央与租户侧边栏自动注入「地址」入口。
  • Filament 管理资源:中央 admin 与租户 admin 面板均注册 AddressResource
  • 可复用表单组件AddressForm::schema() 可在任意 Filament 业务资源中直接展开。

目录结构

packages/custom/lasaas/address/
├── composer.json
├── database/migrations/         # 中央迁移(安装时运行)
│   └── tenant/                  # 租户迁移(安装到租户时运行)
├── resources/
│   ├── lang/                    # 翻译(en / zh_CN)
│   └── views/pages/⚡addresses.blade.php   # 前台地址管理页(Livewire SFC)
├── routes/
│   ├── web.php                  # 中央:GET /addresses → addresses.index
│   └── tenant.php               # 租户:GET /addresses → tenant.addresses.index
├── src/
│   ├── AddressServiceProvider.php
│   ├── Database/Factories/AddressFactory.php
│   ├── Filament/
│   │   ├── Plugins/AdminAddressPlugin.php   # 中央 admin 面板
│   │   ├── Plugins/TenantAddressPlugin.php  # 租户 admin 面板
│   │   └── Resources/Addresses/             # 管理资源(列表/新建/编辑/查看)
│   ├── Forms/AddressForm.php                # 可复用 Filament 表单字段
│   ├── Models/                              # Address / AddressOwner / AddressMeta
│   ├── Repositories/AddressRepository.php
│   ├── Settings/                            # 平台设置 + 租户设置
│   └── Support/                             # SubdivisionService / AddressFormatter
└── tests/                                   # Pest 测试

数据表

说明
addresses地址主表,字段对齐 commerceguys addressing 模型(国家、行政区、街道、姓名、邮编、langcode 等),另含业务联系信息 contact_name/phone/phone_country_code
addressables地址 ↔ 业务实体多态挂接表(addressable 多态 + type 用途)
address_metas主体私有属性表(owner 多态 + meta_key/meta_value JSON),如 is_default

使用:挂接/查询业务实体的地址

通过 AddressRepository 以多态方式操作,业务模型无需任何改动:

use Lasaas\Address\Repositories\AddressRepository;

$repo = app(AddressRepository::class);

// 查询某实体的全部地址(默认地址优先)
$addresses = $repo->for($order);

// 创建地址并挂接(type 须在设置声明的用途内)
$repo->createFor($order, $attributes, type: 'shipping');

// 复制已有地址到新业务实体(下单选地址簿条目时使用,双方编辑互不影响)
// 副本可覆盖字段;is_default 表示把副本同时设为目标实体的默认地址
$repo->copyFor($bookAddress, $order, type: 'shipping', [
    'organization' => $company->name,
    'is_default' => true,
]);

// 解绑(地址行仅属于该实体时随解绑清理)
$repo->detach($order, $address);

// 默认地址
$default = $repo->getDefault($order);
$repo->setDefault($order, $address);

$address->formatted() 按国家格式输出完整地址字符串(纯地理地址,不含联系信息)。 联系信息单独取值/输出,供打印、物流面单、导出:

$address->contact();           // 收件人姓名(contact_name 优先,回退 given_name + family_name)
$address->phoneNumber();       // 完整手机号(国际区号 + 号码,如 +86 13800138000)
$address->formattedWithContact(); // 收件人 + 手机号 + 纯地理地址(多行拼接)

// 与 commerceguys 值对象互转
$vo   = $address->toBaseAddress();                    // 模型 → 值对象
$addr = Address::fromBaseAddress($vo, ['phone' => '13800138000']); // 值对象 → 模型

注意:commerceguys 的 Address 值对象不包含手机号/联系人(CLDR 只管地理地址), formatted() 不会输出它们;扩展字段由 Eloquent 模型持有,与值对象解耦,升级 composer 包不受影响。

使用方冗余快照(推荐用法:选中即复制)

地址模块是「一行地址最多归属一个业务实体」的 copy-on-write 设计(Address::assertExclusiveOwner 保证)。业务实体(如订单)在下单/选地址时,应把选中的地址复制一份挂到自己名下,这样地址簿后续的修改/删除都不会影响历史订单的地址:

use Lasaas\Address\Models\Address;
use Lasaas\Address\Repositories\AddressRepository;

$repo = app(AddressRepository::class);

// 下单:把用户选中的地址簿条目冗余复制到订单(双方互不影响)
$snapshot = $repo->copyFor($selectedAddress, $order, type: 'shipping');

// 等价写法(模型便捷方法)
$snapshot = $selectedAddress->copyTo($order, type: 'shipping', [
    'organization' => $company->name, // 覆盖副本字段,如订单抬头
    'is_default' => true,             // 同时把副本设为目标实体的默认地址
]);

// 订单展示/编辑自己的副本,永不串改用户地址簿
echo $repo->for($order)->first()->formatted();

要点:

  • 副本是 addresses 表里的一行独立地址,通过 addressables 独占挂接;地址簿条目被修改/删除时订单副本不受影响,反之亦然;
  • 主体私有属性(默认地址、标签、备注)不随副本迁移
  • is_default 仅影响副本,原地址的默认标记不动;copyFor 忽略副本字段里的 id/created_at/updated_at

设置

  • 平台设置 AddressPlatformSettings(中央后台「模块 → 设置」):
    • default_country:表单默认国家(ISO-2)
    • types:允许的地址用途 key => 显示名
  • 租户设置 AddressTenantSettings(中央后台「租户 → 模块管理」):同名覆盖、其余回退到平台设置。

在业务资源中复用地址表单

use Lasaas\Address\Filament\Plugins\TenantAddressPlugin;

public static function schema(): array
{
    return [
        ...TenantAddressPlugin::formSchema(),
        // 业务字段……
    ];
}

测试

php artisan test --compact packages/custom/lasaas/address/tests

覆盖:地址表单互斥分支共享 statePath、前台页面增删改与默认地址、平台/租户设置、仓储挂接与校验、导航注入。