Skip to content

Commit 2594f7b

Browse files
Feature/company addresses and core (#956)
* feautre core Support MorphPivotRelation * init company package * fixes Addres package * Fix styling * fix some stan errors * wip stan errors * Update README.md * Update README.md
1 parent db85a0f commit 2594f7b

51 files changed

Lines changed: 3467 additions & 99 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/address/README.md

Lines changed: 102 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@
22

33
# Moox Address
44

5-
Address is a simple Moox Entity, that can be used to create and manage addresses.
5+
Address is a simple Moox Entity that can be used to create and manage postal addresses and assign them to owners via a morph pivot.
66

77
## Features
88

99
<!--features-->
1010

11-
- Title with Slug
12-
- Active (Toggle)
13-
- Description (Editor)
14-
- Custom Properties (Key-Value)
15-
- Author (User)
16-
- UUID
17-
- ULID
11+
- Postal fields (street, city, country, etc.)
12+
- Optional label and primary flag
13+
- Custom data (JSON)
14+
- Duplicate detection (fingerprint)
15+
- Morph assignments with billing, postal, and delivery roles
16+
- Soft delete
1817
- Taxonomies
18+
- Filament resource with relation manager
1919

2020
<!--/features-->
2121

@@ -36,10 +36,6 @@ Curious what the install command does? See [Installation](https://github.com/moo
3636

3737
![Moox Address](https://github.com/mooxphp/moox/raw/main/art/screenshots/record.jpg)
3838

39-
## Get Started
40-
41-
See [Get Started](docs/GetStarted.md).
42-
4339
## Changelog
4440

4541
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
@@ -59,3 +55,97 @@ Thanks to so many [people for their contributions](https://github.com/mooxphp/mo
5955
## License
6056

6157
The MIT License (MIT). Please see [our license and copyright information](https://github.com/mooxphp/moox/blob/main/LICENSE.md) for more information.
58+
59+
## The Address Model
60+
61+
The `Address` model (`Moox\Address\Models\Address`) stores normalized postal data. It extends `BaseItemModel`, uses soft deletes, and supports taxonomies via `HasModelTaxonomy`.
62+
63+
### Attributes
64+
65+
#### Base Fields
66+
67+
- `label` (string, 120) - Optional internal label (e.g. Headquarter, Warehouse)
68+
- `name` (string, 160) - Recipient or company name on the address
69+
- `street` (string, 160) - Street and house number
70+
- `street2` (string, 160) - Additional address line (suite, building, etc.)
71+
- `postal_code` (string, 20) - Postal / ZIP code
72+
- `city` (string, 120) - City
73+
- `state` (string, 120) - State, region, or province
74+
- `country_code` (string, 2) - ISO 3166-1 alpha-2 country code (stored uppercase)
75+
- `is_primary` (boolean) - Marks this address as primary (default: false)
76+
- `data` (json) - Flexible key-value payload for custom metadata
77+
- `deleted_at` (datetime) - Soft-delete timestamp
78+
- `created_at` (datetime) - Creation timestamp
79+
- `updated_at` (datetime) - Last update timestamp
80+
81+
Validation rules live in `Moox\Address\Support\AddressRules` and are applied in the Filament resource.
82+
83+
#### Duplicate detection
84+
85+
Duplicate addresses are blocked on save via `DuplicateAddressException`. Comparison uses `AddressFingerprint` with these columns only:
86+
87+
- `street`
88+
- `street2`
89+
- `postal_code`
90+
- `country_code`
91+
92+
Not part of the fingerprint: `label`, `name`, `city`, `state`, `is_primary`, and `data`. Two records with the same street, postal code, and country but different city or name are still treated as duplicates.
93+
94+
Empty strings are normalized to `null` before comparison. `country_code` is trimmed and uppercased on save.
95+
96+
### Methods
97+
98+
#### Formatting
99+
100+
- `formattedLine()` - Single-line summary: name, street lines, postal code + city, country code
101+
102+
#### Duplicate detection
103+
104+
- `findDuplicate()` - Returns an existing `Address` with the same fingerprint, or null
105+
- `scopeWithFingerprint()` - Query scope for fingerprint lookup
106+
107+
### Relationships
108+
109+
- `addressables()` - Pivot rows linking this address to owners
110+
111+
## The Addressable Pivot
112+
113+
Assignments between an address and an owner (company, contact, user, etc.) live on `addressables`, not on `addresses`. Roles are pivot flags, not columns on the address itself.
114+
115+
### Attributes
116+
117+
#### Pivot Fields
118+
119+
- `addressable_type` (uuid morph) - Owner model class
120+
- `addressable_id` (uuid) - Owner primary key
121+
- `address_id` (foreignId) - References `addresses.id` (cascade on delete)
122+
- `billing_address` (boolean) - Use as billing address (default: false)
123+
- `postal_address` (boolean) - Use as postal address (default: false)
124+
- `delivery_address` (boolean) - Use as delivery address (default: false)
125+
- `created_at` (datetime) - Creation timestamp
126+
- `updated_at` (datetime) - Last update timestamp
127+
128+
Unique constraint: `(addressable_type, addressable_id, address_id)`.
129+
130+
### Methods
131+
132+
- `activeRoles()` - Active role keys: `billing`, `postal`, `delivery`
133+
134+
### Relationships
135+
136+
- `addressable()` - Owner (`MorphTo`)
137+
- `address()` - Linked `Address` (`BelongsTo`)
138+
139+
### Owner trait
140+
141+
Models that can own addresses use `Moox\Address\Concerns\HasAddresses`:
142+
143+
- `addresses()` - `MorphToMany` via `addressables`, with pivot columns from `config('address.relations.addressables')`
144+
145+
Register allowed owner types under `address.relations.addressables.owner_types` in `config/address.php`.
146+
147+
### Translations
148+
149+
Field labels for the admin UI are in `resources/lang/{locale}/fields.php` (e.g. `address::fields.street`). Entity titles use `address::address.*`.
150+
151+
There are no translatable model attributes on `Address`; all address fields are stored on the main table.

packages/address/config/address.php

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
use Moox\Address\Models\Addressable;
55
use Moox\Category\Resources\CategoryResource;
66
use Moox\Media\Resources\MediaResource;
7-
use Moox\News\Moox\Entities\News\News\NewsResource;
87
use Moox\Tag\Resources\TagResource;
98
use Moox\User\Models\User;
109
use Moox\User\Resources\UserResource;
@@ -78,9 +77,6 @@
7877

7978
'scopes' => [
8079
'allowed' => [
81-
'news' => [
82-
'resource' => NewsResource::class,
83-
],
8480
'media' => [
8581
'resource' => MediaResource::class,
8682
],
@@ -126,8 +122,8 @@
126122
'delivery_address',
127123
],
128124
'owner_types' => [
129-
// Heco\Company\Models\Company::class => 'Company',
130-
// Heco\Contact\Models\Contact::class => 'Contact',
125+
'Moox\Company\Models\Company' => 'Company',
126+
// 'Heco\Contact\Models\Contact' => 'Contact',
131127
],
132128
],
133129
],
@@ -164,6 +160,6 @@
164160
| and if the panel is enabled.
165161
|
166162
*/
167-
'navigation_group' => 'DEV',
163+
'navigation_group' => 'Portal',
168164

169165
];

packages/address/database/Factories/AddressFactory.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ public function definition(): array
2828
]),
2929
'name' => fake()->company(),
3030
'street' => fake()->streetName().' '.fake()->buildingNumber(),
31-
'street2' => fake()->optional(0.15)->secondaryAddress(),
31+
'street2' => fake()->optional(0.15)->streetAddress(),
3232
'postal_code' => fake()->postcode(),
3333
'city' => fake()->city(),
34-
'state' => fake()->optional(0.4)->state(),
34+
'state' => fake()->optional(0.4)->randomElement(['BE', 'BY', 'HH', 'NW', 'HE']),
3535
'country_code' => fake()->countryCode(),
3636
'is_primary' => false,
3737
'data' => null,
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Address Attributes
2+
3+
This document describes the database fields and pivot attributes of the Moox Address entity.
4+
5+
## The Address Model
6+
7+
The `Address` model (`Moox\Address\Models\Address`) stores normalized postal data. It extends `BaseItemModel`, uses soft deletes, and supports taxonomies via `HasModelTaxonomy`.
8+
9+
### Attributes
10+
11+
#### Base Fields
12+
13+
- `label` (string, 120) - Optional internal label (e.g. Headquarter, Warehouse)
14+
- `name` (string, 160) - Recipient or company name on the address
15+
- `street` (string, 160) - Street and house number
16+
- `street2` (string, 160) - Additional address line (suite, building, etc.)
17+
- `postal_code` (string, 20) - Postal / ZIP code
18+
- `city` (string, 120) - City
19+
- `state` (string, 120) - State, region, or province
20+
- `country_code` (string, 2) - ISO 3166-1 alpha-2 country code (stored uppercase)
21+
- `is_primary` (boolean) - Marks this address as primary (default: false)
22+
- `data` (json) - Flexible key-value payload for custom metadata
23+
- `deleted_at` (datetime) - Soft-delete timestamp
24+
- `created_at` (datetime) - Creation timestamp
25+
- `updated_at` (datetime) - Last update timestamp
26+
27+
Validation rules live in `Moox\Address\Support\AddressRules` and are applied in the Filament resource.
28+
29+
#### Duplicate detection
30+
31+
Duplicate addresses are blocked on save via `DuplicateAddressException`. Comparison uses `AddressFingerprint` with these columns only:
32+
33+
- `street`
34+
- `street2`
35+
- `postal_code`
36+
- `country_code`
37+
38+
Not part of the fingerprint: `label`, `name`, `city`, `state`, `is_primary`, and `data`. Two records with the same street, postal code, and country but different city or name are still treated as duplicates.
39+
40+
Empty strings are normalized to `null` before comparison. `country_code` is trimmed and uppercased on save.
41+
42+
### Methods
43+
44+
#### Formatting
45+
46+
- `formattedLine()` - Single-line summary: name, street lines, postal code + city, country code
47+
48+
#### Duplicate detection
49+
50+
- `scopeWithFingerprint()` - Query scope for fingerprint lookup
51+
52+
### Relationships
53+
54+
- `addressables()` - Pivot rows linking this address to owners
55+
56+
## The Addressable Pivot
57+
58+
Assignments between an address and an owner (company, contact, user, etc.) live on `addressables`, not on `addresses`. Roles are pivot flags, not columns on the address itself.
59+
60+
### Attributes
61+
62+
#### Pivot Fields
63+
64+
- `addressable_type` (uuid morph) - Owner model class
65+
- `addressable_id` (uuid) - Owner primary key
66+
- `address_id` (foreignId) - References `addresses.id` (cascade on delete)
67+
- `billing_address` (boolean) - Use as billing address (default: false)
68+
- `postal_address` (boolean) - Use as postal address (default: false)
69+
- `delivery_address` (boolean) - Use as delivery address (default: false)
70+
- `created_at` (datetime) - Creation timestamp
71+
- `updated_at` (datetime) - Last update timestamp
72+
73+
Unique constraint: `(addressable_type, addressable_id, address_id)`.
74+
75+
### Methods
76+
77+
- `activeRoles()` - Active role keys: `billing`, `postal`, `delivery`
78+
79+
### Relationships
80+
81+
- `addressable()` - Owner (`MorphTo`)
82+
- `address()` - Linked `Address` (`BelongsTo`)
83+
84+
### Owner trait
85+
86+
Models that can own addresses use `Moox\Address\Concerns\HasAddresses`:
87+
88+
- `addresses()` - `MorphToMany` via `addressables`, with pivot columns from `config('address.relations.addressables')`
89+
90+
Register allowed owner types under `address.relations.addressables.owner_types` in `config/address.php`.
91+
92+
## Translations
93+
94+
Field labels for the admin UI are in `resources/lang/{locale}/fields.php` (e.g. `address::fields.street`). Entity titles use `address::address.*`.
95+
96+
There are no translatable model attributes on `Address`; all address fields are stored on the main table.

packages/address/resources/lang/de/fields.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@
1717
'assignments' => 'Zuordnungen',
1818
'owner' => 'Besitzer',
1919
'add_assignment' => 'Zuordnung hinzufügen',
20+
'attach_address' => 'Adresse zuordnen',
21+
'create_address' => 'Adresse anlegen',
2022
'duplicate_address' => 'Eine Adresse mit derselben Straße, PLZ und demselben Land existiert bereits.',
23+
'owner_name' => 'Name',
2124
];

