• Keine Ergebnisse gefunden

Bachelorarbeit. Entwicklung eines Moduls zur Digitalisierung der Ressourcenverwaltung in Handwerksbetrieben. Studiengang Informatik.

N/A
N/A
Protected

Academic year: 2022

Aktie "Bachelorarbeit. Entwicklung eines Moduls zur Digitalisierung der Ressourcenverwaltung in Handwerksbetrieben. Studiengang Informatik."

Copied!
62
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Studiengang Informatik

Entwicklung eines Moduls zur

Digitalisierung der Ressourcenverwaltung in Handwerksbetrieben

Markus Heck

Aufgabensteller Prof. Dr. T. Breiner Arbeit vorgelegt am 03.12.2019

durchgeführt in der Fakultät Informatik durchgeführt bei Team Nifty GmbH

87435 Kempten Anschrift des Verfassers -

-

(2)

Kurzzusammenfassung

Diese Arbeit befasst sich mit der digitalen Verwaltung der Ressourcen in Handwerksbetrie- ben. Als Teil einer umfangreichen Softwarelösung zur Digitalisierung der Verwaltung in dieser Branche, soll sich dieses Modul um die Erfassung, Verwaltung und Nachverfolgung von Fahr- zeugen, Arbeitsgeräten und Verbrauchsmaterialien kümmern. Hierzu wird eine App für Android und Apple, sowie eine Webanwendung entwickelt. Während die mobilen Apps den schnellen Zugriffauf alle wichtigen Informationen ermöglichen, können in der Webanwendung insbeson- dere verwaltungstechnische Aufgaben durchgeführt werden.

Abstract

This thesis deals with the digital management of resources in craft enterprises. As part of a com- prehensive software solution to digitize management in this industry, this module is designed to capture, manage and track vehicles, tools and consumables. An app for Android and Apple as well as a web application will be developed. While the mobile apps provide quick access to all important information, the web application can be used for administrative tasks.

(3)

Inhaltsverzeichnis

Inhaltsverzeichnis III

Abbildungsverzeichnis V

1 Einleitung 1

2 Zielsetzung 2

3 Grundlagen 3

3.1 Dart . . . 3

3.2 Flutter . . . 3

3.2.1 Framework . . . 3

3.2.2 State-Management . . . 4

3.3 Lumen . . . 4

3.4 Angular . . . 5

4 Anforderungsanalyse 6 4.1 Funktionale Anforderungen . . . 6

4.1.1 Multiplattform. . . 6

4.1.2 Bestandsverwaltung von Ressourcen . . . 7

4.1.3 Reservieren von Ressourcen . . . 7

4.1.4 Melden von Defekten und anstehenden Wartungen . . . 8

4.1.5 Kategorisierung . . . 8

4.1.6 Ortsbestimmung und Kartendarstellung . . . 8

4.2 Nichtfunktionale Anforderungen . . . 8

4.2.1 Einfache Bedienbarkeit . . . 8

5 Konzept 9 5.1 Backend . . . 10

5.1.1 Datenbank . . . 10

5.2 Webanwendung . . . 14

5.3 App . . . 14

5.3.1 Benutzeroberfläche . . . 14

5.3.2 Netzwerkkommunikation . . . 16

(4)

6 Implementierung 18

6.1 Backend . . . 18

6.1.1 Erstellen der Datenbanktabellen . . . 18

6.1.2 Nutzung der Datenbank in Lumen . . . 19

6.1.3 Controller . . . 22

6.1.4 Anlegen von API-Endpunkten . . . 24

6.2 Webanwendung . . . 25

6.2.1 Services . . . 26

6.2.2 Controller und Benutzeroberfläche . . . 27

6.3 App . . . 31

6.3.1 Services . . . 31

6.3.2 Stores . . . 36

6.3.3 Benutzeroberfläche . . . 39

7 Ergebnisse und Ausblick 48 7.1 Ergebnisse . . . 48

7.2 Ausblick . . . 49

8 Anhang 53 8.1 Inhalt der beigelegten CD . . . 53

Erklärung und Ermächtigung 54

(5)

Abbildungsverzeichnis

1 Diagramm der Anwendungskomponenten. . . 9

2 Umsetzung der Polymorphie von Equipment, Verbrauchsmaterial und Fahrzeugen 11 3 Beispielhafter Ausschnitt des Datenbankmodells . . . 13

5 Vergleich Entwurf der Detailseite mit der fertigen App . . . 15

6 Struktureller Aufbau des Dialogs . . . 28

7 Equipment Übersicht mit Filtern . . . 44

4 Erste App Skizzen . . . 55

8 Craftbuddy auf einem iPad . . . 56

(6)

1 Einleitung

In vielen Handwerksbetrieben wird heutzutage immer noch weitestgehend analog gearbeitet.

Arbeitseinteilungen von Personen und Material finden an großen, stationären Planungswänden statt und Projekte werden in Projektmappen handschriftlich geführt. Dadurch sind viele wichti- ge Informationen nur an einer zentralen Stelle verfügbar und stehen somit bei Bedarf nicht zur Verfügung. Eine flexible Ein- und Umplanung ist nicht möglich.

Über eine App, welche jeder Mitarbeiter nutzt, stünden diese Informationen jederzeit bereit. In einer Webanwendung könnten flexibel neue Planungen vorgenommen, oder bestehende ange- passt werden. Änderungen an der Planung können die Mitarbeiter jederzeit in der App einsehen.

[Gmb19, S. 5]

Die digitale Verwaltung der Ressourcen wird einen einfachen Überblick über den aktuellen Nutzungsstatus, die Auslastung bestimmter Geräte, sowie gemeldeter Defekte und anstehender Wartungen ermöglichen. Ausfälle durch Defekte oder doppelt eingeplante Maschinen können so reduziert werden. [Gmb19, S. 17]

(7)

2 Zielsetzung

Ziel dieser Bachelorarbeit ist es, den Teil der Anwendung zu entwickeln und umzusetzen, wel- cher für die Verwaltung der Ressourcen verantwortlich ist. Dabei ist dieses Modul fester Be- standteil der Gesamtanwendung und wird an einigen Stellen mit anderen Anwendungsteilen verknüpft. Hierzu werden drei Sub-Module entwickelt, wovon sich jeweils eines um die Ver- brauchsmaterialien, Arbeitsgeräte und den Fuhrpark kümmert. Die Sub-Module sollen zu einem gewissen Maße untereinander kompatibel sein, um die Wiederverwendung von Code zu ermög- lichen.

Das Backend wird in dem PHP-Framework Lumen entwickelt. Es stellt insbesondere die Schnitt- stelle zur Datenbank bereit [LLC19e]. Für die Entwicklung der mobilen App wird Flutter ein- gesetzt. Dieses Framework wird von Google entwickelt und ermöglicht es eine App für An- droid und iOS zu entwickeln, ohne dass für beide Betriebssysteme eine eigene Anwendung entwickelt werden muss [Goo19]. Für die Entwicklung der Webanwendung kommt das JavaS- cript/TypeScript basierte Framework Angular zum Einsatz [Flu+19].

(8)

3 Grundlagen

Ziel dieses Kapitels ist es die wichtigsten Grundlagen zu erläutern. Hierzu werden die Aufgaben der eingesetzten Technologien kurz erklärt. Außerdem werden wichtige Besonderheiten und deren Geschichte umrissen.

3.1 Dart

Dart ist eine noch sehr junge Programmiersprache und wird von Google entwickelt [LLC19a].

Die erste stabile Version 1.0 wurde am 14. November 2013 veröffentlicht und damals noch als Programmiersprache für Webanwendungen entworfen, wozu der Dart-Code zu JavaScript kompiliert wird [Bak13]. Ursprünglich war vorgesehen, eine Dart VM in Chromium, und somit auch in Chrome, zu integrieren, um Dart-Code direkt im Browser ausführen zu können [Bak13].

Dieser Plan wurde später zugunsten des JavaScript Compilers verworfen [BL15]. Heute hat sich Dart als Ziel gesetzt, eine Anwendung, mit möglichst geringen Anpassungen, auf mobilen Geräten, sowie Desktoprechnern und als Webanwendung ausführen zu können [LLC19a].

3.2 Flutter

3.2.1 Framework

