'boolean', 'is_private' => 'boolean', 'start_at' => 'datetime', 'end_at' => 'datetime', 'extra' => 'array', ]; /** * convenience wrapper that mimics an “UPSERT” for meta rows. * * @param int $eventId calendarobjects.id * @param array $attrs keyed the same way you’d pass to updateOrCreate * @return static */ public static function upsertForEvent(int $eventId, array $attrs): self { $values = array_merge($attrs, ['event_id' => $eventId]); return static::updateOrCreate( ['event_id' => $eventId], // lookup $values // insert/update ); } /** * relationships */ /* back-reference to Sabre’s calendarobjects row */ public function event(): BelongsTo { return $this->belongsTo(Event::class, 'event_id'); } /* back-reference to location record; calling this venue so that it doesn't shadow the "location" column */ public function venue(): BelongsTo { return $this->belongsTo(Location::class, 'location_id'); } /* convenient, safe label to use in views */ public function getLocationLabelAttribute(): string { // prefer normalized place name; otherwise fall back to the raw string column return $this->venue?->short_label ?? ($this->location ?? ''); } }