packages/address/resources/lang/en/fields.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@
1717
'assignments' => 'Assignments',
1818
'owner' => 'Owner',
1919
'add_assignment' => 'Add assignment',
20+
'attach_address' => 'Attach address',
21+
'create_address' => 'Create address',
2022
'duplicate_address' => 'An address with the same street, postal code and country already exists.',
23+
'owner_name' => 'Name',
2124
];

packages/address/src/AddressServiceProvider.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,27 @@
44

55
namespace Moox\Address;
66

7+
use Moox\Address\Models\Address;
8+
use Moox\Address\Resources\AddressResource;
79
use Moox\Core\MooxServiceProvider;
10+
use Moox\Core\Support\MorphPivot\MorphPivotRelationRegistry;
811
use Spatie\LaravelPackageTools\Package;
912

1013
class AddressServiceProvider extends MooxServiceProvider
1114
{
15+
public function boot(): void
16+
{
17+
parent::boot();
18+
19+
MorphPivotRelationRegistry::registerRelatedModel(Address::class, [
20+
'display_columns' => ['name', 'city', 'postal_code', 'country_code', 'is_primary'],
21+
'translation_prefix' => 'address::fields',
22+
'related_resource' => AddressResource::class,
23+
'record_select_label' => 'formattedLine',
24+
'record_select_search_columns' => ['name', 'city', 'postal_code', 'street', 'street2', 'label'],
25+
]);
26+
}
27+
1228
public function configureMoox(Package $package): void
1329
{
1430
$package

packages/address/src/Concerns/HasAddresses.php

Lines changed: 0 additions & 35 deletions
This file was deleted.

packages/address/src/Concerns/HasMorphedByOwnerTypes.php

Whitespace-only changes.

packages/address/src/Frontend/AddressFrontend.php

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)