Bei Flutter handelt es sich um ein Open-Source-Framework von Google zur Entwicklung gra- fischer Anwendungen, welche auf mobilen Geräten, im Browser und am Computer ausge- führt werden können. Wichtige Aspekte sind eine geteilte Codebasis unter den verschiedenen Plattformen und die Ausführung mit nativer Geschwindigkeit. Somit kann dieselbe App ohne Performance-Verlust gegenüber einer nativ entwickelten Anwendung für Android und Apples iOS kompiliert werden. Flutter bietet einige Komfort-Features wie Hot-Reload, wodurch eine Anwendung in vielen Fällen nicht vollständig neu kompiliert werden muss, um Änderungen im Code in der Anwendung zu sehen. [Goo19]

Flutter wurde das erste Mal auf dem Dart Developer Summit im Jahr 2015 unter dem Na- men Sky vorgestellt. Als Programmiersprache für das Framework setzt Google auf ihre Web- Entwicklungssprache Dart [Ama15]. Ende 2018 hat Flutter den Beta-Status verlassen und wur- de in der Version 1.0 veröffentlicht [Tit18]. Unterstützt wird die Entwicklung für Android und iOS, weitere Plattformen wurden zu diesem Zeitpunkt noch nicht offiziell unterstützt [Tit18].

Die Idee hinter Flutter ist, die komplette App aus Widgets zu bauen. Ein Widget ist beispiels- weise ein Text oder eine Box. Dabei enthält ein Widget weitere Widgets und auch die App selbst ist ein Widget. Eine Box kann ein beliebiges Widget, wie einen Text, beinhalten. Ändert sich der Zustand eines Widgets werden nur die nötigen Widgets neu gerendert. [Cha+19b]

(9)

3.2.2 State-Management

Ziel bei Flutter ist es, den aktuellen Zustand der Daten der App abzubilden, den sogenannten State [THZ19b]. Dabei unterscheidet man zwischen dem Ephemeral-State und dem App-State.

Beim Ephemeral-State handelt es sich um Daten, die nur im aktuellen Widget im Kontext der aktuellen App-Instanz benötigt werden. Diese müssen weder abgespeichert noch beim nächs- ten Start der App wieder geladen werden. Dies ist beispielsweise der aktuell geöffnete Tab in einer Tab-Ansicht. Für diesen Teil des States werden keine komplexeren State-Management- Techniken benötigt. Beim App-State handelt es sich hingegen um Daten, die in verschiedenen Teilen der App und oftmals auch bei der nächsten Ausführung der App benötigt werden. Hier ist das Management des States erheblich komplexer, da beispielsweise Daten von einem Server oder dem internen Speicher geladen oder dort gespeichert werden müssen. Sie müssen in kom- plett unterschiedlichen Teilen der App zugänglich sein und dies oft auch, nachdem der Teil der App, der sie geladen hat, nicht mehr geöffnet ist. Eine klare Abgrenzung zwischen Ephemeral und App-State ist oft schwierig und die Grenzen somit fließend. [THZ19a]

Für das Management des App-States gibt es mehrere Lösungen, alle mit ihren eigenen Vor- und Nachteilen [Mel+19]. Im Rahmen dieser Arbeit wird auf MobX in Kombination mit Provider gesetzt. Dabei gibt es einen, oder in größeren Apps mehrere Stores, welche den App-State hal- ten. Alle Änderungen am State werden über Actions durchgeführt. Dies sind Methoden, welche im simpelsten Fall nur eine Variable ändern. Diese können allerdings auch weitere Aktionen ausführen, wie das Laden oder Speichern von Daten oder die übergebenen Daten auf Ihre Gül- tigkeit prüfen. Gespeichert werden die Daten in Variablen, welche über eine Annotation zu Observables werden. Im eigentlichen Programmcode kann auf Änderungen der Observables geachtet und die Anzeige entsprechend angepasst werden. Auf die Änderung eines Observables können des Weiteren Reactions gelegt werden. Diese werden bei der Änderung eines Observa- bles automatisch aufgerufen. [Pod+19b]

MobX bietet aber kein System, um seine Stores an alle nötigen Stellen in der App zu vertei- len. Hier kommt Provider zum Einsatz. Der Store muss hoch genug im Widget-Baum erzeugt werden, um alle Sub-Widgets, welche den Store benötigen, unter sich zu haben. Dieser wird dann über Provider in den Widget-Baum integriert. In allen Sub-Widgets kann der Store über Provider abgerufen werden. [PBR19]

3.3 Lumen

Lumen ist ein PHP-Framework zum Implementieren von APIs. Das Hauptaugenmerk liegt auf einer simplen und schnellen Implementierung der Schnittstellen. Es nutzt dazu die Komponen- ten von Laravel, einem Framework zur Erstellung vollständiger Webanwendungen, welche für

(10)

die Implementierung einer API notwendig sind. Neben den Komponenten, welche direkt zur Umsetzung einer API benötigt werden, sind dies insbesondere Datenbankschnittstellen und ein Test-Framework. [Red16, S. xvii]

Da Lumen sehr nah an Laravel angelehnt ist, ist ein Wechsel zwischen beiden Frameworks einfach möglich und erworbenes Wissen kann weiterhin eingesetzt werden. [Red16, S. xvii]

3.4 Angular

Angular ist ein Framework zur Entwicklung von Single-Page-Webanwendungen. Es ist jedoch ebenso möglich native Anwendungen mit Angular zu entwickeln. Das Framework ist modular aufgebaut. Durch den Fokus auf eine modulare Entwicklung ist ein einfaches Wiederverwenden von Komponenten möglich. Dies reduziert den Entwicklungsaufwand für das Frontend, wes- halb ein größerer Fokus auf die eigentliche Anwendungslogik gelegt werden kann. [Woi+18, S. vii]

Die Entwicklung einer Angular-Anwendung gliedert sich in zwei Teile. Den für den Nutzer sichtbaren Teil und die Anwendungslogik. Ersterer ist nahe an der Entwicklung statischer Web- seiten angelehnt. Es gibt je Modul eine HTML- und eine CSS-Datei. Für die HTML-Datei stellt Angular viele eigene Tags bereit, mit denen auf Komponenten von Angular zugegriffen werden kann. Die Nutzung klassischer HTML-Tags ist ebenfalls möglich. [Flu+19]

Angular unterstützt neben herkömmlichem CSS auch SCSS und SASS, welche eine hierarchi- sche Struktur in der CSS-Datei ermöglichen [Arj19]. Die Anwendungslogik jedes Moduls wird in einer weiteren Datei geführt, welche auch die HTML- und CSS-Dateien mit sich verknüpft.

Somit wird eine Kommunikation mit den dargestellten Inhalten ermöglicht [Woi+18, S. 27–28].

Diese wird in TypeScript geschrieben, einer Erweiterung von JavaScript. Der Hauptunterschied zu JavaScript ist, wie der Name schon andeutet, die Einführung einer Typisierung [Woi+18, S. 27–28].

Angular wurde mit der Version 2 komplett neu entwickelt. Die vorherige Version 1.x.x wird zur Abgrenzung vom aktuellen Angular als AngularJS bezeichnet und ist inkompatibel zur Version 2 und allen folgenden Versionen. Alle sechs Monate soll eine neue Major- und anschließend jeden Monat eine Minor-Version erscheinen. [Woi+18, S. viii]

Die derzeit aktuelle Version 8 ist Ursprünglich am 28. Mai 2019 erschienen [Fel+19].

(11)

4 Anforderungsanalyse

Vor der Umsetzung des Projekts mussten die Anforderungen definiert werden. Dabei wird zwi- schen zwei Arten von Anforderungen unterschieden, die Funktionalen und die Nichtfunktiona- len.

Funktionale Anforderungen

Nach dem Lehrbuch der Softwaretechnik beschreibt dieser Anforderungstyp "welche Funktionalität oder welches Verhalten das Softwareprodukt unter festgelegten Bedingun- gen besitzen bzw. erfüllen soll" [Bal11, S. 109], alsowasdie Anwendung tut. Diese kön- nen klar definiert und überprüft werden. [Bal11, S. 109]

Nichtfunktionale Anforderungen

Im Gegensatz zu den funktionalen Anforderungen sind die Nichtfunktionalen nicht so einfach zu definieren. Diese beziehen sich meist auf die gesamte Anwendung und stehen oft in Konkurrenz zueinander. Dabei handelt es sich beispielsweise um Benutzbarkeit oder Effizienz. Anders ausgedrückt also,wiedie Anwendung die Funktionalen Anforde- rungen erfüllen soll.

Die Abgrenzung zwischen den Anforderungstypen ist oft fließend. So können beispielsweise Zeitanforderungen in beide Arten von Anforderungen fallen. [Bal11, S. 109]

