Dieser Leitfaden beschreibt, wie man ein Gantt-Diagramm mit PHP-Versionen 5.6x-7.x und einer RESTful API auf dem Server einrichtet.

Dieses Tutorial basiert auf dem älteren Slim Framework v3.x. Für die neueste Version, werfen Sie einen Blick auf die Slim Framework v4.x Dokumentation.

Wenn Sie an der serverseitigen Integration mit anderen Plattformen oder Frameworks interessiert sind, sehen Sie sich diese Tutorials an:

Für diese Einrichtung wird das Slim 3 Framework das Routing übernehmen, und MySQL dient als Datenbank. CRUD-Operationen werden mit PDO implementiert, um die Kompatibilität mit anderen Frameworks sicherzustellen.

Der vollständige Quellcode ist auf GitHub zu finden.

Schritt 1. Initialisieren eines Projekts

Ein Projekt erstellen

Um zu beginnen, wird eine Skeleton-Anwendung für das Slim 3 Framework verwendet.

Zuerst muss das Projekt importiert und installiert werden. Composer kann dabei helfen:

php composer.phar create-project slim/slim-skeleton gantt-rest-php

Wenn Composer global installiert ist, können Sie verwenden:

composer create-project slim/slim-skeleton gantt-rest-php

Sobald das erledigt ist, stellen Sie sicher, dass alles ordnungsgemäß funktioniert. Navigieren Sie zum Anwendungsordner und starten Sie einen Webserver:

cd gantt-rest-php
php -S 0.0.0.0:8080 -t public public/index.php

Öffnen Sie nun http://127.0.0.1:8080 in einem Browser, um die Standard-Slim-Seite zu sehen.


Schritt 2: Hinzufügen von Gantt zur Seite

Als nächstes muss eine Seite mit einem Gantt-Diagramm erstellt werden. Lokalisieren Sie die Standardseite in templates/index.phtml. Hier wird das Gantt-Diagramm hinzugefügt, zusammen mit der Einrichtung zum Laden von Daten.

Hier ist der vollständige Code für die Seite:

/templates/index.phtml

<!DOCTYPE html>
<head>
  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
 
  <script src="https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.js"></script>
  <link href="https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.css" rel="stylesheet">
 
  <style type="text/css">
    html, body{
      height:100%;
      padding:0px;
      margin:0px;
      overflow: hidden;
    }
</style> </head> <body> <div id="gantt_here" style='width:100%; height:100%;'></div> <script type="text/javascript">
    gantt.init("gantt_here");
</script> </body>

Dieser Code zeigt ein leeres Gantt-Diagramm auf der Seite an. Benutzer können Aufgaben und Verknüpfungen erstellen und ändern, aber diese Änderungen werden nach einem Neuladen der Seite nicht beibehalten.

Um es zu testen, starten Sie die App neu:

command line

php -S 0.0.0.0:8080 -t public public/index.php

Öffnen Sie dann http://127.0.0.1:8080/ in einem Browser. Das Gantt-Diagramm sollte nun auf der Seite erscheinen.


Schritt 3: Konfigurieren einer Datenbank

Der nächste Schritt besteht darin, eine Datenbank einzurichten. Eine einfache Datenbank mit zwei Tabellen reicht aus.

CREATE TABLE `gantt_links` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `source` int(11) NOT NULL,
  `target` int(11) NOT NULL,
  `type` varchar(1) NOT NULL,
  PRIMARY KEY (`id`)
);
 
CREATE TABLE `gantt_tasks` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `text` varchar(255) NOT NULL,
  `start_date` datetime NOT NULL,
  `duration` int(11) NOT NULL,
  `progress` float NOT NULL,
  `parent` int(11) NOT NULL,
  PRIMARY KEY (`id`)
);

Sobald die Datenbank bereit ist, füllen Sie die gantt_tasks Tabelle mit einigen Testdaten mit dem folgenden SQL:

INSERT INTO `gantt_tasks` VALUES ('1', 'Projekt #1', '2017-04-01 00:00:00', '5', '0.8', '0');
INSERT INTO `gantt_tasks` VALUES ('2', 'Aufgabe #1', '2017-04-06 00:00:00', '4', '0.5', '1');
INSERT INTO `gantt_tasks` VALUES ('3', 'Aufgabe #2', '2017-04-05 00:00:00', '6', '0.7', '1');
INSERT INTO `gantt_tasks` VALUES ('4', 'Aufgabe #3', '2017-04-07 00:00:00', '2', '0', '1');
INSERT INTO `gantt_tasks` VALUES ('5', 'Aufgabe #1.1', '2017-04-05 00:00:00', '5', '0.34', '2');
INSERT INTO `gantt_tasks` VALUES ('6', 'Aufgabe #1.2', '2017-04-11 13:22:17', '4', '0.5', '2');
INSERT INTO `gantt_tasks` VALUES ('7', 'Aufgabe #2.1', '2017-04-07 00:00:00', '5', '0.2', '3');
INSERT INTO `gantt_tasks` VALUES ('8', 'Aufgabe #2.2', '2017-04-06 00:00:00', '4', '0.9', '3');

