• Hallo liebe Userinnen und User,

    nach bereits längeren Planungen und Vorbereitungen sind wir nun von vBulletin auf Xenforo umgestiegen. Die Umstellung musste leider aufgrund der Serverprobleme der letzten Tage notgedrungen vorverlegt werden. Das neue Forum ist soweit voll funktionsfähig, allerdings sind noch nicht alle der gewohnten Funktionen vorhanden. Nach Möglichkeit werden wir sie in den nächsten Wochen nachrüsten. Dafür sollte es nun einige der Probleme lösen, die wir in den letzten Tagen, Wochen und Monaten hatten. Auch der Server ist nun potenter als bei unserem alten Hoster, wodurch wir nun langfristig den Tank mit Bytes vollgetankt haben.

    Anfangs mag die neue Boardsoftware etwas ungewohnt sein, aber man findet sich recht schnell ein. Wir wissen, dass ihr alle Gewohnheitstiere seid, aber gebt dem neuen Board eine Chance.
    Sollte etwas der neuen oder auch gewohnten Funktionen unklar sein, könnt ihr den "Wo issn da der Button zu"-Thread im Feedback nutzen. Bugs meldet ihr bitte im Bugtracker, es wird sicher welche geben die uns noch nicht aufgefallen sind. Ich werde das dann versuchen, halbwegs im Startbeitrag übersichtlich zu halten, was an Arbeit noch aussteht.

    Neu ist, dass die Boardsoftware deutlich besser für Mobiltelefone und diverse Endgeräte geeignet ist und nun auch im mobilen Style alle Funktionen verfügbar sind. Am Desktop findet ihr oben rechts sowohl den Umschalter zwischen hellem und dunklem Style. Am Handy ist der Hell-/Dunkelschalter am Ende der Seite. Damit sollte zukünftig jeder sein Board so konfigurieren können, wie es ihm am liebsten ist.


    Die restlichen Funktionen sollten eigentlich soweit wie gewohnt funktionieren. Einfach mal ein wenig damit spielen oder bei Unklarheiten im Thread nachfragen. Viel Spaß im ngb 2.0.

php Auf Kommentar Antworten

Diskordier

Neu angemeldet

Registriert
14 Juli 2013
Beiträge
161
Ich hab das zwar schon bei einem anderen Post angeschnitten, jedoch möchte ich doch einen neuen Post eröffnen da es beim alten Post ursprünglich um was anderes ging , ich hoffe das ist Okay so :) ?


Wie gesagt ich möchte zu einer bestehenden Kommentarfunktion eine Button einbauen wo ein user auf einen schon bestehenden Kommentar antworten kann.

Aber irgendwie werde ich nicht ganz schlau daraus wie ich das anstelle.

Wenn ich die db so erstelle kann ich immer nur einem subcommentschreiben ich muss ja wissen das der subcomment zu folgender Commentid gehört und die kann ich ja nicht mehrmals verwenden da die ja automatisch immer hochgezählt wird.

Bis jetzt sieht es so aus, mir ist noch nicht klar wie ich mehrere sub comments erstellen kann ohne die commentid zu manipulieren, so kann ich höchstens eine machen.

Wie ich es Grafisch umsetze ist mir auch noch nicht ganz klar aber das ers tmal nicht so wichtig :)

subcommentbild.JPG

subcomment beginn.JPG

