CRM/backend/app/Http/Controllers/Api/ContactController.php

179 خطوط
7.1 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Contact;
use App\Models\ContactPhone;
use App\Models\Lead;
use App\Services\ActivityLogger;
use App\Support\AccessControl;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
class ContactController extends Controller
{
public function index(Request $request): JsonResponse
{
$query = Contact::with('phones', 'lead:id,company,first_name,last_name', 'company:id,name', 'deal:id,title');
if ($request->lead_id) $query->where('lead_id', $request->lead_id);
if ($request->company_id) $query->where('company_id', $request->company_id);
if ($request->deal_id) $query->where('deal_id', $request->deal_id);
if ($request->status) $query->where('status', $request->status);
if ($request->search) {
$search = $request->search;
$query->where(fn($q) => $q->where('name', 'like', "%{$search}%")->orWhere('email', 'like', "%{$search}%"));
}
return response()->json($query->latest()->paginate(min((int) $request->get('per_page', 15), 100)));
}
public function storeStandalone(Request $request): JsonResponse
{
$validated = $this->validateContact($request);
$contact = DB::transaction(fn() => $this->createContact($validated));
ActivityLogger::log('contact_created', "Contact {$contact->name} created", $contact);
return response()->json($contact->load('phones.lastCaller', 'company:id,name', 'lead:id,company'), 201);
}
public function store(Request $request, Lead $lead): JsonResponse
{
$this->authorizeLeadAccess($lead);
$validated = $this->validateContact($request);
$validated['lead_id'] = $lead->id;
$contact = DB::transaction(function () use ($lead, $validated) {
if (!empty($validated['is_primary'])) {
Contact::where('lead_id', $lead->id)->update(['is_primary' => false]);
}
$contact = $this->createContact($validated);
if ($contact->is_primary) {
ActivityLogger::log('primary_contact_changed', "Primary contact changed to {$contact->name}", $lead);
}
return $contact;
});
ActivityLogger::log('contact_created', "Contact {$contact->name} created for lead {$lead->id}", $contact);
return response()->json($contact->load('phones.lastCaller'), 201);
}
public function setPrimary(Request $request, Contact $contact): JsonResponse
{
if ($contact->lead) {
$this->authorizeLeadAccess($contact->lead);
}
$validated = $request->validate([
'reason' => 'nullable|string|max:255',
]);
DB::transaction(function () use ($contact, $validated) {
Contact::where('lead_id', $contact->lead_id)->update(['is_primary' => false]);
$contact->update([
'is_primary' => true,
'primary_reason' => $validated['reason'] ?? 'انتخاب دستی توسط کاربر',
]);
});
ActivityLogger::log('primary_contact_changed', "Primary contact changed to {$contact->name}", $contact->lead);
return response()->json($contact->fresh('phones.lastCaller'));
}
public function update(Request $request, Contact $contact): JsonResponse
{
$validated = $this->validateContact($request, true);
$contact->update($validated);
if ($request->has('phones')) {
$contact->phones()->delete();
foreach ($validated['phones'] ?? [] as $phone) {
ContactPhone::create([
'contact_id' => $contact->id,
'phone' => $phone['phone'],
'type' => $phone['type'] ?? 'mobile',
'status' => $phone['status'] ?? 'active',
]);
}
}
ActivityLogger::log('contact_updated', "Contact {$contact->name} updated", $contact);
return response()->json($contact->fresh('phones.lastCaller'));
}
public function destroy(Contact $contact): JsonResponse
{
$contact->delete();
ActivityLogger::log('contact_deleted', "Contact {$contact->id} deleted");
return response()->json(['message' => 'مخاطب حذف شد']);
}
private function authorizeLeadAccess(Lead $lead): void
{
Gate::authorize('update', $lead);
}
private function validateContact(Request $request, bool $partial = false): array
{
$sometimes = $partial ? 'sometimes|' : '';
return $request->validate([
'lead_id' => 'nullable|exists:leads,id',
'company_id' => 'nullable|exists:companies,id',
'deal_id' => 'nullable|exists:deals,id',
'name' => $sometimes . 'required|string|max:255',
'first_name' => 'nullable|string|max:120',
'last_name' => 'nullable|string|max:120',
'role' => 'nullable|string|max:80',
'job_title' => 'nullable|string|max:120',
'email' => 'nullable|email|max:255',
'preferred_channel' => 'nullable|string|max:40',
'description' => 'nullable|string',
'status' => 'nullable|string|max:30',
'is_primary' => 'nullable|boolean',
'primary_reason' => 'nullable|string|max:255',
'phones' => ($partial ? 'nullable' : 'required') . '|array|min:1',
'phones.*.phone' => 'required|string|max:30',
'phones.*.type' => 'nullable|string|in:mobile,landline,extension',
'phones.*.status' => 'nullable|string|max:30',
]);
}
private function createContact(array $validated): Contact
{
$contact = Contact::create([
'lead_id' => $validated['lead_id'] ?? null,
'company_id' => $validated['company_id'] ?? null,
'deal_id' => $validated['deal_id'] ?? null,
'name' => $validated['name'],
'first_name' => $validated['first_name'] ?? null,
'last_name' => $validated['last_name'] ?? null,
'role' => $validated['role'] ?? null,
'job_title' => $validated['job_title'] ?? null,
'email' => $validated['email'] ?? null,
'preferred_channel' => $validated['preferred_channel'] ?? null,
'description' => $validated['description'] ?? null,
'status' => $validated['status'] ?? 'active',
'is_primary' => (bool) ($validated['is_primary'] ?? false),
'primary_reason' => $validated['primary_reason'] ?? (!empty($validated['is_primary']) ? 'انتخاب دستی توسط کاربر' : null),
'created_by' => auth()->id(),
]);
foreach ($validated['phones'] as $phone) {
ContactPhone::create([
'contact_id' => $contact->id,
'phone' => $phone['phone'],
'type' => $phone['type'] ?? 'mobile',
'status' => $phone['status'] ?? 'active',
]);
}
return $contact;
}
}