feat: E.164 phone validation and storage with libphonenumber
- Add giggsey/libphonenumber-for-php, PhoneNumberNormalizer, ValidPhoneNumber rule - Store subscribers as E.164; mutator normalizes on save; optional phone required from form block - Migration to normalize legacy subscriber phones; Mailwizz/search/UI/tests updated - Add run-deploy-from-local.sh and PREREGISTER_DEFAULT_PHONE_REGION in .env.example Made-with: Cursor
This commit is contained in:
@@ -108,6 +108,19 @@ class PreregistrationPage extends Model
|
||||
return (bool) $this->phone_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* When the form block marks the phone field as required (only applies if phone is enabled).
|
||||
*/
|
||||
public function isPhoneFieldRequiredForSubscribers(): bool
|
||||
{
|
||||
$form = $this->getBlockByType('form');
|
||||
if ($form !== null) {
|
||||
return (bool) data_get($form->content, 'fields.phone.required', false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function headlineForMeta(): string
|
||||
{
|
||||
$hero = $this->getHeroBlock();
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Services\PhoneNumberNormalizer;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -36,6 +37,52 @@ class Subscriber extends Model
|
||||
return $this->belongsTo(PreregistrationPage::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $value
|
||||
*/
|
||||
public function setPhoneAttribute(mixed $value): void
|
||||
{
|
||||
if ($value === null) {
|
||||
$this->attributes['phone'] = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! is_string($value)) {
|
||||
$this->attributes['phone'] = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$trimmed = trim($value);
|
||||
if ($trimmed === '') {
|
||||
$this->attributes['phone'] = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$normalized = app(PhoneNumberNormalizer::class)->normalizeToE164($trimmed);
|
||||
$this->attributes['phone'] = $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Phones are stored as E.164 (e.g. +31612345678). Legacy rows may still be digits-only.
|
||||
*/
|
||||
public function phoneDisplay(): ?string
|
||||
{
|
||||
$phone = $this->phone;
|
||||
if ($phone === null || $phone === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$p = (string) $phone;
|
||||
if (str_starts_with($p, '+')) {
|
||||
return $p;
|
||||
}
|
||||
|
||||
return preg_match('/^\d{8,15}$/', $p) === 1 ? '+'.$p : $p;
|
||||
}
|
||||
|
||||
public function scopeSearch(Builder $query, ?string $term): Builder
|
||||
{
|
||||
if ($term === null || $term === '') {
|
||||
@@ -47,7 +94,8 @@ class Subscriber extends Model
|
||||
return $query->where(function (Builder $q) use ($like): void {
|
||||
$q->where('first_name', 'like', $like)
|
||||
->orWhere('last_name', 'like', $like)
|
||||
->orWhere('email', 'like', $like);
|
||||
->orWhere('email', 'like', $like)
|
||||
->orWhere('phone', 'like', $like);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user