currentTenant($request)->id; $query = Emotion::query() ->when(true, function ($builder) use ($tenantId) { // Prefer tenant-specific and global emotions if the column exists if (Schema::hasColumn('emotions', 'tenant_id')) { $builder->where(function ($inner) use ($tenantId) { $inner->whereNull('tenant_id') ->orWhere('tenant_id', $tenantId); }); } }) ->with('eventTypes'); if ($request->boolean('only_tenant')) { $query->where('tenant_id', $tenantId); } if ($request->boolean('only_global')) { $query->whereNull('tenant_id'); } $query->orderByRaw('tenant_id is null desc')->orderBy('sort_order')->orderBy('id'); $emotions = $query->paginate($request->integer('per_page', 50)); if ($emotions->isEmpty() && ! $request->boolean('only_tenant')) { // Fallback: return any emotions regardless of tenant to avoid empty selectors $fallback = Emotion::query() ->with('eventTypes') ->orderBy('sort_order') ->orderBy('id') ->paginate($request->integer('per_page', 50)); return EmotionResource::collection($fallback); } return EmotionResource::collection($emotions); } public function store(EmotionStoreRequest $request): JsonResponse { $data = $request->validated(); $tenantId = $this->currentTenant($request)->id; $payload = [ 'tenant_id' => $tenantId, 'name' => $this->localizeValue($data['name']), 'description' => $this->localizeValue($data['description'] ?? null, allowNull: true), 'icon' => $data['icon'] ?? 'lucide-smile', 'color' => $this->normalizeColor($data['color'] ?? '#6366f1'), 'sort_order' => $data['sort_order'] ?? 0, 'is_active' => $data['is_active'] ?? true, ]; $emotion = null; DB::transaction(function () use (&$emotion, $payload, $data) { $emotion = Emotion::create($payload); if (! empty($data['event_type_ids'])) { $emotion->eventTypes()->sync($data['event_type_ids']); } }); return response()->json([ 'message' => __('Emotion erfolgreich erstellt.'), 'data' => new EmotionResource($emotion->fresh('eventTypes')), ], 201); } public function update(EmotionUpdateRequest $request, Emotion $emotion): JsonResponse { $tenantId = $this->currentTenant($request)->id; if ($emotion->tenant_id && $emotion->tenant_id !== $tenantId) { abort(403, 'Emotion gehört nicht zu diesem Tenant.'); } if (is_null($emotion->tenant_id) && $request->hasAny(['name', 'description', 'icon', 'color', 'sort_order'])) { abort(403, 'Globale Emotions können nicht bearbeitet werden.'); } $data = $request->validated(); DB::transaction(function () use ($emotion, $data) { $update = []; if (array_key_exists('name', $data)) { $update['name'] = $this->localizeValue($data['name'], allowNull: false, fallback: $emotion->name); } if (array_key_exists('description', $data)) { $update['description'] = $this->localizeValue($data['description'], allowNull: true, fallback: $emotion->description); } if (array_key_exists('icon', $data)) { $update['icon'] = $data['icon'] ?? $emotion->icon; } if (array_key_exists('color', $data)) { $update['color'] = $this->normalizeColor($data['color'] ?? $emotion->color); } if (array_key_exists('sort_order', $data)) { $update['sort_order'] = $data['sort_order'] ?? 0; } if (array_key_exists('is_active', $data)) { $update['is_active'] = $data['is_active']; } if (! empty($update)) { $emotion->update($update); } if (array_key_exists('event_type_ids', $data)) { $emotion->eventTypes()->sync($data['event_type_ids'] ?? []); } }); return response()->json([ 'message' => __('Emotion aktualisiert.'), 'data' => new EmotionResource($emotion->fresh('eventTypes')), ]); } protected function localizeValue(mixed $value, bool $allowNull = false, ?array $fallback = null): ?array { if ($allowNull && ($value === null || $value === '')) { return null; } if (is_array($value)) { $filtered = array_filter($value, static fn ($text) => is_string($text) && $text !== ''); if (! empty($filtered)) { return $filtered; } return $allowNull ? null : ($fallback ?? []); } if (is_string($value) && $value !== '') { $locale = app()->getLocale() ?: 'de'; return [$locale => $value]; } return $allowNull ? null : $fallback; } protected function normalizeColor(string $color): string { $normalized = ltrim($color, '#'); if (strlen($normalized) === 6) { return '#'.strtolower($normalized); } return '#6366f1'; } protected function currentTenant(Request $request): Tenant { return TenantRequestResolver::resolve($request); } }