Fügen Sie einen Kommentar Systemteil hinzu (Deutsch (German))

Fügen Sie einen Kommentar Systemteil hinzu

Monday, 26 August 2024

//

6 minute read

Einleitung

Einer der wichtigsten Aspekte einer Blogging-Website wie dies ist ein Kommentar-System...So...,.Ich habe beschlossen, eine zu bauen.MSC2.Dies ist der erste Teil einer Reihe von Beiträgen auf, wie man ein Kommentare-System baut...In diesem Beitrag werde ich die Datenbank einrichten.MSZ5.

Einrichtung der Datenbank

Wie mit dem Rest der Website I'-M mit Postgres für die Blogs.;-m mit rekursiven Abfragen beginnen, um die Kommentare zu speichern, .-Ich mag dieses intensiv nicht, aber es bringt uns aus dem Tor mit einem gefädelten Kommentarsystem.MSC5- und ich habe viel Verkehr, so dass es nicht eine große Sache ist ?).-I'-m bewegen sich in ein effizienteres System in der Zukunft....-

Der EF-Kontext

Im aktuellen System verwenden wir ein Kommentar-Schloss, um die Beziehungen zwischen den Kommentaren zu definieren, damit ich Threaded comments unterstützen kann.

classDiagram
    class CommentEntity {
        int Id
        CommentStatus Status
        string Author
        string? HtmlContent
        string Content
        DateTime CreatedAt
        int PostId
        int? ParentCommentId
    }

    class BlogPostEntity {
        int Id
        string Title
        string Content
        DateTime CreatedAt
    }

    class CommentClosure {
        int AncestorId
        int DescendantId
        int Depth
    }

    CommentEntity "1" --> "1" BlogPostEntity : belongs to
    CommentEntity "1" --> "0..1" CommentEntity : parent
    CommentEntity "0..*" --> "0..*" CommentClosure : Ancestors
    CommentEntity "0..*" --> "0..*" CommentClosure : Descendants
    CommentClosure "1" --> "1" CommentEntity : Ancestor
    CommentClosure "1" --> "1" CommentEntity : Descendant

    class CommentStatus {
        <<enum>>
        Pending
        Approved
        Rejected
        Deleted
    }