4.1 Funktionale Anforderungen

Folgende funktionale Anforderungen wurden anhand des Dokuments Craftbuddy Definitionen für die Anwendung identifiziert [Gmb19].

4.1.1 Multiplattform

Die Anwendung soll sowohl am Computer über ein Webinterface als auch mobil über eine App genutzt werden können. Während das Webinterface insbesondere die Verwaltung im Büro über- nimmt, ist die App für den mobilen Einsatz auf der Baustelle gedacht. Entsprechend übernimmt die App vorwiegend das Anzeigen von Informationen, welche im Webinterface eingepflegt wer- den.

Das Webinterface soll alle aktuellen Webbrowser unterstützen. Die Nutzung auf einem Tablet soll ebenso möglich sein, der Fokus liegt aber auf der Verwendung mit Maus und Tastatur. Die mobile Anwendung soll auf Android- und iOS-Geräten laufen. Auch die App soll auf einem Tablet einsetzbar sein, das Augenmerk liegt aber auf der Nutzung am Smartphone.

(12)

4.1.2 Bestandsverwaltung von Ressourcen

Das Softwaremodul soll den Bestand der Ressourcen verwalten können. Diese werden in drei Kategorien eingeteilt.

Maschinen / Equipment

Hierbei handelt es sich um alle Arbeitsgeräte, welche in einem Handwerksbetrieb einge- setzt werden und nicht selbständig fahren können. Dies sind beispielsweise Bohrmaschi- nen, Betonmischer oder Computer.

Material / Verbrauchsgüter

Dies sind alle Gegenstände, welche auf einer Baustelle verbraucht werden. Dies sind Beispielsweise Kies, Schrauben oder Ziegel.

Fahrzeuge

Hierunter werden alle selbstfahrenden Maschinen, wie Autos, Gabelstapler oder Trans- porter zusammengefasst.

Jede Ressource hat abhängig von ihrem Typ verschiedene Felder. Sie beinhalten alle relevan- ten Informationen der Ressource, wie Bezeichnung, Kategorie, technische Details, Bilder, und Weitere.

Über eine Weboberfläche sollen bestehende Ressourcen angezeigt, bearbeitet und gelöscht, so- wie neue angelegt werden können. Unterwegs soll überwiegend lesend auf die Informationen über eine App zugegriffen werden.

4.1.3 Reservieren von Ressourcen

Um sicherzustellen, dass alle notwendigen Ressourcen auf einer Baustelle verfügbar sind, sol- len diese reserviert werden können. Eine Reservierung kann in der App getätigt werden. Bei dieser wird immer der aktuell angemeldete Mitarbeiter als verantwortliche Person eingetragen.

Außerdem wird immer der Zeitraum des Bedarfs erfasst. Zusätzlich wird noch ein Ort angege- ben, welcher nicht zwangsläufig eine Baustelle sein muss.

Ziel ist es Doppelbelegungen zu vermeiden und Engpässe frühzeitig zu erkennen. Außerdem kann immer verfolgt werden, wo sich aktuell eine Ressource befindet.

(13)

4.1.4 Melden von Defekten und anstehenden Wartungen

Ist eine Ressource defekt oder es steht eine Wartung an, kann dies in der App gemeldet werden.

Dazu wird erfasst, ob es sich um einen Defekt oder eine Wartung handelt. Zusätzlich kann hier verlorenes Gerät gemeldet werden. Des Weiteren kann ein Freitext eingegeben werden, um zu beschreiben, um was für ein Problem es sich konkret handelt. In der Ressourcenübersicht soll angezeigt werden, ob eine aktuelle Meldung vorliegt. Wurde ein Problem gelöst, wird die Meldung als erledigt markiert und ist dann nur noch im Verlauf sichtbar.

4.1.5 Kategorisierung

Zur einfacheren Durchsuchbarkeit soll es eine Kategorisierung geben. Dazu gibt es eine Liste an Kategorien, die durch die jeweilige Firma verwaltet werden kann. Jede Ressource wird einer Kategorie zugeteilt. Bei einer Suche kann dann nicht nur nach dem Titel gesucht, sondern auch nach Kategorie gefiltert werden.

4.1.6 Ortsbestimmung und Kartendarstellung

Der aktuelle Standort von Ressourcen soll intelligent bestimmt werden. Hierzu sollen die In- formationen aus den Reservierungen sowie die standardmäßig in jeder Ressource hinterlegte Adresse genutzt werden.

Zur besseren Orientierung soll der aktuelle Standort in der App nicht nur als Adresse, son- dern auch auf einer Karte dargestellt werden. Außerdem soll eine Navigation zum jeweiligen Standort aufrufbar sein.

4.2 Nichtfunktionale Anforderungen

Das Finden von nichtfunktionalen Anforderungen hat sich als schwierig herausgestellt, da das Dokument Craftbuddy Definitionen hauptsächlich mit den funktionalen Anforderungen befasst.

Folgende Anforderung lies sich dennoch daraus ablesen. [Gmb19]

4.2.1 Einfache Bedienbarkeit

Die Anwendung soll eine niedrige Einstiegshürde haben. Insbesondere die App soll auch für technikfremde Personen ohne große Einlernphase genutzt werden können. Dazu soll das Nut- zerinterface möglichst intuitiv gestaltet und auf unnötige Optionen verzichtet werden.

Ebenfalls ist durch eine gute Lesbarkeit und ausreichend groß dimensionierte Bedienelemente sicher zu stellen, sodass die Anwendung auch auf einer Baustelle mit schmutzigen Fingern und bei ungünstigen Lichtverhältnissen möglichst bedienbar bleibt.

(14)

5 Konzept

Die Anwendung besteht aus drei Projekten. Zum einen das Backend, welches sich um die Schnittstellen und Datenbank kümmert, der Webapp, welche insbesondere das Anlegen und Verwalten von Ressourcen übernimmt und der mobilen App, welche für die Informationsdar- stellung unterwegs verantwortlich ist. Aufgrund dieser Aufgabenverteilung bietet es sich an, das Backend als erstes zu entwickeln, um die Schnittstellen für die Frontends bereitzustellen.

Als nächstes wird die Webanwendung umgesetzt werden, um Ressourcen anlegen zu können.

Abschließend kann dann die App implementiert werden, welche dann auf die, in der Weban- wendung erstellten, Datensätze zugreifen kann.

Abbildung 1: Diagramm der Anwendungskomponenten

Da die drei Projekte in sich eigenständig sind, sind diese austauschbar. Es ist problemlos mög- lich, ein weiteres Frontend zu implementieren, welches auf die API zugreift. Auch das Backend lässt sich durch ein anderes austauschen, so lange es dieselben Schnittstellen bereitstellt.

Die Modularität soll sich nicht nur auf die einzelnen Projekte beschränken, sondern auch die Projekte selbst sollen möglichst modular aufgebaut werden. Einzelne Komponenten sollen sich

(15)

einfach austauschen oder wiederverwenden lassen.

5.1 Backend

Das Backend gliedert sich in drei Teile. Die unterste Ebene ist die Datenbank, welche für das Speichern der Daten zuständig ist. Da Lumen über eine Abstraktionsebene für die Datenbank verfügt, werden verschiedene Datenbanksysteme unterstützt. Diese sind MySQL, Postgres, SQ- Lite, und SQL Server. Zwischen diesen Systemen kann frei gewählt werden. Zur Entwicklung wird ein MySQL-Server eingesetzt. [LLC19b]

Darüber einzuordnen sind die Controller, welche die Programmlogik beinhalten. In diesem Fall gestalten diese sich einfach, da lediglich die Modelle von der API entgegengenommen und in die Datenbank gespeichert, sowie von der Datenbank gelesen und an die API gegeben werden.

Wäre weitere Logik nötig, wie die Komprimierung von Bildern, würde dies ebenfalls in dieser Ebene geschehen.

Die dritte Ebene ist die API, welche die Schnittstellen nach außen anbietet. Die Schnittstel- len orientieren sich am Datenbankmodell. Von Pivot-Tabellen abgesehen gibt es für fast jede Tabelle einen API-Endpunkt. Jeder Endpunkt unterstützt GET, POST, PUT und DELETE, um Datensätze abzufragen, zu erstellen, aktualisieren oder zu löschen. Die Schnittstellen werden in der Klassendokumentation im Backend dokumentiert. Aus dieser Dokumentation wird automa- tisch eine vollständige Dokumentation der Schnittstellen erzeugt. Diese kann auf der beiliegen- den CD eingesehen werden.