[src=html5]foreach($comments as $comment){ ?>
<article id="comment-<?=$comment['commentID']?>">
<section class="meta border-red">
<?=include_avatar($comment)?>
<span class="author"><?=$comment['username']?></span>
<time datetime="<?=date('c', strtotime($comment['dateCreated']))?>"><?=date(DATE_HUMAN, strtotime($comment['dateCreated']))?></time>
</section>
<section class="message">
<div class="inset-shadow">
<div class="super-bar border-red"></div>
<p>
<?=bbc2html(nl2br($comment['message']), 'url')?>
</p>
<!--Antwortbutton-->
<a class="acp-link" href="<?=WEB_ROOT?>index.php?site=comment&action=subcomment&animeid=<?=$animeID?>&commentid=<?=$comment['commentID']?>">[Auf Kommentar Antwort]</a>[/src]

[src=php]/SUBcomment erstelllen
case 'subcomment':
$h2title = ' Auf Kommentar Antworten';
$submitvalue = 'Bearbeiten!';
$row = $_POST;

//Subcomment eintragen
case 'Subcomment update':
$message = htmlspecialchars($_POST['form-message']);

$fields = '`userID`, `animeID`, `subcommentid`, `submessage`';
$values = "$userID, $animeID, $subcommentid 1+COUNT, :m";
//todo insert into subcomment
$qry = $sql->prepare("Update `anmi1_comment `commentid`($fields) VALUES($values)");
$qry->bindValue('m', $message, PDO::PARAM_STR);
$qry->execute();
$h2title = 'Inserting';

header("refresh: ".FORWARD_TIME."; index.php?site=streams&animeid=$animeID");
$message = '<h3>Eintrag erfolgreich!</h3><p class="message"><a href="'.WEB_ROOT.'index.php?site=streams&animeid='.$animeID.'">Du wirst in '.FORWARD_TIME.' Sekunden weitergeleitet...</a></p>';
break;[/src]

[src=html5]<div class="wrapper">
<div id="comment-container" class="<?=($isAjax == True) ? 'ajax' : ''?>">

<h2><?=$h2title?></h2>
<?php
if(isset($submitvalue)){
?>
<form id="comment-form" method="POST" action="index.php?site=comment&action=update&animeid=<?=$animeID?>">
<label for="form-message">Kommentar</label>
<br>
<textarea id="form-message" name="form-message" placeholder="Deine Nachricht"><?=@$row['form-message']?></textarea>
<br>
<input type="submit" value="<?=$submitvalue?>">
</form>
<?php
} else {
echo "$message";
}
?>

</div>
</div>[/src]

Achtung der Code ist noch nciht fertg also nicht das der bei euch nun Fehlerfrei ist.
 

Jester

★★★★☆ (Kasparski)

Registriert
1 Dez. 2014
Beiträge
6.066
Ort
Code Azure
Grundsätzlich würde ich das über ein zusätzliches Feld PID (Parent-ID) lösen. Ist es ein Eintrag auf der obersten Ebene, wird eine 0 oder NULL eingetragen, ansonsten die ID des Parent Comments.

Zwar nimmst Du damit eine kleine Redundanz in Kauf (Speicherung der Comment ID, obwohl die schon im Elternkommentar drin steckt und die Zuweisung über die PID eindeutig ist), aber deswegen nun die Tabellen zu teilen, halte ich für "mit Kanonen auf Spatzen geschossen" :)

J.
 

Shodan

runs on biochips

Registriert
14 Juli 2013
Beiträge
661
Ort
Citadel Station
Eine Slideshow über hierarchische Strukturen in SQL:
http://www.slideshare.net/billkarwin/models-for-hierarchical-data

Und @Jesper: wie ich im letzten Topic schon geschrieben habe: Die Daten rekursiv, also in O(n) auszulesen, ist ein Dolchstoß für die Performance!

Wichtig ist vor allem die Frage, ob du Kommentare löschen oder verschieben können willst und was dann mit deren Kindern passieren soll.
Ohne solche Funktionen kann man auch eine nicht normalisierte Datenbankstruktur nutzen, weil die betreffenden Anomalien nicht auftreten.
Wenn du nicht sicher weißt, ob du das brauchen wirst, oder ausreichend Zeit hast und nebenbei noch lernen willst, wie man es ordentlich macht... dann mach es gleich ordentlich ;-)

Kapsel die Datenbankzugriffe objektorientiert in einen Data-Access-Layer, z.B. unter Einsatz des Table-Gateway Patterns und benutze die Closure-Table Struktur aus der Slideshow.
 

Jester

★★★★☆ (Kasparski)

Registriert
1 Dez. 2014
Beiträge
6.066
Ort
Code Azure
@Shodan:
Klar hast Du Recht, aber da die redundante Info der TID (Thread ID) vorliegt, wird das rekursive Durchsuchen überschaubar, oder nicht? Es muss ja nicht die ganze Tabelle durchsucht werden, sondern nur die zum Thread gehörigen Comments.

J.

Nachtrag: außerdem soll das ja nicht über Abfragen laufen, sondern man füllt sich ein Array mit den zum Thread gehörigen Comments und arbeitet damit. Das sollte performant genug sein.
 
Zuletzt bearbeitet:

Diskordier

Neu angemeldet

Registriert
14 Juli 2013
Beiträge
161
  • Thread Starter Thread Starter
  • #5