Sie können hier sehen, dass jeder CommentEntity hat eine PostId für den Pfosten, der an , und a befestigt ist ParentCommentId für den Kommentar, der zu ( angebracht wird CommentClosure Tabelle wird verwendet, um die Beziehungen zwischen Kommentaren zu speichern.

Der Kommentardienst

Das CommentService ist verantwortlich für die Verwaltung von Kommentaren Es hat Methoden, um hinzuzufügen: ,. Genehmigung: ,. Ablehnung:,. Löschen: Kommentare Es hat auch Methoden, Kommentare für einen Beitrag zu erhalten: ,.

Unsere Schnittstelle für diesen Service sieht so aus

public interface ICommentService
{
    Task<string> Add( int postId, int? parentCommentId, string author, string content);
    Task<List<CommentEntity>> GetForPost(int blogPostId, int page = 1, int pageSize = 10, int? maxDepth = null, CommentStatus? status = null);
    Task<List<CommentEntity>> GetDescendants(int commentId, int maxDepth = 0);

     Task<CommentEntity> Get(int commentId);
    Task<List<CommentEntity>> GetAncestors(int commentId);
    Task Delete(int commentId);
    Task Reject(int commentId);
    Task Approve(int commentId);
}

Die Methode Hinzufügen

Unsere Add-Methode fasst die Struktur dieses Kommentarsystems zusammen

Es beginnt zuerst eine neue Transaktion für jene nicht vertrauten Transaktionen sind ein Weg, um sicherzustellen, dass eine Gruppe von Operationen als eine einzige Einheit der Arbeit behandelt werden . . . . Wenn einer der Operationen fehlschlägt . , . die Transaktion kann zurück gerollt werden ? , . und alle Änderungen sind unone .. . Dies ist wichtig, wenn Sie mehrere Operationen haben, die zusammen durchgeführt werden müssen .

Es parsiert dann den Kommentartext von Markdown nach HTML mit der Markdig-Bibliothek.Dies ist ein einfacher Weg, um Benutzern zu erlauben, ihre Kommentare mit Markdown zu formatieren.

Als nächstes erstellt es das Entity und speichert, dass zu der Datenbank

Dies wird in zwei Schritten getan, zuerst wird der Kommentar gespeichert, dann wird die Kommentierungs-Schließung gespeichert, und zwar deshalb, weil der Kommentar abgespeichert werden muss, um den im closure benutzten Id., zu generieren.

Als nächstes bauen wir die Hierarchie von CommentClosures welche die Beziehungen zwischen den Kommentaren definieren, wir beginnen mit einem selbstbestimmenden Abschlusseintrag, wenn es einen Elternkommentar gibt, holen wir alle Vorfahren des Mutterkommentars und fügen sie dem neuen Kommentar hinzu.

Schließlich übertragen wir die Transaktion... Wenn eine der Operationen fehlschlägt, wird die Transaktion zurückgerollt.

 public async Task<string> Add(int postId, int? parentCommentId, string author, string content)
  {
      await using var transaction = await context.Database.BeginTransactionAsync();
      try
      {
         var html = Markdig.Markdown.ToHtml(content);
          // Create the new comment
          var newComment = new CommentEntity()
          {
              HtmlContent = html,
              Content = content,
              CreatedAt = DateTime.UtcNow,
              PostId = postId,
              Author = author,
              Status = CommentStatus.Pending,
              ParentCommentId = parentCommentId
          };
            
          context.Comments.Add(newComment);
          await context.SaveChangesAsync();
          logger.LogInformation("Saved comment to DB");// Save to generate the new comment's Id

          // Insert into CommentClosure table
          var commentClosures = new List<CommentClosure>();

          // Self-referencing closure entry
          commentClosures.Add(new CommentClosure
          {
              AncestorId = newComment.Id,
              DescendantId = newComment.Id,
              Depth = 0
          });

          // If there is a parent comment, insert the ancestor relationships
          if (parentCommentId.HasValue)
          {
              // Fetch all ancestors of the parent comment
              var parentAncestors = await context.CommentClosures
                  .Where(cc => cc.DescendantId == parentCommentId.Value)
                  .ToListAsync();

              // Add ancestor relationships for the new comment
              foreach (var ancestor in parentAncestors)
              {
                  commentClosures.Add(new CommentClosure
                  {
                      AncestorId = ancestor.AncestorId,
                      DescendantId = newComment.Id,
                      Depth = ancestor.Depth + 1
                  });
              }

              // Add a direct parent-child relationship
              commentClosures.Add(new CommentClosure
              {
                  AncestorId = parentCommentId.Value,
                  DescendantId = newComment.Id,
                  Depth = 1
              });
          }

          context.CommentClosures.AddRange(commentClosures);
          await context.SaveChangesAsync();
          logger.LogInformation("Saved comment closure to DB");

          // Commit transaction
          await transaction.CommitAsync();
          return html;
      }
      catch (Exception e)
      {
          // Rollback transaction in case of failure
          await transaction.RollbackAsync();
          logger.LogError(e, "Failed to save comment to DB");
      }

      return string.Empty;
  }

Die GetForPost-Methode

Wir werden nicht die ganze Sache abdecken, aber Add und Get sind unsere wichtigsten CRUD-Operationen für diesen Service

Wie Sie sehen können, haben wir einen Paging basierend auf der Top-Level Kommentar maxDepth Parameter, der uns erlaubt, die Tiefe des Kommentarbaums zu begrenzen Dieses ist nützlich, wenn wir nur die top-Kommentare auf - zeigen möchten oder wenn wir die Tiefe der Baum beschränken wollen, um die Leistung zu verbessern.

Unfortunately mit diesem rekursiven Ansatz müssen wir den Tiefenfilter anwenden, nachdem wir die Kommentare erhalten haben.,, da wir es in der Abfrage tun können.MSC3, weil die Tiefe eines Kommentars durch die Anzahl der Vorfahren bestimmt wird, die er hat.,, und wir können das einfach in SQLC6 abfragen.

  public async Task<List<CommentEntity>> GetForPost(int blogPostId, int page = 1, int pageSize = 10, int? maxDepth = null, CommentStatus? status = null)
  {
      // Step 1: Query the top-level comments for the specified blog post
      var query = context.Comments
          .Where(c => c.PostId == blogPostId)
          .OrderByDescending(c => c.CreatedAt)
          .Skip((page - 1) * pageSize)
          .Take(pageSize);

      // Step 2: Filter by status if provided
      if (status.HasValue)
      {
          query = query.Where(c => c.Status == status.Value);
      }

      var topLevelComments = await query
          .Include(c => c.ParentComment)
          .Include(d=>d.Descendants)
          .ToListAsync();

      // Step 4: Filter descendants based on the maxDepth
      foreach (var comment in topLevelComments)
      {
          if (maxDepth != null)
          {
              FilterDescendantsByDepth(comment, 0, maxDepth.Value);
          }
      }

      return topLevelComments;
  }

// Recursive helper method to limit the descendants based on the specified depth
  private void FilterDescendantsByDepth(CommentEntity comment, int currentDepth, int maxDepth)
  {
      if (currentDepth >= maxDepth)
      {
          // If the max depth is reached or there are no descendants, stop recursion
          comment.Descendants = new List<CommentClosure>();  // Clear further descendants beyond maxDepth
          return;
      }

      foreach (var closure in comment.Descendants.ToList())  // Iterate over a copy to prevent modification during iteration
      {
          FilterDescendantsByDepth(closure.Descendant, currentDepth + 1, maxDepth);
      }
  }

Schlussfolgerung

Dies ist ein einfaches Threaded-Kommentarsystem, das rekursive Abfragen verwendet, um die Beziehungen zwischen den Kommentaren zu speichern...Es ist nicht das effizienteste System, sondern es ist einfach und es funktioniert...In der Zukunft decken ,.I'.ll die Frontend-Aspekte dieses Systems ab.MSC7. viele HTMX-,.

Bis dahin fühlen Sie sich frei, einen Kommentar unten zu hinterlassen.

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.