5.1.1 Datenbank

Da sich die API an der Datenbank orientiert, bietet es sich an, als erstes ein Datenbankenmodell zu entwickeln, welches die geforderten Daten und Funktionen abbildet. Im Mittelpunkt stehen die drei Ressourcentypen Equipment, Material und Fahrzeuge. Die drei Typen haben einige Felder gemeinsam, unterscheiden sich aber auch in einigen. Diese lassen sich also durch Poly- morphie, mit der TabelleResourceals Basistabelle und den drei TabellenEquipment,Material und Vehicle als erbende Tabellen, abbilden. Während dieResource-Tabelle die gemeinsamen Felder abbildet, werden in den drei weiteren die Typenspezifischen Daten gespeichert.

Zur Verknüpfung der Ressourcentabelle mit den Tabellen der einzelnen Ressourcenarten sieht Lumen das Einfügen von zwei Spalten in die Ressourcentabelle vor. Dies ist zum einen ein ID-Feld und eine Spalte, welche angibt, auf welchen Ressourcentyp sich die Ressourcentabelle bezieht. [LLC19f]

Dies ist allerdings sowohl in der Umsetzung als auch auf Datenbankseite keine schöne Lösung.

Insbesondere ist ohne weiteres Kontextwissen, beim Betrachten der Datenbank, nicht ersicht- lich, mit welchen Tabellen die Ressourcentabelle verknüpft ist. Deshalb wird die Polymorphie

(16)

der Ressourcen als 1:1-Verknüpfung gelöst. Wie in Abbildung 2 zu sehen ist, verweisen dazu die einzelnen Ressourcentypen jeweils auf einen Eintrag in der Ressourcentabelle. So ist beim Betrachten der Datenbank direkt ersichtlich, welche Tabellen miteinander verknüpft sind.

Abbildung 2: Umsetzung der Polymorphie von Equipment, Verbrauchsmaterial und Fahr- zeugen

Allgemeiner Tabellenaufbau und Umsetzung der Polymorphie Der Aufbau einer Ta- belle und die Umsetzung der Polymorphie wird im Folgenden anhand der Equipment-Tabelle genauer beschrieben. Diese beinhaltet die Attribute, welche sich die verschiedenen Ressourcen- arten nicht teilen.

(17)

company_id

Da mehrere Firmen auf demselben Backend arbeiten sollen, muss sichergestellt werden, dass jeder Eintrag in jeder Tabelle eindeutig einer Firma zugeordnet werden kann. Des- halb findet sich in vielen Tabellen die company_id. So können jedem Kunden explizit seine Daten angezeigt werden. Außerdem wird so sichergestellt, dass ein Nutzer einer Firma nicht auf Daten einer anderen Firma zugreifen kann.

Diese Spalte ist ein Fremdschlüssel.

resource_id

Über dieses Feld wird die Verknüpfung mit dem jeweiligen Eintrag in der Ressourcenta- belle hergestellt, welche die gemeinsamen Felder aller Ressourcenarten beinhaltet. Dies ist die Umsetzung der Polymorphie auf Datenbankebene.

Diese Spalte ist ein Fremdschlüssel.

serial_number, buy_date, warranty_till

In diesen Spalten werden verschiedene Daten abgelegt. Ihnen kommt aus Sicht der Da- tenbank keine weitere Bedeutung zu.

created_at, updated_at

Diese beiden Felder werden automatisch durch Lumen angelegt und gesetzt.created_at beinhaltet dabei den Zeitpunkt, zu dem ein Datensatz angelegt undupdated_atden, an dem der Eintrag zuletzt bearbeitet wurde. [LLC19d]

deleted_at

Normalerweise werden Einträge beim Löschen komplett aus der Datenbank entfernt. Lu- men bietet die MöglichkeitSoftDeleteszu aktivieren. Hierzu muss in der Migration, welche die jeweilige Tabelle anlegt, die Option aktiviert werden. Nun werden Einträge beim Löschen nicht mehr aus der Datenbank entfernt, sondern lediglich als gelöscht mar- kiert. Dazu wird die Spaltedeleted_ateingefügt, welche standardmäßignullenthält.

Wird ein Eintrag gelöscht, wird der aktuelle Zeitpunkt in dieser Spalte eingetragen. Lu- men wird bei allen weiteren Anfragen diese Einträge ignorieren, außer dies wird explizit angegeben. [LLC19d]

Datenbankdesign Im Zentrum des Datenbankmodells stehen die Ressourcentabellen (siehe Abbildung 2). Viele Informationen, wie Automarken oder Kategorien, werden mehrfach benö- tigt und sollen im Frontend über ein Auswahlfeld festgelegt werden. Diese werden in eigenen Tabellen verwaltet und als Fremdschlüssel mit den Haupttabellen verknüpft.

(18)

Da eine Ressource mehrere Defektmeldungen und Reservierungen haben kann, sind diese ei- genständige Tabellen mit Fremdschlüsseln auf die Ressourcentabelle.

Abbildung 3: Beispielhafter Ausschnitt des Datenbankmodells

Als letztes gibt es noch Eigenschaften, von welchen eine Ressource mehrere haben kann, aber nicht nur in einer Ressource, sondern auch in anderen Tabellen verwendet werden. Dies tritt häufig in Verbindung mit anderen Komponenten aus dem gesamten Projekt auf. Beispielsweise hat eine Ressource ein oder mehrere Bilder, allerdings hat auch ein Benutzerprofil oder eine Baustelle ein Bild. In diesen Fällen werden die Tabellen über Pivot-Tabellen verknüpft. Abbil- dung 3 zeigt einen Ausschnitt des Datenbankmodells, welcher die Fälle beispielhaft darstellt.

(19)

5.2 Webanwendung

Der Fokus der Webanwendung liegt stark auf der Verwaltung der Stammdaten, da dies komple- xe Dialoge und oft längere Texte erfordert. Diese können nur umständlich auf einem mobilen Gerät eingegeben werden.

Wird die Seite zur Ressourcenverwaltung geöffnet, erscheint eine Übersicht aller existierenden Ressourcen. Über drei Tabs kann zwischen Equipment, Verbrauchsmaterial und Fahrzeugen gewechselt werden. Neue Ressourcen können über diese Seite angelegt, sowie bestehende be- arbeitet oder gelöscht werden. Hierzu wird ein Dialog implementiert, welcher sich dynamisch an die gewählte Aktion sowie den Ressourcentyp anpasst. Dies bietet sich an, da die drei Res- sourcentypen in ihrer Struktur ähnlich sind. Außerdem gelten beim Bearbeiten die gleichen Anforderungen, wie sie auch beim Erstellen vorhanden sind.

Eine komplexe Verarbeitung der Daten ist hier nicht notwendig. Diese werden lediglich aus der Benutzeroberfläche gesammelt und an die Services weitergegeben, welche sich um das Senden der Daten an das Backend kümmern.

5.3 App

Da die Anwendung hauptsächlich mobil mit dem Smartphone eingesetzt wird, liegt das Haupt- augenmerk des Projekts auf der App. Neben der Anzeige aller Ressourcen im Detail können auch Defekte gemeldet, Bilder aufgenommen, eine Navigation zum Standort gestartet und Re- servierungen vorgenommen werden.

Struktur und Aufbau der App ist, wie in Abbildung 1 zu sehen, ähnlich wie bei der Webanwen- dung. Es gibt ebenso die Darstellungsschicht sowie Services, welche die Kommunikation mit dem Backend übernehmen. Einen Unterschied gibt es bei den Controllern. Diese teilen sich in zwei Schichten. Zum einen gibt es Controller, welche sehr nah der Benutzeroberfläche zugeord- net sind und sich um die Aufbereitung der Daten zur Darstellung kümmern. Dies beinhaltet den Ephemeral State (siehe Abschnitt 3.2.2). Der zweite Teil der Controller ist verantwortlich für das Verwalten der lokal vorhandenen Daten. Dies entspricht dem, unter 3.2.2 State Management behandelten, App State und erfolgt in Form von MobX Stores.

5.3.1 Benutzeroberfläche

Entwurf der Benutzeroberfläche Zu Projektbeginn war es wichtig einen Entwurf zur Ge- staltung der Nutzeroberfläche anhand der gegebenen Anforderungen zu erstellen. Dieser ist in Abbildung 4 im Anhang zu sehen. Der so entstandene Entwurf dient dann als Vorlage bei der Umsetzung. Grundlegend entspricht die fertige App dem Entwurf, bei der Umsetzung wurde aber bei einigen Punkten vom Entwurf abgewichen. In Abbildung 5 ist ein Vergleich zwischen

