dhtmlxGantt mit PHP: Laravel

Diese Anleitung erklärt, wie man dhtmlxGantt in eine Laravel Anwendung integriert.

Wenn Sie nach Tutorials zur Integration mit anderen Plattformen suchen, schauen Sie sich folgende an:

Den vollständigen Quellcode für dieses Tutorial finden Sie auf GitHub.

Es gibt auch eine Videoanleitung, die die Erstellung eines Gantt-Diagramms mit PHP Laravel zeigt:


Schritt 1. Initialisierung eines Projekts

Erstellen eines Projekts

Um zu beginnen, erstellen Sie eine neue Laravel-Anwendung mit Composer:

composer create-project laravel/laravel gantt-laravel-app

Dieser Prozess wird alle notwendigen Dateien herunterladen und einrichten. Nach Abschluss navigieren Sie in das Projektverzeichnis und starten die Anwendung:

cd gantt-laravel-app
php artisan serve

Sie sollten nun die Standard-Laravel-Seite sehen, wenn Sie die Anwendung im Browser öffnen:


Schritt 2. Hinzufügen von Gantt zur Seite

Hinzufügen einer Ansicht

Um dhtmlxGantt einzufügen, erstellen Sie eine neue Ansichtsdatei im Verzeichnis resources/views und benennen Sie sie gantt.blade.php:

resources/views/gantt.blade.php

<!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>

Diese Datei richtet ein grundlegendes HTML-Layout ein, inkludiert dhtmlxGantt vom CDN und initialisiert das Gantt-Diagramm. Die Höhe für den Dokumentenkörper und den Gantt-Container ist auf 100% gesetzt, um eine korrekte Anzeige zu gewährleisten.

Ändern der Standardroute

Um die Gantt-Seite zugänglich zu machen, setzen Sie sie als Standardroute in routes/web.php:

routes/web.php

<?php
 
Route::get('/', function () {
    return view('gantt');
});

Starten Sie die App neu und vergewissern Sie sich, dass die Gantt-Seite angezeigt wird:


Schritt 3. Erstellen von Modellen und Migrationen

Einrichten der Datenbank

Aktualisieren Sie die Datenbankkonfiguration in der .env Datei, zum Beispiel:

.env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=gantt-test
DB_USERNAME=root
DB_PASSWORD=

Generieren von Modellen und Migrationen

Verwenden Sie Artisan, um Modell- und Migrationsdateien für Aufgaben und Links zu generieren:

php artisan make:model Task --migration
php artisan make:model Link --migration

Definieren Sie das Datenbankschema im Ordner database/migrations. Das erwartete Schema für Gantt ist hier beschrieben.

Migration der Aufgabentabelle

database/migrations/_create_tasks_table.php

<?php
 
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
 
class CreateTasksTable extends Migration
{
    public function up()
    {
        Schema::create('tasks', function (Blueprint $table){
            $table->increments('id');
            $table->string('text');
            $table->integer('duration');
            $table->float('progress');
            $table->dateTime('start_date');
            $table->integer('parent');
            $table->timestamps();
        });
    }
 
    public function down()
    {
        Schema::dropIfExists('tasks');
    }
}

Migration der Link-Tabelle

database/migrations/_create_links_table.php

<?php
 
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
 
class CreateLinksTable extends Migration
{
    public function up()
    {
        Schema::create('links', function (Blueprint $table) {
            $table->increments('id');
            $table->string('type');
            $table->integer('source');
            $table->integer('target');
            $table->timestamps();
        });
    }
 
    public function down()
    {
        Schema::dropIfExists('links');
    }
}

Führen Sie die Migrationen aus:

php artisan migrate

Hinzufügen von Testdaten

Um Testdaten zu generieren, erstellen Sie Seeder mit Artisan:

php artisan make:seeder TasksTableSeeder
php artisan make:seeder LinksTableSeeder

Fügen Sie Testdaten in TasksTableSeeder hinzu:

database/seeds/TasksTableSeeder.php

<?php
 
use Illuminate\Database\Seeder;
 
