Favoriten
Das Favoriten-System erlaubt es Usern, Profile plattformuebergreifend als Favorit zu markieren. Favoriten werden global gespeichert und zusaetzlich in einen dedizierten "Favoriten"-Workspace gespiegelt.
Datenmodell
| Model | Tabelle | Beschreibung |
|---|---|---|
FavoriteSocialProfile | favorite_social_profiles | Globaler Favorit-Marker (user_id + social_profile_id) |
Workspace (Name: "Favoriten") | workspaces | Dedizierter Workspace pro User |
Location: app/Models/FavoriteSocialProfile.php, app/Services/Favorites/FavoriteManager.php
FavoriteManager
Der FavoriteManager ist der zentrale Service fuer alle Favoriten-Operationen:
favorite(User $user, SocialProfile $profile, ?Watcher $sourceWatcher = null)
- Erstellt einen
FavoriteSocialProfile-Eintrag (idempotent viafirstOrCreate) - Erstellt oder findet den "Favoriten"-Workspace (
getOrCreateFavoritesWorkspace()) - Prueft, ob das Profil bereits im Favoriten-Workspace existiert
- Erstellt
Watcher+WatcherSourceim Favoriten-Workspace
$watcher = Watcher::create([
'workspace_id' => $favoritesWorkspace->id,
'created_by' => $user->id,
'name' => $sourceWatcher?->name ?? $profile->title ?? $profile->handle,
'is_name_custom' => false,
]);
unfavorite(User $user, SocialProfile $profile)
- Loescht den
FavoriteSocialProfile-Eintrag - Findet den Favoriten-Workspace
- Loescht zugehoerige
WatcherSourceundWatcheraus dem Favoriten-Workspace
Beide Methoden laufen in einer DB-Transaction.
getOrCreateFavoritesWorkspace(User $user)
Erstellt oder findet den dedizierten Workspace:
public const FAVORITES_WORKSPACE_NAME = 'Favoriten';
return Workspace::query()->firstOrCreate(
['owner_id' => $user->id, 'name' => self::FAVORITES_WORKSPACE_NAME],
['description' => null]
);
isFavorite(User $user, SocialProfile $profile)
Prueft, ob ein Profil vom User favorisiert ist (einfacher exists()-Check).
isFavoritesWorkspace(Workspace $workspace)
Identifiziert den Favoriten-Workspace anhand des kanonischen Namens.
Location: app/Services/Favorites/FavoriteManager.php
Watcher + WatcherSource Spiegelung
Der Favoriten-Workspace verhaelt sich wie ein normaler Workspace. Jeder Favorit erzeugt einen vollstaendigen Watcher mit WatcherSource. Das bedeutet:
- Favoriten erscheinen in der normalen Watcher-Liste
- Dashboard-Metriken gelten auch fuer den Favoriten-Workspace
- Alle Features (Charts, Related, Score) funktionieren identisch
Delete-Verhalten
Watcher im Favoriten-Workspace loeschen
Wenn ein Watcher innerhalb des Favoriten-Workspaces geloescht wird, wird automatisch auch der Favorit-Marker entfernt:
if ($user && $favorites->isFavoritesWorkspace($workspace)) {
foreach ($sources as $source) {
if ($source->profile) {
$favorites->unfavorite($user, $source->profile);
}
}
}
Location: app/Livewire/Watchers/Index.php (Methode delete()), app/Livewire/Watchers/Show.php (Methode deleteWatcher())
Unfavorite ueber Star-Toggle
Beim Entfernen des Favorits ueber den Star-Button wird der Watcher im Favoriten-Workspace automatisch mit entfernt (ueber FavoriteManager::unfavorite()).
UI: Star-Icons
Star-Icons erscheinen an drei Stellen:
Watcher-Liste (Watchers\Index)
Kleine Stern-Icons neben Watchern, deren primaeres Profil favorisiert ist. Die Favoriten-IDs werden in einer einzelnen Query geladen:
$favoriteProfileIds = FavoriteSocialProfile::query()
->where('user_id', $user->id)
->pluck('social_profile_id')
->all();
Location: resources/views/livewire/watchers/index.blade.php
Dashboard (Dashboard\Index)
Favoriten-Rows enthalten das is_favorite-Flag fuer Star-Rendering. Zusaetzlich gibt es einen Favoriten-Filter (all / favorites), der zur Laufzeit auf die Snapshot-Daten angewendet wird.
Location: app/Livewire/Dashboard/Index.php, resources/views/livewire/dashboard/index.blade.php
Watcher-Detail (Watchers\Show)
Star-Toggle neben dem Profil-Handle. Toggle-Aktion ueber toggleFavorite():
public function toggleFavorite(int $sourceId, FavoriteManager $favorites): void
{
// Reload source to avoid stale relations
$source = WatcherSource::query()
->whereKey($sourceId)
->where('watcher_id', $this->watcher->id)
->with('profile')
->first();
if ($favorites->isFavorite($user, $profile)) {
$favorites->unfavorite($user, $profile);
} else {
$favorites->favorite($user, $profile, $this->watcher);
}
// Reload favorites for immediate UI feedback
$this->favoriteProfileIds = $this->loadFavoriteProfileIds();
}
Location: app/Livewire/Watchers/Show.php
Workspace-Schutz
Der Favoriten-Workspace hat besondere Einschraenkungen:
- Kein Import: Multi-Import in den Favoriten-Workspace ist gesperrt
- Kein Move-Ziel: Watcher koennen nicht in den Favoriten-Workspace verschoben werden
- Nur Favoriten: Neue Eintraege entstehen ausschliesslich ueber
FavoriteManager::favorite()
if ($workspace->name === FavoriteManager::FAVORITES_WORKSPACE_NAME) {
$this->addError($errorKey, 'Import in den Favoriten-Workspace ist nicht moeglich.');
return;
}
Daily Scrape Priority
Favorisierte Profile koennen bei der taeglichen Datenaktualisierung priorisiert werden, damit ihre Metriken stets aktuell sind.
Tests
Die Favoriten-Logik wird durch dedizierte Tests abgedeckt:
php artisan test --filter=WatcherFavoritesTest
Getestete Szenarien:
- Favorisieren und Unfavorisieren
- Automatisches Entfernen beim Loeschen aus dem Favoriten-Workspace
- Toggle-Cleanup bei Watcher-Delete
is_favorite-Flag im Dashboard
Location: tests/Feature/Watchers/WatcherFavoritesTest.php, tests/Feature/DashboardRollupTest.php