Hei ho, Also die Kommentare können schon gelöscht werden jedoch nur von user mit den entsprechenden rechten dafür. Aber ja wenn ich nun den Hauptcomment lösche müssen ja auch alle subcomments gelöscht werden. ist schon noch eine verzwickte Sache.

hmmm ich hab mir sein bsp angeschaut , da es ja einen Hautcomment gibt an denen dann die subcomments mit jeweils neues commentiD angehängt werden ist die Zuweiseung ja nicht so ne sache. Aber wie funktioniert das mit mehreren hauptcomments mit subcomments wie ordnet er das welcher sbcomment zu welchem hautcomment gehört zu. ich müsse glaub dem hautpcomment nebst der Commentid noch eine weitere ID zuweisen die nicht autoincrementet ist aber sich bei jedem hautcomment hochzählt. Das heisst ich müsste über den Button die information der db geben das es sich um einen subcomment handelt wo er die gleiche ID reinschreiben. dann die subcommentid die auch bei jedem subcomment hochgezählt wird. anhand diese rinfo und datum kann ich dann zuweisen welche rhauptcomment welchen subcomment an welcherstelle. Jedoch löse ich damit das einfache problem. Nicht aber das verhalten wenn ich auf subcomment einen subcomment schreibe. Danach folgt ja noch das problem vom löschen :D

Ach das alles Privat, bin da an keine Zeit gebunden :) Und ich bin kein php hirsch :D Hab mir das alles mehr oder weniger selber beigebracht.

Jedenfalls tausend dank für eure Hilfe, finde es so cool das Ihr mir helft auch wenn keiner was dabei verdient. *_*
Arigato
 

virtus

Gehasst

Registriert
24 Apr. 2015
Beiträge
1.689
Ort
AUF DEM MOND
Wozu einen neuen Thread?

Nein, du hast das falsch verstanden: Ein Kommentar soll nicht (unmittelbar) die ID seines Subkommentars kennen, sondern umgekehrt, der Subkommentar soll die ID seines Elternkommentars kennen. Damit machst du es möglich, dass ein Elternkommentar mehrere Kindkommentare hat (die ggf. wieder Kindkommentare haben).
Dann können auch mehrere Kommentare den gleichen Elternkommentar besitzen.

[src=text]
comments
-------------
commentID animeID userID message dateModified dateCreated parentCommentID
61 1 1 "Toll!" 2015-05-01 ... null
62 1 2 "Ist x?" 2015-05-01 ... 61
63 1 1 "Ja." 2015-05-01 ... 62[/src]


User 1 schreibt einen Kommentar zu einem Beitrag.
User 2 stellt bezüglich des Kommentars von 1 eine Frage.
User 1 beantwortet den Kommentar von User 2.


commentID ist int(225) primary mit auto_increment
animeID ist FOREIGN KEY zu der Key-Spalte der Tabelle in der du die Animes speicherst.
userID ist FOREIGN KEY zu der Key-Spalte der Tabelle in der du die Nutzer speicherst.
message ist TEXT
dateModified und dateCreated sind TIMESTAMP mit default NOW() bzw CURRENT_TIMESTAMP
parentCommentID ist FOREIGN KEY zu `comments`.commentID


Wenn du einen neuen Kommentar einträgst ist das:
INSERT INTO `comments` (commentID, animeID, userID, message, dateModified dateCreated parentCommentID) VALUES (null, aid, uid, "Text", NOW(), NOW(), null)

Wenn du einen neuen Kommentar hinzufügst, der einem Elternkommentar zugeordnet werden soll:
INSERT INTO `comments` (commentID, animeID, userID, message, dateModified dateCreated parentCommentID) VALUES (null, aid, uid, "Text", NOW(), NOW(), parentID)

Root-Kommentare:
SELECT * FROM `comments` WHERE `animeID` = aid AND `parentCommentID` IS NULL ORDER BY `commentID` ASC

Die "Kindkommentare" eines Kommentars mit der id $id erhältst du so:
SELECT * FROM `comments` WHERE `parentCommentID` = $id


Die Abfrage musst du allerdings rekursiv formulieren, dass du zu jedem Kommentar all seine Kind-(und Kindeskind-)kommentare bekommst.



Das Löschen ist trivial. Wenn du erst mal FOREIGN KEYs definiert hast, kannst du UPDATE bzw DELETE propagieren: ON DELETE CASCADE
Sprich: Wird ein referenzierter Eintrag gelöscht, so auch man selbst.