class TasksTableSeeder extends Seeder
{
    public function run()
    {
        DB::table('tasks')->insert([
            ['id'=>1, 'text'=>'Project #1', 'start_date'=>'2017-04-01 00:00:00', 
                'duration'=>5, 'progress'=>0.8, 'parent'=>0],
            ['id'=>2, 'text'=>'Task #1', 'start_date'=>'2017-04-06 00:00:00', 
                'duration'=>4, 'progress'=>0.5, 'parent'=>1],
            ['id'=>3, 'text'=>'Task #2', 'start_date'=>'2017-04-05 00:00:00', 
                'duration'=>6, 'progress'=>0.7, 'parent'=>1],
            ['id'=>4, 'text'=>'Task #3', 'start_date'=>'2017-04-07 00:00:00', 
                'duration'=>2, 'progress'=>0, 'parent'=>1],
            ['id'=>5, 'text'=>'Task #1.1', 'start_date'=>'2017-04-05 00:00:00', 
                'duration'=>5, 'progress'=>0.34, 'parent'=>2],
            ['id'=>6, 'text'=>'Task #1.2', 'start_date'=>'2017-04-11 00:00:00', 
                'duration'=>4, 'progress'=>0.5, 'parent'=>2],
            ['id'=>7, 'text'=>'Task #2.1', 'start_date'=>'2017-04-07 00:00:00', 
                'duration'=>5, 'progress'=>0.2, 'parent'=>3],
            ['id'=>8, 'text'=>'Task #2.2', 'start_date'=>'2017-04-06 00:00:00', 
                'duration'=>4, 'progress'=>0.9, 'parent'=>3]
        ]);
    }
}

Rufen Sie die Seeder in DatabaseSeeder.php auf:

database/seeds/DatabaseSeeder.php

<?php
 
use Illuminate\Database\Seeder;
 
class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call(TasksTableSeeder::class);
        $this->call(LinksTableSeeder::class);
    }
}

Führen Sie das Seeding der Datenbank durch:

php artisan db:seed

Definieren von Modellklassen

Die generierten Modelle für Aufgaben und Links sind einsatzbereit. Sie können jedoch ein open Attribut zum Task-Modell hinzufügen, um sicherzustellen, dass der Projektbaum beim Laden erweitert wird:

/app/Task.php

<?php
 
namespace App;
 
use Illuminate\Database\Eloquent\Model;
 
class Task extends Model
{
    protected $appends = ["open"];
 
    public function getOpenAttribute()
    {
        return true;
    }
}

Das Link-Modell bleibt unverändert:

/app/Link.php

<?php
 
namespace App;
 
use Illuminate\Database\Eloquent\Model;
 
class Link extends Model
{
}

Schritt 4. Laden von Daten

Um Daten in Gantt zu laden, erstellen Sie einen Controller, der Daten im JSON-Format zurückgibt:

app/Http/Controllers/GanttController.php

<?php
namespace App\Http\Controllers;
 
use App\Task;
use App\Link;
 
class GanttController extends Controller
{
    public function get()
    {
        $tasks = new Task();
        $links = new Link();
 
        return response()->json([
            "data" => $tasks->all(),
            "links" => $links->all()
        ]);
    }
}

Fügen Sie eine Route für die Aktion in routes/api.php hinzu:

routes/api.php

<?php
 
use App\Http\Controllers\GanttController;
 
Route::get('/data', 'GanttController@get');

Aktualisieren Sie die Ansicht, um Daten zu laden:

resources/views/gantt.blade.php

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

Die Methode gantt.load sendet eine AJAX-Anfrage, um Daten im JSON-Format abzurufen. Das date_format gibt das erwartete Format für Daten an.

Überprüfen Sie die App erneut und Sie werden sehen, dass Aufgaben im Gantt-Diagramm angezeigt werden:


Schritt 5. Speichern von Änderungen

Nachdem Daten vom Backend gelesen werden können, ist der nächste Schritt, Änderungen zurück in die Datenbank zu speichern. Der Client sendet RESTful-Anfragen (POST/PUT/DELETE) für Aufgaben- und Link-Aktionen. Details zu diesen Anfragen finden Sie hier.

Der nächste Schritt besteht darin, Controller zu erstellen, um diese Aktionen zu behandeln und das Speichern von Daten auf der Client-Seite zu ermöglichen.

Hinzufügen von Controllern