(20)

Konzeptzeichnung und der fertigen App zu sehen. Bei dem Screenshot der App handelt es sich lediglich um den oberen Ausschnitt dieser Seite, in dessen weiterem Verlauf noch weitere De- tails zu sehen sind.

Abbildung 5: Vergleich Entwurf der Detailseite mit der fertigen App

Konzept zur einheitlichen Gestal- tung der Oberflächen Da die App aus vielen einzelnen Seiten und Dialo- gen besteht, ist das einheitliche Design ein wichtiges Thema. Zwar unterstützt das Flutter-Framework bereits Themes in den vielzähligen Widgets, welche das Framework mitbringt. In eignen Widgets müssen die Theming-Optionen aber kor- rekt genutzt werden, um ein einheitli- ches Design zu erreichen. Ein Text kann beispielsweise eine Überschrift oder ein- fach nur ein normaler Text sein. Um die- sem Problem entgegenzuwirken, müssen angepasste Widgets entwickelt werden, welche dann an den verschiedenen Stel- len genutzt werden und das Theming be- reits korrekt implementieren.

Scaffold Ein wichtiges Beispiel ist die angepassteScaffold-Komponente. Dieses Widget ist das Basiswidget jeder einzelnen Seite. Es ist verantwortlich für die Titelzeile, unter anderem mit Titel, Menü und Aktionsschaltflächen.

Im Vergleich zur normalen Komponente hat die angepasste Version einige Vorgaben und Funk- tionen erhalten. Der Seitentitel ist immer ein links orientierter Text. In der Standardversion kann als Titel jedes beliebige Widget genutzt werden, nicht nur ein Text. In der Titelzeile kann eine Suchleiste eingeblendet werden. Wird diese aktiviert, ersetzt diese den Seitentitel, bis sie wie- der geschlossen wird. Außerdem sind einige Aktionsschaltflächen vordefiniert. Diese sind somit immer gleich sortiert. Als letzte wichtige Besonderheit wurde das Hauptmenü in das Scaffold integriert. Dieses Menü wird überall automatisch eingesetzt und muss nicht in jeder Seite ein- zeln eingebaut werden. Es beinhaltet eine Liste aller Seiten der App. Die aktuell aktive Zeile wird farblich hervorgehoben. In der Abbildung 5 ist die Verwendung in Form einer Detailsei- te zu sehen. Dort wird das Menü automatisch ausgeblendet und durch einen Zurück-Button ersetzt.

(21)

List-Tiles Darüber hinaus gibt es noch einige neue List-Tiles. Ein List-Tile ist eine Zeile in einer Liste. Es kann alle Arten an Widgets beinhalten, beispielsweise Bilder, Überschriften, Icons oder normalen Text. Je nach Einsatzzweck werden hier unterschiedliche Inhalte ange- zeigt. Hierzu wurden zwei verschiedene List-Tiles definiert.

DefaultListTile

Dieses List-Tile wird für die Auflistung vieler Elemente genutzt, also unter anderem für die Übersicht über alle Arbeitsgeräte. Es kann Bilder, Titel, Nebentitel und frei definier- bare Texte darstellen.

DetailsListTile

Mit diesem List-Tile werden die Details zu einem Element dargestellt. Es beinhaltet einen Titel und den dazugehörigen Wert. Für lange Texte bietet es die Möglichkeit den Wert nicht neben dem Titel, sondern darunter darzustellen.

Von beiden List-Tiles gibt es auch eine ausklappbare Variante, welchen eine Liste von weiteren List-Tiles oder anderen Widgets übergeben werden kann.

Neben den beiden allgemeinen List-Tiles gibt es noch List-Tiles für spezielle Zwecke. Dazu zählen:

ImagesListTile

Zeigt eine Liste von Bildern als Vorschau an, kann diese als Vollbild darstellen und bietet die Möglichkeit neue Bilder aufzunehmen. In der Vollbildansicht können Bilder gelöscht oder als Primärbild markiert werden.

MapTile

Zeigt eine Adresse als Text und Karte an.

Diese beiden List-Tiles sind in Abbildung 5 abgebildet.

5.3.2 Netzwerkkommunikation

Die Kommunikation mit dem Backend ist ein sehr relevanter Bestandteil der App, da diese nicht eigenständig arbeiten kann. Allerdings ist nicht immer eine Netzwerkverbindung vorhan- den. In diesem Fall ist es zwar nicht notwendig, dass die App vollständig funktionsfähig ist, eine gewissen Funktionalität sollte aber gegeben sein. So sollte es zumindest möglich sein, von der Übersichts- auf die Detailseite einer Ressource zu wechseln. Hierzu gibt es Caching an zwei Stellen. Zum einen werden Daten in den MobX-Stores zwischengespeichert, zum anderen können auch die Anfragen an das Backend in den Services zwischengespeichert werden.

(22)

MobX-Store-Caching

Dieses Caching-Konzept wird bei korrekter Nutzung der Stores automatisch umgesetzt, da die Stores alle Daten vorhalten, welche aktuell in der Benutzeroberfläche angezeigt werden. Dies ist aber auch gleichzeitig ein Nachteil, da lediglich der konkret aktuell an- gezeigte Stand zwischengespeichert wird. Außerdem ist es schwierig und aufwendig alle Anfragen an das Netzwerk richtig zu handhaben, falls keine Netzwerkverbindung vor- handen ist oder die Verbindung fehlschlägt.

Service-Caching

Bei diesem Ansatz werden die Anfragen an das Backend zwischengespeichert. Das Ca- ching an dieser Stelle hat den großen Vorteil, dass aus Sicht der Stores immer eine Anfra- ge an das Backend gestellt werden kann und immer ein gültiger Datensatz zurück geliefert wird, unabhängig davon, ob aktuell eine Netzwerkverbindung besteht oder nicht. Hierzu wird ein Cache in den Services geführt, der den letzten bekannten Datensatz vom Ba- ckend enthält. Schlägt eine Anfrage fehl, nachdem bereits zu einem früheren Zeitpunkt eine erfolgreiche Anfrage durchgeführt wurde, wird das letzte Ergebnis zurückgeliefert.

Über Metainformationen, welche dieser Antwort beigefügt werden, erfahren die höheren Ebenen, dass der aktuelle Datensatz aus dem Cache stammt. Dieses Konzept reduziert auch die Anfragen an das Backend, da oftmals auch die Daten aus dem Cache ausrei- chend sind.

In Kombination können diese beiden Mechanismen eine gewisse Funktionalität ohne Netzwerk- verbindung sicherstellen.

(23)

6 Implementierung

Dieses Kapitel beschäftigt sich mit der Umsetzung des Konzepts. Die Hintergründe, sowie Pro- bleme und deren Lösung wird anhand des Ressourcentyps Equipment erläutert. Außerdem wird auf relevante Funktionen genauer eingegangen.

6.1 Backend

Bei der Backend-Entwicklung wurde der Bottom-up-Ansatz gewählt, da so neuer Code ohne größeren Aufwand direkt getestet werden kann. Würde der Top-down-Ansatz angewendet, wä- re ein Testen erst nach der vollständigen Implementierung einer Komponente möglich, oder es müsste mit Mock-Daten gearbeitet werden, was unnötigen Aufwand bedeutet. Im Folgen- den wird die Umsetzung, analog zum Abschnitt 5.1.1 Datenbank unter Konzept, anhand der Equipment Komponente gezeigt.

6.1.1 Erstellen der Datenbanktabellen

Alle Änderungen an der Datenbank, inklusive des Anlegens von Tabellen, erfolgen in Lumen über Migrations. Eine Migration ist eine Datei, welche jeweils eine Änderung an der Daten- bank durchführt. Dazu implementieren sie immer die zwei Methoden up()und down(). Der Codeausschnitt 1 zeigt die Migration zum Erstellen der Equipment-Tabelle. [LLC19c]

up()

Beim Migrieren wird diese Methode ausgeführt. Sie enthält die Änderungen, wel- che an der Datenbank durchgeführt werden sollen. Dies geschieht mit Hilfe von Schema::create. Die Funktion nimmt zwei Parameter entgegen. Der erste ist der Name der Tabelle und der zweite eine Funktion zur Interaktion mit der Tabelle. Eine neue Spal- te kann beispielsweise durch$table->bigInteger(’company_id’)angelegt werden.