CREATE `comments` (
commentID int(225) PRIMARY
animeID int(225) FOREIGN KEY references `animes`.`id`
userID int(225) FOREIGN KEY references `users`.`id`
message TEXT
dateModified TIMESTAMP
dateCreated TIMESTAMP
parentCommentID int(225) FOREIGN KEY references `comments`.`commentID` ON DELETE CASCADE
)


Wird in der obigen Tabelle also commentID 61 gelöscht, dann werden 62 und 63 ebenfalls gelöscht.
Du kannst dir das so vorstellen, dass der Eintrag 62 durch die "ON DELETE CASCADE" Anweisung permanent auf den Eintrag mit der ID 61 schaut. Sobald der Eintrag 61 gelöscht wird, löscht sich auch die 61. Da die 63 aber auf den Eintrag 62 schaut (gleicher Hintergrund wie bei 62), wird sich 63 ebenfalls löschen, weil sein Elternknoten verschwunden ist.
 
Zuletzt bearbeitet:

Diskordier

Neu angemeldet

Registriert
14 Juli 2013
Beiträge
161
  • Thread Starter Thread Starter
  • #7
Vielen vielen Danke dann mache ich mich doch gleich mal ans Werk ^^
 

Shodan

runs on biochips

Registriert
14 Juli 2013
Beiträge
661
Ort
Citadel Station
Root-Kommentare:
SELECT * FROM `comments` WHERE `animeID` = aid AND `parentCommentID` IS NULL ORDER BY `commentID` ASC

Die "Kindkommentare" eines Kommentars mit der id $id erhältst du so:
SELECT * FROM `comments` WHERE `parentCommentID` = $id


Die Abfrage musst du allerdings rekursiv formulieren, dass du zu jedem Kommentar all seine Kind-(und Kindeskind-)kommentare bekommst.

Oder einfach nur
SELECT * FROM `comments` WHERE `animeID` = aid ORDER BY `commentID` ASC

und dann in PHP den Baum hochziehen:
mir war grad langweilig
Code ist natürlich, wie immer, blind geschrieben und könnte fehlerhaft sein.
[src=php]/**
* Represents a row of the comments table
**/
class Comment {
public $commentID;
public $animeID;
...
/** @var Comments[] **/
public $childs;

/**
* Create a HTML representation of this comment
**/
function render($comment) {
try {
ob_start();
include('templates/comment.php')
return ob_get_clean();
} catch (\Exception $ex) {
ob_end_clean();
throw $ex;
}
}
}[/src]

[src=php]/**
* Gateway for the comments table
**/
class CommentTable() {
protected $sql;
protected $comments;

public function __construct($sql) {
$this->sql = $sql;
$this->comments = [];
}

/**
* Fetch all comments for an animeId from the database as a list
* Note that their child arrays are empty.
* @param integer $animeId
* @param mysqli $sql
* @return Comment[]
**/
public function fetchByAnime($id) {
$id = (integer) $id;

if (isset($this->comments[$id])) {
return $this->comments[$id];
}

$comments = [];
$result = $this->sql->query("SELECT * FROM comments WHERE animeId = '$id' ORDER BY id");
if (!$result) {
throw new ErrorException('SQL-Error: '. $this->sql->error);
}
while ($c = $result->fetch_object('Comment')) {
$c->childs = [];
$comments[$c->id] = $c;
}

return $this->comments[$animeId] = $comments;
}

/**
* create a list od trees from a list of comments using {@link Comment::parent}
* @param Comment[] $comments one dimensional list
* @return Comment[] list of root comments with their childs set
**/
protected function makeTreesNotWar($comments) {
$roots = [];
foreach($comments as $c) {
if (isset($c->parent)) {
// fixme: virtus called them parentCommentID and commentID
$comments[$c->parent]->childs[$c->id] = $c;
} else {
$roots[$c->id] = $c;
}
}
return $roots;
}

/**
* Fetch all comments for an animeId from the database as a list of trees
* @param integer $animeId
* @return Comment[]
**/
public function fetchTreesByAnime($id) {
return $this->makeTreesNotWar($this->fetchByAnime($id));
}
}[/src]

