Trudny / egzaminacyjny · lekcja 24

Projekt 5: Mini portal społecznościowy — wpisy, komentarze i polubienia

Kolejna nazwana wprost w podstawie programowej (INF.04.7.3) funkcjonalność — portal społecznościowy. Ten projekt buduje uproszczony "feed" (strumień wpisów) z możliwością komentowania i polubień, wymagający zalogowania — dobre ćwiczenie z relacji WIELE-do-WIEL...

Zacznij tutaj

Po co się tego uczymy?

Kolejna nazwana wprost w podstawie programowej (INF.04.7.3) funkcjonalność — portal społecznościowy. Ten projekt buduje uproszczony "feed" (strumień wpisów) z możliwością komentowania i polubień, wymagający zalogowania — dobre ćwiczenie z relacji WIELE-do-WIELU (użytkownicy i polubienia) i z pokazywania danych POWIĄZANYCH z aktualnie zalogowanym użytkownikiem.

Wprowadzenie

Teoria

Wymagania projektu:

  • Feed — lista wpisów (tekst + opcjonalnie zdjęcie, upload plików z lekcji 18) od WSZYSTKICH użytkowników, posortowana od najnowszego;
  • Dodawanie własnego wpisu — WYMAGA zalogowania (ASP.NET Core Identity, lekcja 14);
  • Komentarze pod każdym wpisem — relacja jeden-do-wielu (jeden wpis, wiele komentarzy);
  • Polubienia — relacja WIELE-do-WIELU (jeden użytkownik może polubić wiele wpisów, jeden wpis może być polubiony przez wielu użytkowników) — z licznikiem i wizualnym oznaczeniem, czy AKTUALNIE zalogowany użytkownik już polubił dany wpis.

Relacja wiele-do-wielu — tabela pośrednicząca. W przeciwieństwie do relacji jeden-do-wielu z lekcji 17 (klucz obcy bezpośrednio w "słabszej" tabeli), relacja wiele-do-wielu wymaga OSOBNEJ tabeli pośredniczącej: Polubienie (WpisId, UzytkownikId) — każdy wiersz tej tabeli reprezentuje JEDNO polubienie KONKRETNEGO wpisu PRZEZ konkretnego użytkownika. Sprawdzenie "czy zalogowany użytkownik już polubił ten wpis" to zapytanie: context.Polubienia.AnyAsync(p => p.WpisId == wpisId && p.UzytkownikId == idAktualnegoUzytkownika).

Model danych:

public class Wpis { public int Id; public string Tresc; public string? SciezkaZdjecia;
                     public DateTime DataUtworzenia; public string AutorId;
                     public List<Komentarz> Komentarze; public List<Polubienie> Polubienia; }
public class Komentarz { public int Id; public string Tresc; public int WpisId;
                          public string AutorId; public DateTime DataUtworzenia; }
public class Polubienie { public int Id; public int WpisId; public string UzytkownikId; }

Pobieranie wpisów z LICZBĄ polubień i statusem "czy JA polubiłem". Zamiast dociągać CAŁĄ listę polubień dla każdego wpisu (co byłoby marnotrawstwem — potrzebujemy tylko liczby i jednego boola), backend może zwrócić OSOBNY, "spłaszczony" obiekt DTO (Data Transfer Object) zamiast surowego modelu EF Core: WpisDto { Id, Tresc, LiczbaPolubien, CzyJaPolubilem } — kontroler sam oblicza te wartości przez LINQ (.Count(), .Any()) PRZED wysłaniem odpowiedzi, zamiast przesyłać surowe dane i każąc Angularowi je przeliczać.

Zobacz zależności

Schemat

Wpis (1) ──── (N) Komentarz          Wpis (N) ──── (N) Uzytkownik
"Dzisiaj super pogoda!"  "Super!" - Ala           przez tabelę pośredniczącą:
                          "Zgadzam się" - Bartek   Polubienie (WpisId, UzytkownikId)

GET /api/wpisy  (wymaga [Authorize], żeby wiedzieć KTO pyta - CzyJaPolubilem)
        │
        ▼
ASP.NET Core:
  context.Wpisy
    .Select(w => new WpisDto {
        Id = w.Id, Tresc = w.Tresc,
        LiczbaPolubien = w.Polubienia.Count(),
        CzyJaPolubilem = w.Polubienia.Any(p => p.UzytkownikId == idAktualnegoUzytkownika)
    })
        │
        ▼
Angular: FeedComponent
  @for (wpis of wpisy; track wpis.id) {
    <app-wpis-karta [wpis]="wpis" (polubiono)="przelacz(wpis.id)">
  }

Połącz z praktyką

Przykład z życia

Facebook, Instagram, LinkedIn — wszystkie duże portale społecznościowe opierają się na tym samym fundamencie: wpisy, komentarze, polubienia (relacja wiele-do-wielu między użytkownikami a treścią) i personalizacja widoku pod KONKRETNEGO zalogowanego użytkownika. Ten projekt buduje maleńką, ale strukturalnie prawdziwą wersję tego mechanizmu.