Über die Optionen $table->timestamps() und $table->softDeletes(), werden die Felderupdated_at,created_atunddeleted_ateingefügt. Die Options SoftDe- letes muss später im Modell noch aktiviert werden. Fremdschlüssel werden mit Hilfe von

$table->foreign(...)gesetzt. [LLC19c]

(24)

down()

Diese Methode macht die Änderungen von up() rückgängig. Sie wird bei einem Rollback aufgerufen. In der Migration, welche eine neue Tabelle anlegt, ist es meist ausreichend, diese zu löschen. Dies kann mit dem BefehlSchema::dropIfExists(...)geschehen.

[LLC19c]

1 class CreateEquipmentTable extends Migration {

2 public function up() {

3 Schema::create('equipment', function (Blueprint $table) {

4 $table->bigIncrements('id');

5 $table->bigInteger('company_id',false,true);

6 $table->bigInteger('resource_id',false,true);

7 $table->string('serial_number', 50)->nullable();

8 $table->date('buy_date')->nullable();

9 $table->date('warranty_till')->nullable();

10

11 $table->timestamps();

12 $table->softDeletes();

13

14 $table->foreign('company_id')->references('id')->on('companies');

15 $table->foreign('resource_id')->references('id')->on('resources');

16 });

17 }

18

19 public function down() {

20 Schema::dropIfExists('equipment');

21 }

22 }

Quellcode 1: Migration zum Anlegen der Equipment-Tabelle

Mit diesem Konzept kann immer von jedem Stand der Anwendung auf die aktuelle Version ak- tualisiert werden, ohne dass die Konsistenz der Datenbank zum Problem wird oder aufwendige und fehleranfällige Update-Prozesse gepflegt werden müssen. [LLC19c]

6.1.2 Nutzung der Datenbank in Lumen

In Lumen erfolgen Datenbankzugriffe in aller Regel nicht über SQL-Statements, sondern über Modelle. Hierzu erhält jede Tabelle ein zugehöriges Modell. Alle Änderungen an den Modellen werden automatisch in die Datenbank übertragen. Im Sourcecode 2 ist das Equipment-Modell, welches zurequipment-Tabelle gehört, zu sehen.

(25)

1 class Equipment extends Model {

2 use SoftDeletes;

3

4 protected $fillable = [

5 'company_id',

6 'resource_id',

7 'serial_number',

8 'buy_date',

9 'warranty_till',

10 ];

11 protected $table = 'equipment';

12 protected $hidden = [

13 'id',

14 'resource_id',

15 'company_id',

16 'deleted_at',

17 'resource_id'

18 ];

19 protected $appends = [

20 'resource',

21 'onVehicle'

22 ];

23

24 public function resource() {

25 return $this->belongsTo('App\Models\Resources\Resource');

26 }

27 public function company() {

28 return $this->belongsTo('App\Models\Company');

29 }

30 public function onVehicle() {

31 return $this->belongsToMany('App\Models\Resources\Vehicle', 'vehicle_equipment');

,→

32 }

33 public function getOnVehicleAttribute() {

34 $onVehicle = $this->onVehicle()->first();

35 if (empty($onVehicle))

36 return null;

37 return $onVehicle->id;

38 }

39 public function getResourceAttribute() {

40 return $this->resource()->first();

41 }

42 }

Quellcode 2: Equipment-Modell

(26)

Die korrekte Benennung des Modells ist wichtig für die automatische Zuordnung zur Tabelle.

Hierzu wird der Tabellenname, welcher im Snake Case Format benannt sein muss, automatisch in Camel Case im Singular konvertiert. Alternativ, oder zur Sicherheit, kann der Tabellenname in der Variable$tableangegeben werden.

Alle Spalten der Tabelle stehen automatisch im Modell als Variable zur Verfügung. Relationen zwischen Tabellen müssen zusätzlich im Modell angegeben werden. Hierzu wird eine Funktion mit frei wählbarem Namen angelegt, über welche später die zugeordneten Datensätze abgeru- fen werden können. Die Funktionen bestehen lediglich aus einem Methodenaufruf. Je nach Art der Relation muss hier die richtige Methode gewählt werden. Für die Relation zur Basistabelle Resourceist dies beispielsweisebelongsTo(), wodurch das aktuelle Modell exakt einem Mo- dell der Tabelle Resource zugeordnet wird. Als Parameter muss der Methode belongsTo() ein Verweis auf das jeweilige Modell übergeben werden. Eine vollständige Liste aller mögli- chen Relationen kann der offiziellen Dokumentation entnommen werden1.

Um beim Anlegen eines neuen Datensatzes direkt das Modell befüllen zu können, müssen die Felder alsfillablemarkiert werden. Dies ist etwa vergleichbar mit einem Konstruktor bei norma- len Modellen. Dazu wird der Variablen$fillableein Array mit einer Liste aller ausfüllbaren Variablen zugewiesen.

Wird das Modell als Ergebnis einer API-Anfrage zurückgegeben, so wird das Modell automa- tisch, passend für die Antwort, umgewandelt. Welche Daten dabei genau gesendet werden, kann über die Variablen$hiddenund$appendsgesteuert werden.Appendshängt dem Modell wei- tere Daten an. Dies sind in der Regel die zuvor erwähnten, verknüpften Tabellen. Dazu wird der Variablen$appendsein Array mit anzuhängenden Attributen zugewiesen. Zu jedem dieser Attribute muss es eine entsprechende Methode geben. Dazu wird vor das Attribut das Wortget gesetzt und der Anfangsbuchstabe des Attributes großgeschrieben. Der Rückgabewert dieser Methode wird dann dem Attribut zugewiesen.Hiddenerlaubt es, Felder zu verstecken. Diese werden der Antwort an den Client nicht beigefügt. Ein häufiger Einsatzzweck ist das Verste- cken von Fremdschlüsseln, welche über$appendsohnehin der Antwort beigefügt werden. Die in vielen Tabellen vorkommende Spaltecompany_id, welche sicherstellt, dass ein Kunde auch nur seine Daten sehen kann, wird in der Regel ebenfalls ausgeblendet. In diesem Beispiel wird auch dieidversteckt, da diese im Controller durch dieidderResource-Tabelle ersetzt wird.

Dies ist Teil der Umsetzung der Polymorphie, wie sie im Abschnitt 5.1.1 erläutert wurde. Zu- letzt wird noch die Spaltedeleted_atausgeblendet, da dieses Feld immernullsein wird. Der Grund für dieses Feld ist die OptionSoftDeleteswelche beim Löschen von Datensätzen die- se nicht aus der Tabelle entfernt sondern diese lediglich, durch das Eintragen eines Zeitstempels in die Spalte deleted_at, als gelöscht markiert und künftig ausblendet. Dank dieser Option

1https://laravel.com/docs/5.8/eloquent-relationships

(27)

können Änderungen besser nachverfolgt und gegebenenfalls rückgängig gemacht werden.

Pivot-Tabellen bilden eine Ausnahme von diesem Schema, da diese für das Abbilden von n:n- Verknüpfungen notwendig sind. Deshalb erhalten diese Tabellen kein Modell. Aus Sicht der Modelle sind Pivot-Tabellen mit normalen Fremdschlüsseln vergleichbar, nur dass diese eine n:n-Verknüpfung abbilden können. [LLC19f]

6.1.3 Controller

In den Controllern befindet sich die eigentliche Programmlogik. Hauptsächlich werden hier die erhaltenen Daten validiert, die notwendigen Datensätze in der Datenbank angelegt und Antwor- ten an den anfragenden Client generiert.

Die POST-Methode zum Anlegen eines neuen Equipments deckt diese drei Fälle ab. Als erstes findet eine grundlegende Validierung des Requests statt. Hierfür bietet Lumen dievalidate()- Methode. Für jedes Attribut, welches Bestandteil des Requests sein kann, wird eine Reihe von Regeln definiert, die erfüllt sein müssen. Nur wenn alle Bedingungen erfüllt sind, gilt ein Re- quest in diesem Schritt als gültig. Für das Feld title ist die Regel required|string fest- gelegt. Dies bedeutet, dass das Feld vorhanden und vom Typ String sein muss. Die Regel sometimes|required|date_format:Y-m-d des Feldes buy_date sagt aus, dass, falls das Feld buy_date teil des Requests ist, dieses nicht null sein darf und ein Datum im Format Y-m-dsein muss. Eine vollständige Liste aller Regeln zum Validieren von Requests kann in der Dokumentation eingesehen werden 2. Schlägt eine dieser Regeln fehl, wird automatisch eine angepasste Antwort mit dem aufgetretenen Regelverstoß an den Client gesendet.