comment.phtml:
[src=php]<?php /** @var Comment $this **/ ?>
<div class="comment">
<div class="title"><?= htmlentities($this->title); ?></div>
...
<?php foreach($this->childs as $child) { ?>
<div class='child'><?= $child->render(); ?></div>
?>
...
</div>
[/src]

[src=php]/** @var integer $animeId **/
$animeId = ... ;
/** @var Comments[] $comments**/
$comments = [];

try {
$sql = ... ;
$table = new CommentTable($sql);
$comments = $table->fetchTrees($animeId)
} finally {
unset($table);
$sql->close();
unset($sql);
}

...

foreach($comments as $c) {
echo $c->render();
}
[/src]

inb4 prepared statements..
 

virtus

Gehasst

Registriert
24 Apr. 2015
Beiträge
1.689
Ort
AUF DEM MOND
In PHP den Baum aufbauen geht natürlich ebenfalls, allerdings ist das normal typische Aufgaben, die man auf die Datenbank abwälzt, sofern möglich.
 

Shodan

runs on biochips

Registriert
14 Juli 2013
Beiträge
661
Ort
Citadel Station
Und wie stellst du dir das vor, lieber virtus? Eine SQL Anfrage liefert eine zweidimensionales Struktur: Spalten und Zeilen.
Das Datenbank-Modell sollte natürlich für das Datenmodell sinnvoll strukturiert sein. Bilden die Daten einen Baum, dann ist nur sinnvoll, wenn auch die Datenbank das abbildet. Aber SQL kann keine Baum-Strukur ausgeben. Der Baum muss immer PHP hochgezogen werden.

Mir ging es darum, dass ich, wenn ich einen Baum aus n Knoten aufbaue, ich lieber Θ(1) SQL Abfragen mache und mit einem Θ(n) PHP-Algorithmus den Baum hochziehe, statt einen PHP-Algorithmus Θ(n) zu verwenden, der O(n) Datenbankabfragen macht.

Lass es mich so formulieren: Datenbankabfragen sind teuer. Rekursiv Datenbankabfragen zu machen ist schlicht nicht effizient. (Nicht verwechseln mit rekursiven Abfragen, ganz andere Baustelle).
Der einzige Fall, in dem es interessant ist die Zahl der Datenbankabfragen hochzuschrauben, ist Pagination: Man zeigt nur x Kommentare an und wenn der User mehr sehen will, läd man die nächsten x nach. Das läuft auch auf O(n) hinaus, wird aber gerne hingenommen, weil die Seite dafür nicht mehrere Sekunden läd und dem User dann 125.000 Zeilen Text ausspuckt, die er wahrscheinlich gar nicht haben will.


Hei ho, Also die Kommentare können schon gelöscht werden jedoch nur von user mit den entsprechenden rechten dafür. Aber ja wenn ich nun den Hauptcomment lösche müssen ja auch alle subcomments gelöscht werden. ist schon noch eine verzwickte Sache.
Sieh dir die Slideshow an, die ich oben verlinkt habe. Die Closure Table ist eine schöne Struktur.
Ansonsten ist der einfachste Fall den Kommentar einfach durch einen Platzhalter zu ersetzen: *Von der Moderation entfernt* ergibt auch für spätere Betrachter Sinn, die sich sonst wundern würden, worauf sich anderen Kommentare beziehen. Wobei: für Spam ist natürlich schon schön, die komplett verschwinden zu lassen.
 

virtus

Gehasst

Registriert
24 Apr. 2015
Beiträge
1.689
Ort
AUF DEM MOND
Mir ging es darum, dass ich, wenn ich einen Baum aus n Knoten aufbaue, ich lieber Θ(1) SQL Abfragen mache und mit einem Θ(n) PHP-Algorithmus den Baum hochziehe, statt einen PHP-Algorithmus Θ(n) zu verwenden, der O(n) Datenbankabfragen macht.
Natürlich solltest du prinzipiell vermeiden mehr einzelne Anfragen auf der Datenbank auszuführen als nötig. Daher hast du natürlich Recht, dass Θ(1) SQL Abfragen besser sind als Θ(n) SQL Abfragen. Das steht hoffentlich außer Frage.

Ich wollte eher darauf hinaus, dass du durch die Vorsortierung der Beiträge durch die Datenbank beim Aufbauen des Baums später Arbeit seitens PHP sparst und es performanter ist als die ungeordneten Beiträge durch PHP an der korrekten Stelle in den Baum zu schieben. Wobei das dann ggf. noch mal mit der Implementierung des Baums und der Größe des Datensatzes zusammen hängt.
 
Zuletzt bearbeitet:
Oben