Erstellen Sie RESTful Resource Controller für Aufgaben und Links, um Aktionen wie das Hinzufügen, Aktualisieren und Löschen zu verwalten.

Controller für Aufgaben

Der TaskController behandelt das Erstellen, Aktualisieren und Löschen von Aufgaben. Hier ist der Code:

app/Http/Controllers/TaskController.php

<?php
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use App\Task;
 
class TaskController extends Controller
{
    public function store(Request $request){
        $task = new Task();
 
        $task->text = $request->text;
        $task->start_date = $request->start_date;
        $task->duration = $request->duration;
        $task->progress = $request->has("progress") ? $request->progress : 0;
        $task->parent = $request->parent;
 
        $task->save();
 
        return response()->json([
            "action"=> "inserted",
            "tid" => $task->id
        ]);
    }
 
    public function update($id, Request $request){
        $task = Task::find($id);
 
        $task->text = $request->text;
        $task->start_date = $request->start_date;
        $task->duration = $request->duration;
        $task->progress = $request->has("progress") ? $request->progress : 0;
        $task->parent = $request->parent;
 
        $task->save();
 
        return response()->json([
            "action"=> "updated"
        ]);
    }
 
    public function destroy($id){
        $task = Task::find($id);
        $task->delete();
 
        return response()->json([
            "action"=> "deleted"
        ]);
    }
}

Die Route für diese Aktionen wird wie folgt definiert:

routes/api.php

<?php
 
use Illuminate\Http\Request;
 
Route::get('/data', 'GanttController@get');
Route::resource('task', 'TaskController');

Hinweise

  • Wenn eine Aufgabe hinzugefügt wird, wird ihre ID im tid-Eigenschaft der Antwort zurückgesendet.
  • Wenn die Anfrage keinen progress-Parameter enthält, wird er auf 0 gesetzt.
  • Das Antwort-JSON kann zusätzliche Eigenschaften enthalten, die im Client-seitigen Handler zugänglich sind. Weitere Details sind hier verfügbar.

Nun erstellen wir einen ähnlichen Controller für Links.

Controller für Links

Der LinkController verwaltet die Erstellung, Aktualisierung und Löschung von Links. Hier ist der Code:

app/Http/Controllers/LinkController.php

<?php
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use App\Link;
 
class LinkController extends Controller
{
    public function store(Request $request){
        $link = new Link();
 
        $link->type = $request->type;
        $link->source = $request->source;
        $link->target = $request->target;
 
        $link->save();
 
        return response()->json([
            "action"=> "inserted",
            "tid" => $link->id
        ]);
    }
 
    public function update($id, Request $request){
        $link = Link::find($id);
 
        $link->type = $request->type;
        $link->source = $request->source;
        $link->target = $request->target;
 
        $link->save();
 
        return response()->json([
            "action"=> "updated"
        ]);
    }
 
    public function destroy($id){
        $link = Link::find($id);
        $link->delete();
 
        return response()->json([
            "action"=> "deleted"
        ]);
    }
}

Die Routen für diesen Controller sind wie folgt:

routes/api.php

<?php
 
use Illuminate\Http\Request;
 
Route::get('/data', 'GanttController@get');
Route::resource('task', 'TaskController');
Route::resource('link', 'LinkController');

Aktivieren der Datenspeicherung auf der Client-Seite

Um die API auf der Client-Seite funktional zu machen, sind einige Konfigurationen erforderlich:

resources/views/gantt.blade.php

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

Mit dieser Konfiguration ist das Gantt-Diagramm nun vollständig interaktiv, sodass Aufgaben und Links hinzugefügt, aktualisiert und gelöscht werden können.

Für weitere Funktionen, schauen Sie sich unsere Anleitungen an.

Speichern der Reihenfolge der Aufgaben

Wenn Aufgaben durch Ziehen und Ablegen neu geordnet werden, muss die neue Reihenfolge in der Datenbank gespeichert werden. Der Prozess wird hier detailliert beschrieben.

Aktivierung des Neuanordnens auf der Client-Seite

Um das Neuanordnen in der Benutzeroberfläche zu ermöglichen, aktualisieren Sie die Gantt-Konfiguration in der Index-Ansicht:

resources/views/gantt.blade.php

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

Aktivierung des Neuanordnens auf der Server-Seite