1 class EquipmentController extends Controller {

2 public function createEquipment(Request $request) {

3 $this->validate($request, [

4 'title' => 'required|string',

5 'manufacturer_id' => 'required|integer',

6 'type_id' => 'required|integer',

7 'category_id' => 'required|integer',

8 'location_id' => 'required|integer',

9 'serial_number' => 'sometimes|required|string',

10 'buy_date' => 'sometimes|required|date_format:Y-m-d',

11 'warranty_till' => 'sometimes|required|date_format:Y-m-d',

12 'technical_details' => 'sometimes|required|string',

13 'additional_info' => 'sometimes|required|string',

14 ]);

15

16 if ( // check foreign keys

17 empty($request->user()->company->getResourceType($request->type_id))

2https://laravel.com/docs/5.8/validation

(28)

18 || empty($request->user()->company->getResourceCategory($request->category_id))

19 || empty($request->user()->company->getLocation($request->location_id))

20 || empty($request->user()->company->getManufacturer($request->manufacturer_id))

21 )

22 return response(['status' => 'invalid foreign key'], 404);

23

24 DB::beginTransaction();

25 try {

26 $resource = new Resource([

27 'type_id' => $request->type_id,

28 'category_id' => $request->category_id,

29 'location_id' => $request->location_id,

30 'manufacturer_id' => $request->manufacturer_id,

31 'title' => $request->title,

32 'technical_details' => $request->technical_details,

33 'additional_info' => $request->additional_info,

34 ]);

35 $resource->save();

36 $equipment = new Equipment([

37 'company_id' => $request->user()->company_id,

38 'serial_number' => $request->serial_number,

39 'buy_date' => $request->buy_date,

40 'warranty_till' => $request->warranty_till,

41 'resource_id' => $resource->id,

42 ]);

43 $equipment->save();

44 } catch (\Exception $e) {

45 DB::rollBack();

46 return response(['status' => 'unexpected error occurred'], 417);

47 }

48 DB::commit();

49

50 return response(['status' => 'equipment created', 'id' =>

$equipment->resource_id], 201);

,

51 }

52 }

Quellcode 3: Ausschnitt des Equipment-Controllers mit der POST-Methode

Als nächstes folgen individuelle Validierungen. Hier sind dies die Überprüfung der Fremd- schlüssel. Jeder Fremdschlüssel, beispielsweise der Hersteller, muss in der Datenbank auch existieren, darf nicht als gelöscht markiert sein und muss zur jeweiligen Firma gehören. Es ist ausreichend zu prüfen, ob der eigenen Firma ein Hersteller mit der übergebenen Id zugeordnet ist. Das Ergebnis dieser Abfrage wird auch leer sein, wenn es den Eintrag überhaupt nicht gibt oder als gelöscht markiert ist. Jedem Request ist ein Benutzer zugeordnet. Dies geschieht über

(29)

den Authentifizierungstoken, welcher jeder Anfrage an das Backend beigefügt werden muss und wird von einem Modul im Hauptprojekt erledigt. Aus Sicht des Ressourcenmoduls hat also jeder Request ein user()-Attribut. Dem Benutzer ist immer eine Firma zugeordnet und mit der Firma sind die Hersteller, wie auch die anderen Tabellen der zu prüfenden Fremdschlüssel, verknüpft und können nach den ids aus dem Request durchsucht werden. Zur Abfrage muss somit nur durch die Modelle navigiert werden. Ist das Ergebnis einer dieser Abfragen leer, ist die Anfrage ungültig und es wird eine entsprechende Antwort generiert.

Alle Antworten sind grundlegend identisch aufgebaut. Neben einem HTTP-Statuscode enthal- ten diese immer einenstatus-Text, welcher das aufgetretene Problem genauer beschreibt, oder bei Erfolg einen Infotext beinhaltet. Bei einer erfolgreichen Anfrage wird auch immer die Id des jeweiligen Datensatzes mitgesendet. Wenn nötig können auch weitere Informationen der Ant- wort beigefügt werden.

Da nun alle Vorbedingungen erfüllt sind, kann der Datensatz in die Datenbank eingefügt wer- den. Da in diesem Fall mehrere Tabellen betroffen sind, lassen sich Datenbankbefehle nicht vollständig vermeiden. Die Datenbankverbindung muss explizit geöffnet und später wieder ge- schlossen oder die Änderungen durch ein Rollback rückgängig gemacht werden. Zum Anlegen wird ein neues Modell erstellt und direkt mit den, im Modell (siehe 6.1.2) alsfillable mar- kierten, Attributen befüllt. Durch den Aufruf der save()-Methode des neuen Objektes wird der Datenbankeintrag erzeugt und dem$resource-Objekt wird eineidzugewiesen. Im nächs- ten Schritt wird das Equipment angelegt. Dies funktioniert genauso wie bei dem$resource- Objekt. Dem Equipment wird direkt beim Anlegen dieresource_iddes$resource-Objektes zugewiesen.

Tritt bei diesem Vorgang ein Fehler auf, werden alle Änderungen durch ein Rollback wieder rückgängig gemacht. Ansonsten wird durch einen Commit die Änderung übernommen.

6.1.4 Anlegen von API-Endpunkten

Das Definieren der API-Endpunkte ist mit Lumen sehr einfach. Alle Endpunkte des kompletten Projektes werden in derapi.phperstellt, welche im Sourcecode 4 dargestellt wird. Für jeden Endpunkt wird dort ein Eintrag eingefügt, welcher den Request-Typ, den Endpunkt und einen Verweis auf den Controller mitsamt der Funktion enthält.

Das Rechtemanagement wird ebenfalls hier gehandhabt, indem der Middleware eine Liste von zulässigen Berechtigungen übergeben wird. Um deren Verwaltung kümmert sich ebenfalls das Hauptprojekt.

(30)

1 $router->GROUP([ 'prefix' => 'v1', 'middleware' => 'auth:api' ], function () use (

$router ) {

,→

2 $router->GROUP(

3 ['middleware' => ['role_or_permission:superAdmin|get_resources']], function () use ($router) {

,

4 $router->GET('/equipment', 'EquipmentController@getEquipment');

5 });

6 $router->GROUP(

7 ['middleware' => ['role_or_permission:superAdmin|create_resources']], function () use ($router) {

,

8 $router->POST('/equipment', 'EquipmentController@createEquipment');

9 });

10 $router->GROUP(

11 ['middleware' => ['role_or_permission:superAdmin|update_resources']], function () use ($router) {

,

12 $router->PUT('/equipment', 'EquipmentController@updateEquipment');

13 });

14 $router->GROUP(

15 ['middleware' => ['role_or_permission:superAdmin|delete_resources']], function () use ($router) {

,

16 $router->DELETE('/equipment/{equipment_id}', 'EquipmentController@deleteEquipment');

,→

17 });

18 });

Quellcode 4: Ausschnitt aus der api.php mit den Endpunkten für Equipment

6.2 Webanwendung

Das Modul in der Webanwendung integriert sich stark in das Hauptprojekt. In der Benutzero- berfläche wird aus dem Hauptprojekt die komplette Menüführung mitsamt den Einstellungen bereitgestellt. Dort fügt das Ressourcenmodul sein Menü mit ein. Ansonsten ist die Benutze- roberfläche eigenständig. Für die Netzwerkkommunikation stellt das Hauptprojekt den Base- Service zur Verfügung. Dieser stellt bereits die grundlegenden Funktionen zur Netzwerkkom- munikation bereit und kümmert sich um das Caching und die Authentifizierung. Damit der Base-Service einen gültigen Token zur Authentifizierung hat, muss sich der Benutzer zuerst einloggen. Auch diese Seite steht durch die Hauptanwendung bereits bereit. Die Webanwen- dung unterstützt auch mehrere Sprachen. Die Wahl der Sprache erfolgt über Komponenten des Hauptprojekts und kann beispielsweise auf der Login-Seite gewählt werden. Die ausgewählte Sprache wird dann in der kompletten Webanwendung genutzt.

(31)

6.2.1 Services

Die Services sind für die Kommunikation mit dem Backend zuständig. Ein Service ist eine Klasse, welche mit demBaseServiceerweitert wird. Bei einem einfachen Service, wie bei- spielsweise für die Hersteller, ist es ausreichend den Konstruktor zu nutzen. In diesem wird der Endpunkt festgelegt. Ansonsten werden dort nur zwei Objekte des Base-Services durchgereicht.

