feat(phone): implement PhoneNumber and TenantPhoneNumber models with relationships and migration
This commit is contained in:
30
app/Domains/Core/PhoneNumber/Models/PhoneNumber.php
Normal file
30
app/Domains/Core/PhoneNumber/Models/PhoneNumber.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Core\PhoneNumber\Models;
|
||||
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class PhoneNumber extends Model
|
||||
{
|
||||
protected $fillable = ['label', 'number'];
|
||||
|
||||
public static function dialableNumber(string $number): string
|
||||
{
|
||||
return preg_replace('/[^0-9+]/', '', $number);
|
||||
}
|
||||
|
||||
public function telUrl(): string
|
||||
{
|
||||
return 'tel:'.self::dialableNumber($this->number);
|
||||
}
|
||||
|
||||
public function tenants(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Tenant::class, 'tenant_phone_numbers', 'phone_number_id', 'tenant_codigo', 'id', 'codigo')
|
||||
->using(TenantPhoneNumber::class)
|
||||
->withPivot(['id', 'is_main'])
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
31
app/Domains/Core/PhoneNumber/Models/TenantPhoneNumber.php
Normal file
31
app/Domains/Core/PhoneNumber/Models/TenantPhoneNumber.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Core\PhoneNumber\Models;
|
||||
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
class TenantPhoneNumber extends Pivot
|
||||
{
|
||||
protected $table = 'tenant_phone_numbers';
|
||||
|
||||
public $incrementing = true;
|
||||
|
||||
protected $fillable = ['tenant_codigo', 'phone_number_id', 'is_main'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return ['is_main' => 'boolean'];
|
||||
}
|
||||
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class, 'tenant_codigo', 'codigo');
|
||||
}
|
||||
|
||||
public function phoneNumber(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PhoneNumber::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Core\PhoneNumber\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class PhoneNumberResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'label' => $this->label,
|
||||
'number' => $this->number,
|
||||
'tel_url' => $this->resource->telUrl(),
|
||||
'is_main' => $this->whenPivotLoaded('tenant_phone_numbers', fn () => (bool) $this->pivot->is_main),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ use App\Domains\Commerce\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Commerce\Catalog\Models\Category;
|
||||
use App\Domains\Core\Address\Models\Address;
|
||||
use App\Domains\Core\Address\Models\TenantAddress;
|
||||
use App\Domains\Core\PhoneNumber\Models\PhoneNumber;
|
||||
use App\Domains\Core\PhoneNumber\Models\TenantPhoneNumber;
|
||||
use App\Domains\Core\Client\Models\Client;
|
||||
use App\Domains\Core\Menu\Models\Menu;
|
||||
use App\Domains\Core\Menu\Models\TenantMenu;
|
||||
@@ -328,6 +330,22 @@ class Tenant extends Model
|
||||
->where('tenant_addresses.is_main', true);
|
||||
}
|
||||
|
||||
public function phoneNumbers(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(PhoneNumber::class, 'tenant_phone_numbers', 'tenant_codigo', 'phone_number_id', 'codigo', 'id')
|
||||
->using(TenantPhoneNumber::class)
|
||||
->withPivot(['id', 'is_main'])
|
||||
->withTimestamps()
|
||||
->orderByPivot('is_main', 'desc')
|
||||
->orderByPivot('id');
|
||||
}
|
||||
|
||||
public function mainPhoneNumber(): HasOneThrough
|
||||
{
|
||||
return $this->hasOneThrough(PhoneNumber::class, TenantPhoneNumber::class, 'tenant_codigo', 'id', 'codigo', 'phone_number_id')
|
||||
->where('tenant_phone_numbers.is_main', true);
|
||||
}
|
||||
|
||||
public function menues(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Domains\Core\Tenant\Resources;
|
||||
|
||||
use App\Domains\Commerce\Catalog\Models\Category;
|
||||
use App\Domains\Core\Address\Resources\AddressResource;
|
||||
use App\Domains\Core\PhoneNumber\Resources\PhoneNumberResource;
|
||||
use App\Domains\Core\Menu\Models\Menu;
|
||||
use App\Domains\Core\Tenant\Models\StorefrontWebsiteType;
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
@@ -45,6 +46,8 @@ class TenantResource extends JsonResource
|
||||
'addresses' => $this->whenLoaded('addresses', fn () => TenantAddressResource::collection($this->addresses)),
|
||||
'main_address' => $this->whenLoaded('mainAddress', fn () => AddressResource::make($this->mainAddress)),
|
||||
'phone' => $this->phone,
|
||||
'phone_numbers' => $this->whenLoaded('phoneNumbers', fn () => PhoneNumberResource::collection($this->phoneNumbers)),
|
||||
'main_phone_number' => $this->whenLoaded('mainPhoneNumber', fn () => PhoneNumberResource::make($this->mainPhoneNumber)),
|
||||
'favicon' => $this->favicon?->getTemporaryUrl(1440),
|
||||
'primary_color' => $this->primary_color,
|
||||
'secondary_color' => $this->secondary_color,
|
||||
|
||||
@@ -20,6 +20,8 @@ class TenantInformationService
|
||||
'socialMedia',
|
||||
'addresses',
|
||||
'mainAddress',
|
||||
'phoneNumbers',
|
||||
'mainPhoneNumber',
|
||||
'websiteExtras.websiteTypeExtra',
|
||||
'activeEvent.dates',
|
||||
'activeEvent.socialMedia',
|
||||
|
||||
@@ -12,6 +12,7 @@ Es la raíz del modelo multi-tenant. Gestiona organizaciones/sitios, tipos de we
|
||||
- `StorefrontWebsiteTypeExtra`: definición de un extra y su configuración admitida.
|
||||
- `WebsiteExtra`: valor resuelto y estado del extra para un tenant.
|
||||
- `SocialMedia`: catálogo de redes sociales asociables.
|
||||
- `phoneNumbers` y `mainPhoneNumber`: teléfonos públicos del tenant, definidos en el dominio `Core/PhoneNumber` y vinculados por `tenant_phone_numbers`. Solo un vínculo por tenant puede ser principal.
|
||||
|
||||
## Servicios
|
||||
|
||||
@@ -31,3 +32,5 @@ El par `(dominio, base_path)` es único. Un mismo dominio puede alojar el tenant
|
||||
## Dependencias y reglas
|
||||
|
||||
Usa `Attachable` para logos y archivos. Es referenciado por casi todos los dominios para aislamiento. El `codigo` es clave de ruta y clave foránea heredada; no debe sustituirse por `id` sin una migración integral.
|
||||
|
||||
Los resources exponen `phone_numbers` (incluye `is_main`) y `main_phone_number`, con `id`, `label`, `number` y `tel_url`. Contacto muestra la colección y el footer usa el principal. La migración copia primero `tenants.phone` como principal y luego el teléfono de `help.contact`, si es distinto; si no existe teléfono del tenant, el del menú pasa a ser principal. Los duplicados por formato se comparan dentro de cada tenant sin inferir país ni código de área. Los campos anteriores se conservan como datos legacy, pero la tienda consume las relaciones nuevas. No hay endpoints de edición de teléfonos ni cambios del adminapp en esta etapa.
|
||||
|
||||
Reference in New Issue
Block a user