Um die Aufgabenreihenfolge zu speichern, fügen Sie eine sortorder-Spalte in das Datenbankschema ein. Hier ist ein Beispielschema:

Schema::create('tasks', function (Blueprint $table){
    $table->increments('id');
    $table->string('text');
    $table->integer('duration');
    $table->float('progress');
    $table->dateTime('start_date');
    $table->integer('parent');
    $table->integer('sortorder')->default(0);
    $table->timestamps();
});

Alternativ dazu erstellen Sie eine Migration, um die Spalte hinzuzufügen:

php artisan make:migration add_sortorder_to_tasks_table --table=tasks

Migrationsdatei:

database/migrations/_add_sortorder_to_tasks_table.php

<?php
 
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
 
class AddSortorderToTasksTable extends Migration
{
    public function up()
    {
        Schema::table('tasks', function (Blueprint $table) {
            $table->integer('sortorder')->default(0);
        });
    }
 
    public function down()
    {
        Schema::table('tasks', function (Blueprint $table) {
            $table->dropColumn('sortorder');
        });
    }
}

Wenden Sie die Migration an:

php artisan migrate

Aktualisierung der Controller

  1. Der GET /data Endpunkt sollte Aufgaben geordnet nach sortorder zurückgeben:

app/Http/Controllers/GanttController.php

<?php
namespace App\Http\Controllers;
use App\Task;
use App\Link;
 
class GanttController extends Controller
{
    public function get(){
        $tasks = new Task();
        $links = new Link();
 
        return response()->json([
            "data" => $tasks->orderBy('sortorder')->get(),            "links" => $links->all()
        ]);
    }
}
  1. Neue Aufgaben sollten einen initialen sortorder Wert haben:

app/Http/Controllers/TaskController.php

public function store(Request $request){
    $task = new Task();
 
    $task->text = $request->text;
    $task->start_date = $request->start_date;
    $task->duration = $request->duration;
    $task->progress = $request->has("progress") ? $request->progress : 0;
    $task->parent = $request->parent;
    $task->sortorder = Task::max("sortorder") + 1; 
    $task->save();
 
    return response()->json([
        "action"=> "inserted",
        "tid" => $task->id
    ]);
}
  1. Bearbeiten Sie Updates, wenn Aufgaben neu geordnet werden:

app/Http/Controllers/TaskController.php

public function update($id, Request $request){
    $task = Task::find($id);
 
    $task->text = $request->text;
    $task->start_date = $request->start_date;
    $task->duration = $request->duration;
    $task->progress = $request->has("progress") ? $request->progress : 0;
    $task->parent = $request->parent;
 
    $task->save();
 
    if($request->has("target")){        $this->updateOrder($id, $request->target);    } 
    return response()->json([
        "action"=> "updated"
    ]);
}
 
private function updateOrder($taskId, $target){
    $nextTask = false;
    $targetId = $target;
 
    if(strpos($target, "next:") === 0){
        $targetId = substr($target, strlen("next:"));
        $nextTask = true;
    }
 
    if($targetId == "null")
        return;
 
    $targetOrder = Task::find($targetId)->sortorder;
    if($nextTask)
        $targetOrder++;
 
    Task::where("sortorder", ">=", $targetOrder)->increment("sortorder");
 
    $updatedTask = Task::find($taskId);
    $updatedTask->sortorder = $targetOrder;
    $updatedTask->save();
}

Anwendungssicherheit

Sicherheitsmaßnahmen wie die Verhinderung von SQL-Injektionen, XSS und CSRF-Angriffen sind standardmäßig nicht enthalten. Es liegt in der Verantwortung der Entwickler, die Anwendung sicher zu gestalten. Weitere Details finden Sie hier.

Fehlerbehebung

Wenn Aufgaben und Links nach Abschluss der Einrichtung nicht gerendert werden, konsultieren Sie den Fehlerbehebungsleitfaden hier.

Was kommt als Nächstes

Das Gantt-Diagramm ist nun voll funktionsfähig. Der komplette Code ist auf GitHub verfügbar. Sie können ihn für Ihre Projekte klonen oder herunterladen. Zusätzliche Anleitungen und Tutorials finden Sie hier.

Zurück nach oben