Implement multi-tenancy support with OAuth2 authentication for tenant admins, Stripe integration for event purchases and credits ledger, new Filament resources for event purchases, updated API routes and middleware for tenant isolation and token guarding, added factories/seeders/migrations for new models (Tenant, EventPurchase, OAuth entities, etc.), enhanced tests, and documentation updates. Removed outdated DemoAchievementsSeeder.
This commit is contained in:
224
app/Http/Controllers/Api/Tenant/EventController.php
Normal file
224
app/Http/Controllers/Api/Tenant/EventController.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Tenant;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Tenant\EventStoreRequest;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Http\Resources\Tenant\EventResource;
|
||||
use App\Models\Event;
|
||||
use App\Models\Tenant;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class EventController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the tenant's events.
|
||||
*/
|
||||
public function index(Request $request): AnonymousResourceCollection
|
||||
{
|
||||
$tenantId = $request->attributes->get('tenant_id');
|
||||
|
||||
if (!$tenantId) {
|
||||
throw ValidationException::withMessages([
|
||||
'tenant_id' => 'Tenant ID not found in request context.',
|
||||
]);
|
||||
}
|
||||
|
||||
$query = Event::where('tenant_id', $tenantId)
|
||||
->with(['eventType', 'photos'])
|
||||
->orderBy('created_at', 'desc');
|
||||
|
||||
// Apply filters
|
||||
if ($request->has('status')) {
|
||||
$query->where('status', $request->status);
|
||||
}
|
||||
|
||||
if ($request->has('type_id')) {
|
||||
$query->where('event_type_id', $request->type_id);
|
||||
}
|
||||
|
||||
// Pagination
|
||||
$perPage = $request->get('per_page', 15);
|
||||
$events = $query->paginate($perPage);
|
||||
|
||||
return EventResource::collection($events);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created event in storage.
|
||||
*/
|
||||
public function store(EventStoreRequest $request): JsonResponse
|
||||
{
|
||||
$tenantId = $request->attributes->get('tenant_id');
|
||||
|
||||
// Check credits balance
|
||||
$tenant = Tenant::findOrFail($tenantId);
|
||||
if ($tenant->event_credits_balance <= 0) {
|
||||
return response()->json([
|
||||
'error' => 'Insufficient event credits. Please purchase more credits.',
|
||||
], 402);
|
||||
}
|
||||
|
||||
$validated = $request->validated();
|
||||
|
||||
$event = Event::create(array_merge($validated, [
|
||||
'tenant_id' => $tenantId,
|
||||
'status' => 'draft', // Default status
|
||||
'slug' => $this->generateUniqueSlug($validated['name'], $tenantId),
|
||||
]));
|
||||
|
||||
// Decrement credits
|
||||
$tenant->decrement('event_credits_balance', 1);
|
||||
|
||||
$event->load(['eventType', 'tenant']);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Event created successfully',
|
||||
'data' => new EventResource($event),
|
||||
], 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified event.
|
||||
*/
|
||||
public function show(Request $request, Event $event): JsonResponse
|
||||
{
|
||||
$tenantId = $request->attributes->get('tenant_id');
|
||||
|
||||
// Ensure event belongs to tenant
|
||||
if ($event->tenant_id !== $tenantId) {
|
||||
return response()->json(['error' => 'Event not found'], 404);
|
||||
}
|
||||
|
||||
$event->load([
|
||||
'eventType',
|
||||
'photos' => fn ($query) => $query->with('likes')->latest(),
|
||||
'tasks',
|
||||
'tenant' => fn ($query) => $query->select('id', 'name', 'event_credits_balance')
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'data' => new EventResource($event),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified event in storage.
|
||||
*/
|
||||
public function update(EventStoreRequest $request, Event $event): JsonResponse
|
||||
{
|
||||
$tenantId = $request->attributes->get('tenant_id');
|
||||
|
||||
// Ensure event belongs to tenant
|
||||
if ($event->tenant_id !== $tenantId) {
|
||||
return response()->json(['error' => 'Event not found'], 404);
|
||||
}
|
||||
|
||||
$validated = $request->validated();
|
||||
|
||||
// Update slug if name changed
|
||||
if ($validated['name'] !== $event->name) {
|
||||
$validated['slug'] = $this->generateUniqueSlug($validated['name'], $tenantId, $event->id);
|
||||
}
|
||||
|
||||
$event->update($validated);
|
||||
|
||||
$event->load(['eventType', 'tenant']);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Event updated successfully',
|
||||
'data' => new EventResource($event),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified event from storage.
|
||||
*/
|
||||
public function destroy(Request $request, Event $event): JsonResponse
|
||||
{
|
||||
$tenantId = $request->attributes->get('tenant_id');
|
||||
|
||||
// Ensure event belongs to tenant
|
||||
if ($event->tenant_id !== $tenantId) {
|
||||
return response()->json(['error' => 'Event not found'], 404);
|
||||
}
|
||||
|
||||
// Soft delete
|
||||
$event->delete();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Event deleted successfully',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk update event status (publish/unpublish)
|
||||
*/
|
||||
public function bulkUpdateStatus(Request $request): JsonResponse
|
||||
{
|
||||
$tenantId = $request->attributes->get('tenant_id');
|
||||
$validated = $request->validate([
|
||||
'event_ids' => 'required|array',
|
||||
'event_ids.*' => 'exists:events,id',
|
||||
'status' => 'required|in:draft,published,archived',
|
||||
]);
|
||||
|
||||
$updatedCount = Event::whereIn('id', $validated['event_ids'])
|
||||
->where('tenant_id', $tenantId)
|
||||
->update(['status' => $validated['status']]);
|
||||
|
||||
return response()->json([
|
||||
'message' => "{$updatedCount} events updated successfully",
|
||||
'updated_count' => $updatedCount,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique slug for event name
|
||||
*/
|
||||
private function generateUniqueSlug(string $name, int $tenantId, ?int $excludeId = null): string
|
||||
{
|
||||
$slug = Str::slug($name);
|
||||
$originalSlug = $slug;
|
||||
|
||||
$counter = 1;
|
||||
while (Event::where('slug', $slug)
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('id', '!=', $excludeId)
|
||||
->exists()) {
|
||||
$slug = $originalSlug . '-' . $counter;
|
||||
$counter++;
|
||||
}
|
||||
|
||||
return $slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search events by name or description
|
||||
*/
|
||||
public function search(Request $request): AnonymousResourceCollection
|
||||
{
|
||||
$tenantId = $request->attributes->get('tenant_id');
|
||||
$query = $request->get('q', '');
|
||||
|
||||
if (strlen($query) < 2) {
|
||||
return EventResource::collection(collect([]));
|
||||
}
|
||||
|
||||
$events = Event::where('tenant_id', $tenantId)
|
||||
->where(function ($q) use ($query) {
|
||||
$q->where('name', 'like', "%{$query}%")
|
||||
->orWhere('description', 'like', "%{$query}%");
|
||||
})
|
||||
->with('eventType')
|
||||
->limit(10)
|
||||
->get();
|
||||
|
||||
return EventResource::collection($events);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user