hasMany(Event::class, 'calendarid'); // FK in calendarobjects } /* ui-specific metadata (color, sharing flags, json settings) */ public function meta(): HasOne { return $this->hasOne(CalendarMeta::class, 'calendar_id'); } /* get instances */ public function instances() { return $this->hasMany(CalendarInstance::class, 'calendarid'); } /* get the primary? instance for a user */ public function instanceForUser(?User $user = null): CalendarInstance { $user = $user ?? auth()->user(); return $this->instances() ->where('principaluri', $user->principal_uri) ->first(); } /** * * build all calendar data for calendar display */ public function scopeDashboardForPrincipal(Builder $q, string $principal): Builder { return $q->select( 'calendars.id', 'ci.displayname', 'ci.calendarcolor', 'ci.uri as slug', 'ci.timezone as timezone', 'meta.color as meta_color', 'meta.color_fg as meta_color_fg', DB::raw('COALESCE(meta.is_remote, 0) as is_remote') ) ->join('calendarinstances as ci', 'ci.calendarid', '=', 'calendars.id') ->leftJoin('calendar_meta as meta', 'meta.calendar_id', '=', 'calendars.id') ->where('ci.principaluri', $principal) ->orderBy('ci.displayname'); } /** * * inbound urls * convert "/calendar/{slug}" into the correct calendar instance (uri column) * * @param mixed $value The URI segment (instance UUID). * @param string|null $field Ignored in our override. */ public function resolveRouteBinding($value, $field = null): mixed { $user = Auth::user(); return $this->newQuery() ->whereHas('instances', function (Builder $q) use ($value, $user) { $q->where('uri', $value) ->where('principaluri', $user->principal_uri); }) ->with(['instances' => function ($q) use ($user) { $q->where('principaluri', $user->principal_uri); }]) ->firstOrFail(); } /** * outbound urls */ public function getRouteKey(): string { // use the per-user instance slug for URLs; fall back to id if not available $instance = $this->instances() ->where('principaluri', Auth::user()->principal_uri) ->first(); return $instance->uri ?? (string) $this->getKey(); } }