Weitere Details sind hier verfügbar.

Mit der abgeschlossenen Projekteinstellung ist es Zeit, mit dem Laden der Daten fortzufahren.


Schritt 4: Laden von Daten

Nun wird das Laden von Daten aus der Datenbank implementiert. Auf der Client-Seite werden die Daten mit der gantt.load Methode angefordert:

/templates/index.phtml

gantt.config.date_format = "%Y-%m-%d %H:%i:%s"; 
gantt.init("gantt_here");
gantt.load("/data");

Dies sendet eine AJAX-Anfrage an die angegebene URL, die eine Antwort mit Gantt-Daten im JSON-Format erwartet.

Der date_format Wert wird hier ebenfalls angegeben, damit die Client-Seite weiß, wie das Datumsformat der Datenquelle zu analysieren ist.

Der nächste Schritt besteht darin, einen Handler für diese Anfrage auf dem Backend hinzuzufügen. In src/routes.php wird eine neue Route hinzugefügt:

src/routes.php

<?php
// Routen
 
$app->get('/', function ($request, $response, $args) {
  // Indexansicht rendern
  return $this->renderer->render($response, 'index.phtml', $args);
});
 
$app->get('/data',  'getGanttData');

Die Logik für getGanttData wird als nächstes implementiert. Um die Dinge organisiert zu halten, wird die gesamte Gantt-bezogene Funktionalität in eine separate Datei gelegt.

Erstellen Sie eine neue Datei, src/gantt.php, und fügen Sie den folgenden Code hinzu:

src/gantt.php

