fix/projects: нормализация телефона-источника + понятная ошибка и подсказка
Косяк 02: поле телефона проекта типа call отвергало +7.., 8.., пробелы и скобки. prepareForValidation в StoreProjectRequest и UpdateProjectRequest приводит номер через PhoneNormalizer к канону 7XXXXXXXXXX без ведущего плюса, чтобы раздача LeadRouter матчила без плюса. Финальная regex оставлена страховкой. Кастомные messages по signal_type: ошибка с примером формата, без имени Источник. Фронт: постоянная подсказка под полем в NewProjectDialog и ProjectDetailsDrawer. TDD: ProjectPhoneNormalizationTest 8 кейсов, GREEN. Проверено глазами на 8000. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,15 +5,59 @@ declare(strict_types=1);
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Support\PhoneNormalizer;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateProjectRequest extends FormRequest
|
||||
{
|
||||
private ?Project $resolvedProject = null;
|
||||
|
||||
private bool $projectResolved = false;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user() !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Косяк 02: при редактировании call-проекта нормализуем введённый номер
|
||||
* к 7XXXXXXXXXX (тот же PhoneNormalizer, что и реквизиты; «+» срезаем —
|
||||
* раздача матчит без него). Тип signal_type immutable — берём из проекта.
|
||||
*/
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
if (! $this->filled('signal_identifier')) {
|
||||
return;
|
||||
}
|
||||
if ($this->resolveProject()?->signal_type === 'call') {
|
||||
$normalized = PhoneNormalizer::normalize((string) $this->input('signal_identifier'));
|
||||
if ($normalized !== null) {
|
||||
$this->merge(['signal_identifier' => ltrim($normalized, '+')]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
public function messages(): array
|
||||
{
|
||||
return match ($this->resolveProject()?->signal_type) {
|
||||
'call' => ['signal_identifier.regex' => 'Введите номер в формате 79161234567 — цифра 7 и 10 цифр после неё. Можно вводить с +7, 8, скобками и пробелами — приведём сами.'],
|
||||
'site' => ['signal_identifier.regex' => 'Введите домен в формате example.ru — без http://, без www и без пути.'],
|
||||
default => [],
|
||||
};
|
||||
}
|
||||
|
||||
private function resolveProject(): ?Project
|
||||
{
|
||||
if (! $this->projectResolved) {
|
||||
$projectId = $this->route('id');
|
||||
$this->resolvedProject = $projectId !== null ? Project::find($projectId) : null;
|
||||
$this->projectResolved = true;
|
||||
}
|
||||
|
||||
return $this->resolvedProject;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
// signal_type immutable: не валидируется в правилах, controller игнорирует поле
|
||||
@@ -36,17 +80,14 @@ class UpdateProjectRequest extends FormRequest
|
||||
// 18.05.2026 UX: редактирование источника (signal_identifier) для site/call.
|
||||
// Регулярки соответствуют StoreProjectRequest (domain + 7\d{10}).
|
||||
// signal_type immutable — берём из текущего проекта по route id.
|
||||
$projectId = $this->route('id');
|
||||
if ($projectId !== null) {
|
||||
$project = Project::find($projectId);
|
||||
if ($project !== null) {
|
||||
if ($project->signal_type === 'site') {
|
||||
$rules['signal_identifier'] = ['sometimes', 'string', 'regex:/^[a-z0-9][a-z0-9\-]*(\.[a-z0-9][a-z0-9\-]*)*\.[a-z]{2,}$/i'];
|
||||
} elseif ($project->signal_type === 'call') {
|
||||
$rules['signal_identifier'] = ['sometimes', 'string', 'regex:/^7\d{10}$/'];
|
||||
}
|
||||
// sms: signal_identifier меняется через sms_senders/sms_keyword (см. выше)
|
||||
$project = $this->resolveProject();
|
||||
if ($project !== null) {
|
||||
if ($project->signal_type === 'site') {
|
||||
$rules['signal_identifier'] = ['sometimes', 'string', 'regex:/^[a-z0-9][a-z0-9\-]*(\.[a-z0-9][a-z0-9\-]*)*\.[a-z]{2,}$/i'];
|
||||
} elseif ($project->signal_type === 'call') {
|
||||
$rules['signal_identifier'] = ['sometimes', 'string', 'regex:/^7\d{10}$/'];
|
||||
}
|
||||
// sms: signal_identifier меняется через sms_senders/sms_keyword (см. выше)
|
||||
}
|
||||
|
||||
return $rules;
|
||||
|
||||
Reference in New Issue
Block a user