Im Falle des Equipment-Services ist es notwendig dieserializeDataunddeserializeData- Methoden zu überschreiben, da sich das Post- und Put-Modell von dem eines Get-Requests unterscheidet. In den überschriebenen Methoden wird anhand des Typs des Requests geprüft, ob eine besondere Behandlung erforderlich ist. Dies ist beim Equipment-Service für Post und Put der Fall. Da die Methoden zum Parsen der JSON-Daten in den Modellen enthalten sind, müssen lediglich die passenden Methoden im jeweiligen Modell aufgerufen werden. Ist keine gesonderte Behandlung nötig, wird dieserializeDataunddeserializeData-Methode des Base-Service aufgerufen.

1 export class EquipmentService extends BaseService {

2 constructor(cacheService: CacheService, requestService: RequestService) {

3 super('equipment', cacheService, requestService);

4 }

5

6 protected serializeData<T, V>(model: V, route: string, method: 'post' | 'get' | 'put' | 'delete'): T {

,

7 return (method && (method === 'put' || method === 'post'))

8 ? ApiEquipmentPostModel.of(model) as unknown as T

9 : super.serializeData(model, route, method);

10 }

11

12 protected deserializeData<T, V>(model: T, route: string, method: 'post' | 'get' | 'put' | 'delete', response: V): V {

,

13 if (method !== 'delete' && method !== 'get') {

14 if (Array.isArray(response)) {

15 return response.map(

16 (tmp) => ApiEquipmentModel.from(tmp)

17 ) as unknown as V;

18 }

19 return ApiEquipmentModel.from(model) as unknown as V;

20 } else {

21 return super.deserializeData(model, route, method, response);

22 }

23 }

24 }

Quellcode 5: Equipment-Service

(32)

6.2.2 Controller und Benutzeroberfläche

Die Anwendungs- und Darstellungslogik sind bei Angular stark verbunden. Jede Komponente besteht aus einer TypeScript-Datei, welche die Funktionalität eines Controllers übernimmt und einer HTML-Datei, welche in Verbindung mit einer SCSS-Datei für die Darstellung der Daten verantwortlich ist.

Die Benutzeroberfläche gliedert sich jeweils in zwei Komponenten. Zum einen die Übersichts- liste, welche alle existierenden Datensätze eines Typs darstellt und einem Dialog, welcher für das Erstellen und Bearbeiten zuständig ist.

Beide Komponenten wurden für Equipment, Material und Vehicles so gestaltet, dass diese vom Polymorphie-Ansatz profitieren können. Am deutlichsten ist dies beim Dialog zu erkennen. Vie- le Felder und der grundlegende Ablauf sind bei allen drei Arten identisch. Daher kann derselbe Dialog genutzt werden und es werden einige Felder ausgeblendet, wenn diese nicht benötigt werden. In der weiteren Programmlogik muss dann noch darauf geachtet werden, dass immer der richtige Service zur Kommunikation mit dem Backend aufgerufen wird.

Als Beispiel wird hier das Führerscheintypen-Feld von Fahrzeugen genutzt, da dieses den An- satz gut abbildet.

Darstellung Angular erlaubt das Ausführen simpler Befehle im HTML-Code. So ist es ein- fach möglich Daten kontextabhängig anzuzeigen. In Zeile 2 des Codeausschnittes 6 wird mit einem*ngIfdas Führerschein-Feld nur dargestellt, wenn der Dialog im Fahrzeug-Modus ge- öffnet ist.

Im weiteren Code werden einige Texte gesetzt und Events entsprechende Funktionen im TypeScript-Code zugewiesen.

Interessant ist hier noch dasmat-autocomplete. Über ein*ngFor wird für jeden Eintrag in derfilteredLicense-Variable eine Zeile im Autocomplete-Feld erzeugt. Für den Text wird die Bezeichnung des jeweiligen Führerscheintyps genutzt. Als Wert, welcher für das Klick- Event auf einen Eintrag in der Liste genutzt wird, wird die jeweilige Id genutzt. Dies ermöglicht später eine einfache und eindeutige Identifizierung eines angeklickten Eintrages.

1 <mat-form-field

2 *ngIf="resourceType === dialogResourceTypes.vehicle"

3 [ngClass]="'inputFullWidth'">

4 <input type="text"

5 placeholder= "{{localizationService.localizedMessages?.view.resource.create.

selectLicense.title}}"

,→

6 matInput

7 formControlName="license"

8 (ngModelChange)="changeLicenseInputHandler($event)"

(33)

9 [matAutocomplete]="autoLicense">

10 <mat-hint>

11 {{localizationService.localizedMessages?.view.resource.create.selectLicense.hint}}

12 </mat-hint>

13 <mat-error>

14 {{localizationService.localizedMessages?.view.resource.create.selectLicense.error}}

15 </mat-error>

16 <mat-autocomplete

17 [displayWith]="displaySelectedLicense.bind(this)"

18 #autoLicense="matAutocomplete">

19 <mat-option *ngFor="let license of filteredLicenses | async"

20 [value]=license.id>

21 {{license.type}}

22 </mat-option>

23 </mat-autocomplete>

24 </mat-form-field>

Quellcode 6: Autocomplete-Feld für Führerscheintypen

Controller Die TypeScript-Datei stellt die Funktionen bereit, welche die Oberfläche mit Da- ten versorgt und behandelt von ihr ausgelöste Events.

Jedes Formelement in der Benutzeroberfläche erhält eineFormControl[BD+19a]. Diese ver- waltet den aktuellen Wert der einzelnen Formelemente und kann diese mittels Validatoren auf ihre Gültigkeit prüfen [BD+19a]. Zur Strukturierung der FormControls werden diese in Form- Groups zusammengefasst [BD+19b].

Abbildung 6: Struktureller Aufbau des Dialogs Der Ressourcendialog ist in eine Haupt-

und zwei Subkomponenten aufgeteilt.

Die Hauptkomponente beinhaltet den Dialog selbst sowie die, nur beim Er- stellen einer neuen Ressource benötigte, erste Dialogseite zur Auswahl des Res- sourcentyps. Die beiden Subkomponen- ten beinhalten jeweils eine Seite für die

verpflichtenden sowie optionalen Felder. Dies ist schematisch in Abbildung 6 zu sehen. Da die Subkomponenten nur für das Ausfüllen der Felder zuständig sind, müssen die Werte in der Hauptkomponente verfügbar sein, sodass diese dort weiterverarbeitet werden können. Um dieses Problem zu lösen werden die FormGroups für die Subkomponenten in der Hauptkompo- nente initialisiert.

Die Initialisierung einer FormGroup ist im Quellcode 7 zu sehen. Der FormGroup werden im Konstruktor beliebig viele FormControls übergeben. Jede FormControl erhält einen Namen,

Referenzen

ÄHNLICHE DOKUMENTE

Neben metallischen Materialien, die in guter Näherung linear-elastisch modelliert werden können, beinhalten die zu untersuchenden Bauteile auch Kunststoffe, deren Materialverhalten

Unterrichtsbaustein 4 ist auf die fachbezogenen Kompetenzen 8.2 und 11.2 ausgerichtet (siehe Kapitel 2) und fördert die spezifische Kompetenz, dass Schülerinnen und Schüler bei

Nachdem in den vorherigen Abschnitten die Anforderungen einer blinden Signatur ¨ uber einer allgemeinen digitale Signatur sowie die Anforderungen von einigen bestehenden

Wir haben jeweils angenommen, dass der Startpunkt kein Eckpunkt der geschlossenen Kurve ist.. Wir hatten da in Beispiel 4.3.3 das Problem, dass die analytisch berechnete

Wenn es mehrere L¨ ocher gibt die zu nahe beieinander liegen, kann es passie- ren, dass keine Drehung existiert, so dass jede Folge von benachbarten Pixel, die eine

Ziel der ausgeschriebenen Arbeit ist es daher, einen iterativen Workflow zu erstellen, um den Entwurfs-, Simulati- ons- und Testprozess für mechanische Komponenten

Um das Verständnis, die Vorstellungen und Erfahrungen der Studierenden mit dem Thema Digitalisierung zu erheben, wurden die Studierenden zu Beginn beider

Auch wenn bei Kleinverbrauchern die reinen Energiekosten für die Leckagen relativ gering sind, ist das Abschalten nach Arbeitsschluss / am Wochenende eine unbedingt