function getConnection()
{
    return new PDO("mysql:host=localhost;dbname=gantt", "root", "root", [
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
}
 
function getGanttData($request, $response, $args) {
  $db = getConnection();
  $result = [
    "data"=> [],
    "links"=> []
  ];
 
  foreach($db->query("SELECT * FROM gantt_tasks") as $row){
    $row["open"] = true;
    array_push($result["data"], $row);
  }
 
  foreach ($db->query("SELECT * FROM gantt_links") as $link){
    array_push($result["links"], $link);
  }
 
  return $response->withJson($result);
};

Binden Sie src/gantt.php in public/index.php ein:

public/index.php

<?php
// Vorhandener Code...
 
// dhtmlxGantt CRUD hinzufügen
require __DIR__ . '/../src/gantt.php';  
// App ausführen
$app->run();

Hier ist, was im Code passiert:

  • Eine Route für die Datenaktion wird in src/routes.php definiert.
  • Der Handler liest alle Aufgaben und Links aus der Datenbank und sendet sie als JSON an den Client.
  • Eine open Eigenschaft wird zu den Aufgabenobjekten hinzugefügt, um den Aufgabenbaum standardmäßig im erweiterten Zustand anzuzeigen.

Mit implementiertem Datenladen sollte das Öffnen von http://127.0.0.1:8080/ nun das Gantt-Diagramm mit den Testdaten anzeigen.


Schritt 5: Änderungen speichern

Der nächste Schritt besteht darin, Änderungen, die auf der Client-Seite vorgenommen wurden, zurück an den Server zu speichern. Die in Gantt eingebettete dataProcessor Bibliothek wird dafür verwendet.

In index.phtml fügen Sie die folgenden Zeilen hinzu:

templates/index.phtml

gantt.config.date_format = "%Y-%m-%d %H:%i:%s";
 
gantt.init("gantt_here");
gantt.load("/data");
 
var dp = new gantt.dataProcessor("/data");dp.init(gantt);dp.setTransactionMode("REST");

Der dataProcessor reagiert auf Aktionen wie das Hinzufügen, Ändern oder Entfernen von Daten im Diagramm, indem er AJAX-Anfragen an den Server sendet. Er arbeitet im REST-Modus und verwendet verschiedene HTTP-Methoden für verschiedene Aktionen. Hier ist die vollständige Liste der Routen.

Als nächstes werden diese Routen zur App hinzugefügt und ihre Logik implementiert. Gehen Sie zu src/routes.php und aktualisieren Sie es:

src/routes.php

<?php
// Routen
 
$app->get('/', function ($request, $response, $args) {
  // Indexansicht rendern
  return $this->renderer->render($response, 'index.phtml', $args);
});
 
$app->get('/data',  'getGanttData');
 
$app->post("/data/task", 'addTask');
$app->put("/data/task/{id}", 'updateTask');
$app->delete("/data/task/{id}", 'deleteTask');
 
$app->post("/data/link", 'addLink');
$app->put("/data/link/{id}", 'updateLink');
$app->delete("/data/link/{id}", 'deleteLink');

Definieren Sie nun die Methoden, die mit diesen Routen in src/gantt.php verknüpft sind:

src/gantt.php

// Vorhandener Code...
 
function getTask($data) {
  return [
    ':text' => $data["text"],
    ':start_date' => $data["start_date"],
    ':duration' => $data["duration"],
    ':progress' => isset($data["progress"]) ? $data["progress"] : 0,
    ':parent' => $data["parent"]
  ];
}
 
function getLink($data) {
  return [
    ":source" => $data["source"],
    ":target" => $data["target"],
    ":type" => $data["type"]
  ];
}
 
// Task- und Link-CRUD-Funktionen...

Jede Methode behandelt grundlegende Erstellungs-, Aktualisierungs- oder Löschvorgänge für Aufgaben und Links. Beim Hinzufügen neuer Elemente wird die Datenbank-ID an den Client zurückgegeben.

Beachten Sie, dass hier keine Datenbankbeziehungen behandelt werden. Zum Beispiel werden verschachtelte Aufgaben oder zugehörige Links nicht automatisch gelöscht, wenn eine Aufgabe entfernt wird. Die Client-Seite behandelt dies standardmäßig und sendet separate Anfragen für jede untergeordnete Aufgabe oder Verknüpfung. Wenn eine Backend-Behandlung bevorzugt wird, aktivieren Sie die cascade_delete Konfiguration.

Alles ist nun eingerichtet. Führen Sie die App aus und besuchen Sie http://127.0.0.1:8080, um das voll funktionsfähige Gantt-Diagramm zu sehen.


Speichern der Reihenfolge der Aufgaben

Das Gantt-Diagramm erlaubt Aufgaben-Neuanordnung per Drag-and-Drop. Wenn diese Funktion verwendet wird, muss die Reihenfolge in der Datenbank gespeichert werden. Details dazu finden Sie hier.

Fügen wir diese Funktion der App hinzu.

Aktivieren der Aufgaben-Neuanordnung auf dem Client

Um Benutzern die Neuanordnung von Aufgaben in der Benutzeroberfläche zu ermöglichen, müssen Sie einige Anpassungen vornehmen. Beginnen Sie mit dem Öffnen der Index Ansicht und ändern Sie die Gantt-Konfiguration wie unten gezeigt:

/templates/index.phtml

gantt.config.order_branch = true;gantt.config.order_branch_free = true; 
gantt.init("gantt_here");

Stellen Sie als nächstes sicher, dass diese Änderungen auf dem Backend reflektiert werden. Die Reihenfolge der Aufgaben wird in einer Spalte namens "sortorder" gespeichert. Hier ist ein Beispiel, wie die aktualisierte gantt_tasks Tabelle aussehen könnte:

CREATE TABLE `gantt_tasks` (
  `id` int(11) NOT NULL  AUTO_INCREMENT PRIMARY KEY,
  `text` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `start_date` datetime NOT NULL,
  `duration` int(11) NOT NULL,
  `progress` float NOT NULL DEFAULT 0,
  `parent` int(11) NOT NULL,
  `sortorder` int(11) NOT NULL );

Alternativ können Sie, wenn die Tabelle bereits existiert, einfach die neue Spalte hinzufügen:

ALTER TABLE `gantt_tasks` ADD COLUMN `sortorder` int(11) NOT NULL;

Nach dem Aktualisieren der Datenbank passen Sie die CRUD-Operationen in src/gantt.php an.

  1. GET /data sollte nun Aufgaben nach der sortorder Spalte zurückgeben:

src/gantt.php

function getGanttData($request, $response, $args) {
 $db = getConnection();
 $result = [
   "data" => [],
   "links" => []
 ];
 
 foreach($db->query("SELECT * FROM gantt_tasks ORDER BY sortorder ASC") as $row){
   $row["open"] = true;
   array_push($result["data"], $row);
 }
 
 foreach ($db->query("SELECT * FROM gantt_links") as $link){
   array_push($result["links"], $link);
 }
 
 return $response->withJson($result);
}
  1. Beim Hinzufügen neuer Aufgaben, stellen Sie sicher, dass sie einen initialen sortorder Wert erhalten:

src/gantt.php

// Eine neue Aufgabe erstellen
function addTask($request, $response, $args) {
 $task = getTask($request->getParsedBody());
 $db = getConnection();
 
 $maxOrderQuery = "SELECT MAX(sortorder) AS maxOrder FROM gantt_tasks";
 $statement = $db->prepare($maxOrderQuery);
 $statement->execute();
 
 $maxOrder = $statement->fetchColumn();
 if(!$maxOrder)
   $maxOrder = 0;
 
 $task[":sortorder"] = $maxOrder + 1;
 
 $query="INSERT INTO gantt_tasks(text,start_date,duration,progress,parent,sortorder)".
   "VALUES (:text,:start_date,:duration,:progress,:parent, :sortorder)";
 $db->prepare($query)->execute($task);
 
 return $response->withJson([
   "action"=>"inserted",
   "tid"=> $db->lastInsertId()
 ]);
}
  1. Wenn Aufgaben neu angeordnet werden, muss die Aufgabenreihenfolge entsprechend aktualisiert werden. So können Sie dies handhaben:

src/gantt.php

// Eine Aufgabe aktualisieren
function updateTask($request, $response, $args) {
  $sid = $request->getAttribute("id");
  $params = $request->getParsedBody();  $task = getTask($params);
  $db = getConnection();
  $query = "UPDATE gantt_tasks ".
    "SET text = :text, start_date = :start_date, duration = :duration, ".
      "progress = :progress, parent = :parent ".
    "WHERE id = :sid";
 
  $db->prepare($query)->execute(array_merge($task, [":sid"=>$sid]));
 
  if(isset($params["target"]) && $params["target"])    updateOrder($sid, $params["target"], $db);
 
  return $response->withJson([
    "action"=>"updated"
  ]);
}
 
function updateOrder($taskId, $target, $db){
  $nextTask = false;
  $targetId = $target;
 
  if(strpos($target, "next:") === 0){
    $targetId = substr($target, strlen("next:"));
    $nextTask = true;
  }
 
  if($targetId == "null")
    return;
 
  $sql = "SELECT sortorder FROM gantt_tasks WHERE id = :id";
  $statement = $db->prepare($sql);
  $statement->execute([":id"=>$targetId]);
 
  $targetOrder = $statement->fetchColumn();
  if($nextTask)
    $targetOrder++;
 
  $sql = "UPDATE gantt_tasks SET sortorder = sortorder + 1 ".
    "WHERE sortorder >= :targetOrder";
  $statement = $db->prepare($sql);
  $statement->execute([":targetOrder"=>$targetOrder]);
 
  $sql = "UPDATE gantt_tasks SET sortorder = :targetOrder WHERE id = :taskId";
  $statement = $db->prepare($sql);
  $statement->execute([
    ":targetOrder"=>$targetOrder,
    ":taskId"=>$taskId
  ]);
}

Eine fertige Demo ist auf GitHub verfügbar.

Verwendung von dhtmlxConnector

Eine andere Möglichkeit, das Backend einzurichten, ist die Verwendung der dhtmlxConnector-Bibliothek. Eine Schritt-für-Schritt-Anleitung ist hier verfügbar.

Anwendungssicherheit

Gantt selbst behandelt nicht die Sicherheit der Anwendung, daher liegt es an den Entwicklern, sich gegen Bedrohungen wie SQL-Injections, XSS und CSRF-Angriffe zu schützen. Weitere Details finden Sie in diesem Artikel.

Fehlerbehebung

Wenn Aufgaben und Links nach den Schritten nicht wie erwartet gerendert werden, können Sie im Artikel Fehlerbehebung bei Backend-Integrationsproblemen nach möglichen Lösungen suchen.

Was kommt als Nächstes

An diesem Punkt sollten Sie eine voll funktionsfähige Gantt-Einrichtung haben. Der vollständige Code ist auf GitHub verfügbar, wo Sie ihn für Ihre Projekte klonen oder herunterladen können.

Für weitere Funktionen und Integrationen, schauen Sie sich die Leitfäden zu Gantt-Funktionen an oder erkunden Sie Tutorials zur Integration von Gantt mit anderen Backend-Frameworks.

Zurück nach oben