TU Delft
Bachelorproject IN3405
dbQuery Verslag
Auteur: Rutger PLAK (#1358375) Opdrachtgever: Larry POORT, Anan6 BV
August 17, 2012
Supervisor: Gerd GROSS Faculteit: EWI
Voorwoord Om een bachelor Technische Informatica aan de Technische Universiteit van Delft af te ronden is het in de laatste fase van de opleiding verplicht een project te doen in de vorm van een stage bij een bedrijf of op de universiteit. Dit project heet het Bachelorproject en geldt voor 15 ECTS. Deze stage is uitgevoerd bij Anan6 BV te ’s Gravenhage in opdracht van eigenaar van Anan6 en tevens interne begeleider Larry Poort. Ik wil hem graag bedanken voor zijn hulp in de vorm van aanbevelingen en ervaringsgericht advies. Ook wil ik hem bedanken voor de ervaring die ik heb mogen opdoen tijdens deze stage. De heer Gerd Gross is tijdens deze stage de stagebegeleider namens de Technische Universiteit Delft geweest. Ook meneer Gross wil ik graag bedanken voor het mogelijk maken van deze stage.
1
Contents Voorwoord
1
Definities
7
I
8
Eindproduct
1 Inleiding
9
2 Eindproduct 2.1 Productomschrijving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.1 Het uit handen nemen van PHP programmatuur van de javascript programmeur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.2 Het uit handen nemen van MySQL programmatuur van de javascript programmeur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.3 Het uit handen nemen van het maken van klasses (functies) per tabel in javascript van de javascript programmeur. . . . . . . . . . . . . . . 2.2.4 Het minimaliseren van netwerkverkeer. . . . . . . . . . . . . . . . . . . 2.3 Functionaliteit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4 Gebreken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5 Toekomst en aanbevelingen . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11 11 11
3 Vooronderzoek 3.1 Plan van aanpak . . . . . . . 3.2 Userstories . . . . . . . . . . 3.3 Requirements Analysis . . . . 3.4 Architectural Design . . . . . 3.5 Test and Implementation plan
. . . . .
16 16 16 16 16 17
. . . . . . .
18 18 18 18 19 19 19 19
4 Procesverslag 4.1 01/07/2012 4.2 08/07/2012 4.3 15/07/2012 4.4 22/07/2012 4.5 29/07/2012 4.6 05/08/2012 4.7 12/08/2012
-
06/07/2012 13/07/2012 20/07/2012 27/07/2012 03/08/2012 10/08/2012 17/08/2012
. . . . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
3
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
. . . . .
. . . . . . .
12 12 13 13 13 14 14
IN3405 - Bachelorproject - dbQuery
4.8
Reflectie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5 Documentatie 5.1 Hoe dbQuery werkt . . 5.2 Installatie . . . . . . . . 5.3 Configuratie . . . . . . . 5.4 db() . . . . . . . . . . . 5.4.1 Voorbeeld 1 . . . 5.4.2 Voorbeeld 2 . . . 5.4.3 Voorbeeld 3 . . . 5.4.4 Voorbeeld 4 . . . 5.5 dbobject.getAttribute() 5.5.1 Voorbeeld 5 . . . 5.5.2 Voorbeeld 6 . . . 5.6 dbobject.setAttribute() 5.6.1 Voorbeeld 7 . . . 5.7 dbobject.save() . . . . . 5.7.1 Voorbeeld 8 . . . 5.7.2 Voorbeeld 9 . . . 5.8 dbobject.remove() . . . 5.8.1 Voorbeeld 10 . . 5.8.2 Voorbeeld 11 . . 5.9 Uitgebreid voorbeeld . . 6 Testen 6.1 Unit testing . . . . 6.1.1 PHP . . . . 6.1.2 Javascript . 6.2 Integration testing 6.3 Acceptance testing
II
CONTENTS
. . . . .
. . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
21 21 21 22 23 23 23 24 24 24 24 25 25 25 25 26 26 26 26 27 27
. . . . .
29 29 29 31 33 33
Vooronderzoek
34
A Plan van aanpak A.1 Voorwoord . . . . . . . . . . . . . . . . . . . . A.2 Samenvatting . . . . . . . . . . . . . . . . . . . A.3 Introductie . . . . . . . . . . . . . . . . . . . . A.3.1 Aanleiding . . . . . . . . . . . . . . . . A.3.2 Accordering en bijstelling . . . . . . . . A.3.3 Toelichting op de opbouw van het plan . A.4 Projectopdracht . . . . . . . . . . . . . . . . . . A.4.1 Projectomgeving . . . . . . . . . . . . . A.4.2 Doelstelling project . . . . . . . . . . . . A.4.3 Opdrachtformulering . . . . . . . . . . . A.4.4 Op te leveren producten en diensten . . A.4.5 Eisen en beperkingen . . . . . . . . . . . A.4.6 Voorwaarden . . . . . . . . . . . . . . .
4
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
35 35 35 35 35 36 36 36 36 36 37 37 37 37
IN3405 - Bachelorproject - dbQuery
CONTENTS
A.5 Planning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 B Userstories B.1 Introductie . . . . . B.2 Globale beschrijving B.3 Userstories . . . . . Objecten . . . . . . . Eigenschappen . . . Opslaan . . . . . . . Gebruiksgemak . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
39 39 39 39 39 40 41 41
C Requirements Analysis C.1 Introductie . . . . . . . . . . . . . . . . . . . . . . C.2 Overzicht . . . . . . . . . . . . . . . . . . . . . . . C.2.1 Clientside . . . . . . . . . . . . . . . . . . . C.2.2 Serverside . . . . . . . . . . . . . . . . . . . C.3 Functional requirements . . . . . . . . . . . . . . . C.3.1 Objecten . . . . . . . . . . . . . . . . . . . C.3.2 Semantische links . . . . . . . . . . . . . . . C.3.3 Eigenschappen van objecten . . . . . . . . . C.3.4 Opslaan . . . . . . . . . . . . . . . . . . . . C.3.5 Installatie . . . . . . . . . . . . . . . . . . . C.4 Quality requirements . . . . . . . . . . . . . . . . . C.4.1 Toegankelijkheid . . . . . . . . . . . . . . . C.4.2 Prestaties / Efficientie . . . . . . . . . . . . C.4.3 Onderhoudbaarheid . . . . . . . . . . . . . C.4.4 Betrouwbaarheid en beschikbaarheid . . . . C.4.5 Security . . . . . . . . . . . . . . . . . . . . C.4.6 Precisie en accuraatheid . . . . . . . . . . . C.5 Design requirements . . . . . . . . . . . . . . . . . C.5.1 Constraint gebaseerd op het type gebruiker C.6 Process requirements . . . . . . . . . . . . . . . . . C.6.1 Ontwikkelmethode . . . . . . . . . . . . . . C.6.2 Kosten en opleveringsdatum . . . . . . . . . C.7 Constraints . . . . . . . . . . . . . . . . . . . . . . C.8 Analysis models . . . . . . . . . . . . . . . . . . . . C.8.1 Use case models . . . . . . . . . . . . . . . C.8.2 Dynamische weergave dataverkeer . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
42 42 42 42 42 43 43 43 43 44 44 44 44 44 45 45 45 45 45 45 46 46 46 46 47 47 51
D Architectural Design D.1 Introductie . . . . . . . . . . . . . . . D.1.1 Doel van het systeem . . . . . D.2 Voorgestelde software architectuur . . D.2.1 Ontwerppatroon . . . . . . . . D.2.2 Ontwerpprincipes . . . . . . . . D.2.3 Systeemcomponenten . . . . . D.2.4 Subsysteem decompositie . . . D.2.5 Hardware / Software mapping
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
52 52 52 52 52 53 54 55 57
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
5
. . . . . . .
. . . . . . .
. . . . . . . .
. . . . . . .
. . . . . . . .
. . . . . . .
. . . . . . . .
. . . . . . .
. . . . . . . .
. . . . . . .
. . . . . . . .
. . . . . . .
. . . . . . . .
. . . . . . . .
IN3405 - Bachelorproject - dbQuery
CONTENTS
D.2.6 Persistent Data Management . . . . . . . . . . . . . . . . . . . . . . . 58 D.2.7 Global Resource handling en Access Control voor verschillende actoren 58 D.2.8 Randvoorwaarden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 E Test and Implementation plan E.1 Introductie . . . . . . . . . . . . . . . . E.2 Requirements met MoSCoW . . . . . . . E.2.1 Must . . . . . . . . . . . . . . . . E.2.2 Should . . . . . . . . . . . . . . . E.2.3 Could . . . . . . . . . . . . . . . E.2.4 Won’t . . . . . . . . . . . . . . . E.3 Implementatie en tests . . . . . . . . . . E.3.1 Volgorde voor het implementeren E.3.2 Documentatie . . . . . . . . . . . E.3.3 Unit testen . . . . . . . . . . . . E.4 Risico analyse . . . . . . . . . . . . . . . F Afwegingen F.1 Databasesoort . . . . . . . . . F.2 PHP vs. NodeJS . . . . . . . F.2.1 PHP . . . . . . . . . . F.2.2 NodeJS . . . . . . . . F.2.3 PHP vs. NodeJS . . . F.2.4 Conclusie . . . . . . . F.3 Standaardisatie berichten . . F.3.1 Server naar client . . . F.3.2 Client naar server . . F.4 Chaining . . . . . . . . . . . . F.4.1 Introduction . . . . . F.4.2 Directe chaining . . . F.4.3 Callbacks . . . . . . . F.4.4 Combinatie van beide
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . van de . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . features . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
60 60 60 60 60 61 61 61 61 62 63 63
. . . . . . . . . . . . . .
64 64 64 64 65 66 66 67 67 67 68 68 68 69 70
G Voorbeeldcode 71 G.1 NodeJS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 G.2 PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Bibliografie
76
6
Definities Apache Backwards Compatibility
+ +
Beta versie
+
Client
+
Clientside Database Databasecommunicatie Databasetabel dbQuery Documentatie Entry Foreign key HTML
+ + + + + + + + +
Javascript json
+ +
jQuery MySQL
+ +
MySQL-poort
+
NodeJS Open-Source PHP
+ + +
Pseudocode
+
Release versie
+
SCRUM
+
Server
+
Serverside Sleutel SVN (Subversion)
+ + +
Tabel Tabelnaam
+ +
Webservice die op serverside draait. Updates van de software zijn altijd te gebruiken met software die de oudere versie gebruikt. Update van software die nog in de testfase is maar wel openbaar wordt gemaakt. Computer die met behulp van bepaalde software (dbQuery.js) via een netwerk gebruikt maakt van de software op de server. Software aan de kant van de client. Georganiseerde collectie data op de server. Communicatie met de database. Tabel in een database die informatie bevat. Naam van de te ontwikkelen software. Document waarin alle code van de software staat uitgelegd. Regel in een databasetabel waarin gegevens staan. Sleutel in een databasetabel waarin een link ligt naar een andere databasetabel. HyperText Markup Language. Syntax die gebruikt wordt om webbrowsers websites te laten draaien en renderen. Programmeertaal die op clientside gedraaid wordt in webbrowsers. Door zowel PHP als Javascript te interpreteren manier van informatie coderen. Javascript library die browser compatibiliteit gemakkelijker maakt. Relational Database Management System die draait op serverside. Dit systeem omvat de database en heeft deze database is toegankelijk met een door MySQL gedefinieerde syntax. Netwerkpoort die open staat om verkeer binnen te kunnen krijgen voor MySQL. Software waarmee javascript op een server kan draaien. Manier van software uitgeven waarbij de code volledig openbaar is. Programmeertaal die op serverside gedraaid wordt en aanroepen kan verwerken vanaf de clientside. Niet werkende nep code die eenvoudig leesbaar is en een goed beeld geeft van de uiteindelijke code. Versie van software waarbij alle updates in de nieuwe versie volledig getest zijn. In korte sprints werkende software leveren. Na iedere iteratie blijft de software werken maar worden er nieuwe functionaliteiten toegevoegd. Machine waarnaar via een netwerkverbinding aanroepen naar gedaan kunnen worden. Software aan de kant van de server. Identifier van een entry in een database. Manier van versiebeheer waarbij alle gecommitte wijzigingen (revisions) ongedaan gemaakt kunnen worden. Zie databasetabel. Naam van een databasetabel.
7
Part I
Eindproduct
8
Chapter 1
Inleiding In de inleiding zal de gedachte achter het project worden uitgewerkt aan de hand van de huidige situatie, de tools die gebruikt worden om een bevredigend eindresultaat te leveren en een globale beschrijving van het eindproduct zelf. De opdrachtgever Anan6 BV ontwerpt, implementeert en onderhoudt webbased software. Deze software wordt gemaakt in het zelfgemaakte Content Management Systeem Anan6 CMS. Het betreft in veel gevallen intranet systemen van bedrijven. Zo heeft Anan6 BV een Bussiness Process Management System (BPMS) dat door vele bedrijven wordt gebruikt. Hierin kunnen bedrijven al hun administratie verwerken, aan de hand van door de bedrijven zelf vastgelegde protocollen en routeformulieren. Het BPMS zorgt ervoor dat alle taken behorende bij een bepaalde klantafhandeling worden uitgevoerd en het BPMS motiveert de gebruikers van het systeem deze handelingen zo snel en effici¨ent mogelijk af te handelen. Deze systemen maken bijzonder veel gebruik van databases. De data van alle klanten van de bedrijven staan in databases en moeten worden gebruikt in de systemen. Om deze data te kunnen gebruiken, moet deze worden opgehaald van de server op een zodanige manier dat de informatie bruikbaar is aan de clientside van de software. Anan6 BV wil de manier van informatie vergaren uit een database richting de clientside van de software vereenvoudigen, om tijd te besparen en de code van de systemen meer uniform te maken. Om dit te realiseren wordt dit bacheloreindproject uitgevoerd. In de loop der jaren zijn de systemen van Anan6 BV gestaag gegroeid. De grote en uitgenormaliseerde databases worden elk met aparte software benaderd. Door een javascript plugin te cree¨eren die de databaseafhandeling voor zijn rekening neemt, worden alle aparte softwareschillen om databasecommunicatie te realiseren overbodig. Belangrijker nog, toekomstige softwareschillen voor databasecommunicatie hoeven niet geschreven te worden. In dit document wordt de onderzoeksfase van het bacheloreindproject uitgewerkt. Aan de hand van deze onderzoeksfase is software ontwikkeld in de implementatiefase. Beide fases zijn uitgebreid gedocumenteerd en vastgelegd in dit document. Er wordt vooraf gepland en achteraf gereflecteerd. Ook zullen toekomstige features die de plugin zouden kunnen verbeteren uitgewerkt. Dit document zal tevens documentatie van de software bevatten. Ook zal geconcludeerd worden in hoeverre de software bruikbaar is en hoe deze open source software voor andere bedrijven bruikbaar kan zijn.
9
IN3405 - Bachelorproject - dbQuery
CHAPTER 1. INLEIDING
Huidige situatie Wanneer in de huidige situatie informatie nodig is of aan te passen moet zijn, zijn er verschillende softwarecomponenten nodig: • Er moet een javascript object worden gemaakt die overeenkomt met de databasestructuur, waar de vergaarde informatie moet worden opgeslagen. • Er is een javascript die een call doet naar de server, die de benodige informatie doorgeeft aan de server. • Op de server is een script dat luistert naar de javascript call. • Op de server wordt in MySQL informatie uit de database opgehaald of aangepast. • Door MySQL geretourneerde informatie wordt door PHP op de server verwerkt en teruggestuurd naar de client. • Het javascript bestand verwerkt de informatie die geretourneerd wordt door de server. Een programmeur van de systemen van Anan6 BV moet kennis hebben van zowel javascript, PHP als MySQL. Dit terwijl de programmeur de informatie slechts wil gebruiken in Javascript. Zowel PHP als MySQL zijn hierbij talen die slechts nodig zijn om de informatie te vergaren die hij in javascript wil gebruiken. Het wegnemen van PHP en MySQL uit de handen van de javascript programmeur zou tijd en geld schelen voor Anan6 BV. In hoofdstuk 2 wordt het geimplementeerde eindproduct uitgewerkt. Vervolgens zal in hoofdstuk 5 de documentatie van het eindproduct verwerkt zijn. In de Appendix kunnen de verschillende documenten, geschreven in de onderzoeksfase, gevonden worden.
10
Chapter 2
Eindproduct In dit hoofdstuk wordt het eindproduct van de opdracht beschreven. Er zal worden uitgewerkt wat het globale idee is achter het eindproduct. Ook wordt uitgewerkt waarom het product de huidige situatie zou verbeteren. Het nut van het eindproduct wordt hierin besproken aan de hand van de verschillen met de huidige situatie. De objectives van het product worden behandeld. Tevens zal worden uitgelegd waar het eindproduct niet zou werken en waar de huidige situatie de huidige versie van het eindproduct nog overtreft. Hierbij zal realistisch ingegaan worden op de gebreken van de huidige versie van het systeem. Ook zal worden uitgewerkt op welke manier het eindproduct in de toekomst kan worden uitgebreid.
2.1
Productomschrijving
Het eindproduct zal databasecommunicatie vanuit javascript op de clientside vereenvoudigen. Hierbij zullen objecten aan de hand van hun databasetabelnaam kunnen worden opgehaald uit de database. Om dit te bereiken zal een javascript programmeur slechts een enkele functie hoeven aan te roepen. Hierbij geeft hij aan welke databaseregels er moeten worden opgehaald. Dit kan aan de hand van een identifier of selectiecriteria. Wanneer de javascript programmeur deze objecten heeft aangemaakt, kan hij ze gebruiken als normale objecten. Hij kan hierbij alle eigenschappen die het object in de database bevat opvragen en aanpassen. Wanneer aanpassingen moeten worden opgeslagen in de database, kan de programmeur dit met een eenvoudige functie aanroep doen. Goed uitgenormaliseerde databases bevatten veel foreign keys. Deze sleutels wijzen naar andere tabellen in de database. Op deze manier kan een tabel ’klant’ naar een tabel ’adres’ linken. Wanneer drie klanten op hetzelfde adres wonen, bevatten zij alle drie een foreign key naar dezelfde regel in de tabel ’adres’. Hierbij wordt redundante informatie gereduceerd. Wanneer een javascript programmeur aan het klantobject het adres opvraagt, wordt een database object van het type ’adres’ teruggestuurd.
2.2
Objectives
De main objectives van de te implementeren javascript plugin zijn: 1. Het uit handen nemen van PHP programmatuur van de javascript programmeur. 2. Het uit handen nemen van MySQL programmatuur van de javascript programmeur. 11
IN3405 - Bachelorproject - dbQuery
CHAPTER 2. EINDPRODUCT
3. Het uit handen nemen van het maken van klasses (functies) per tabel in javascript van de javascript programmeur. 4. Het minimaliseren van netwerkverkeer. Wanneer deze objectives behaald zijn, kan gesproken worden van een succesvol verlopen project. Ieder van deze objectives zijn een zeer wenselijke situatie. De voordelen van het behalen van de objectives wordt in de komende secties uitgewerkt.
2.2.1
Het uit handen nemen van PHP programmatuur van de javascript programmeur.
In de huidige situatie moet een javascript programmeur wanneer hij objecten uit de database wil halen in PHP een door javascript aanspreekbare webservice schrijven. Deze webservice moet vervolgens objecten per tabel gebruiken. Deze objectklassen moeten ook worden geimplementeerd door de programmeur. Vanuit PHP wordt met MySQL gecommuniceerd met de database. De javascript programmeur zal om al deze functionaliteit te implementeren een goede kennis hebben van PHP. Deze kennis moet worden vergaard. Het leren van PHP kost zeer veel tijd. Javascript en PHP zijn functioneel vrij verschillende programmeertalen. De asynchroniteit van javascript en de sequentialiteit van PHP staan lijnrecht tegenover elkaar en vereisen een volledig andere behandeling van functie afhandelingen. Hierdoor kan het voor een programmeur lastig zijn om zowel Javascript als PHP volledig te beheersen. Wanneer het niet nodig is voor een javascript programmeur om PHP te beheersen, kunnen kosten worden bespaard op opleiding en/of training. Ook zou het tijdens de implementatiefase veel tijd kosten om voor iedere databasetabel een aparte PHP klasse te schrijven met een webservice erbij. Deze tijd moet iedere keer opnieuw worden gebruikt. Het wegnemen van deze verplichting scheelt veel tijd en geld.
2.2.2
Het uit handen nemen van MySQL programmatuur van de javascript programmeur.
Vanuit PHP kan worden gecommuniceerd met de databases. Anan6 gebruikt hiervoor MySQL. Het vergaren van informatie uit de database benodigd het uitvoeren van ’MySQLqueries’ naar de database. Deze MySQL queries hebben een syntax die is vastgesteld door MySQL. Iedere tabel heeft een andere structuur en zal anders benaderd moeten worden. Per tabel moet een programmeur zich verdiepen in de tabel en per gewenste actie is een andere MySQL querie nodig. De geretourneerde informatie vanuit MySQL moet worden verwerkt in PHP. Door de volledige databasecommunicatie van PHP naar MySQL te automatiseren, hoeft de programmeur geen verstand te hebben van MySQL. De volledige databasestructuur, datatypes, default values e.d. hoeven niet meer tot de kennis van de programmeur te behoren. De structuur van de tabellen staat vastgesteld in interne databasetabellen binnen MySQL. Automatisch uitlezen van deze tabellen kan deze functionaliteit mogelijk maken. Wanneer deze functionaliteit is geimplementeerd, hoeft een javascript programmeur geen verstand meer te hebben van MySQL. Hiermee kan Anan6 BV kosten besparen in het opleiden van werknemers om MySQL te beheersen. Tevens kan de tijd die wordt gebruikt
12
IN3405 - Bachelorproject - dbQuery
CHAPTER 2. EINDPRODUCT
om MySQL queries te implementeren nu voor andere doeleinden worden gebruikt en kan Anan6 BV hiermee tijd (en geld) besparen.
2.2.3
Het uit handen nemen van het maken van klasses (functies) per tabel in javascript van de javascript programmeur.
Iedere tabel in een database kan gezien worden als een klasse. Iedere regel in deze tabel kan gezien worden als een object van deze klasse. Om informatie uit de database bruikbaar te maken, is het handig om een schil om de databasetabel te maken. Deze schil wordt geimplementeerd in de vorm van een klasse. Deze klassen moeten per tabel worden geimplementeerd. Dit is veel werk, gezien alle functies voor iedere tabel voor iedere kolom opnieuw geimplementeerd moeten worden. Door het aanmaken van deze klassen te automatiseren, kan veel tijd worden bespaard. Dit is mogelijk door de interne MySQL structuur van de tabellen uit te lezen en aan de hand hiervan de eigenschappen van een klasse dynamisch te vullen.
2.2.4
Het minimaliseren van netwerkverkeer.
Wanneer er met een database gecommuniceerd moet worden vanuit een client, moeten er calls vanuit javascript naar de server plaatsvinden. Deze calls sturen informatie over een netwerk naar de server. De server stuurt op zijn beurt informatie terug over het netwerk naar de client. Om netwerkbelasting te minimaliseren moeten deze calls zo min mogelijk worden uitgevoerd. Er zijn op twee momenten netwerkverkeer noodzakelijk: • Bij het uitlezen van objecten uit de database. • Bij het opslaan van informatie in de database. Door slechts bij deze twee acties een netwerkcall te doen, wordt het netwerk zo min mogelijk belast. Wanneer een set objecten uit de database wordt gehaald, moeten deze in ´e´en call worden teruggestuurd. De aangemaakte objecten kunnen globaal worden bewaard aan de clientside. Zij hoeven niet steeds opnieuw ingelezen te worden.
2.3
Functionaliteit
Om gebruiksvriendelijk en eenvoudig te werken, moet dbQuery zo min mogelijk functies hebben om zoveel mogelijk functionaliteit uit te voeren. De functies die uitgevoerd moeten kunnen worden zijn: • Het ophalen van objecten uit de database. • Het ophalen van attributen van objecten uit de database. • Het aanpassen van attributen van objecten uit de database. • Het opslaan van aanpassingen aan objecten in de database. • Het verwijderen van objecten uit de database. • Het ophalen van een set objecten uit de database met selectiecriteria. • Het ’chainen’[F.4] van functies. 13
IN3405 - Bachelorproject - dbQuery
CHAPTER 2. EINDPRODUCT
Deze functionaliteiten worden bereikt met de volgende functies: • db() Haalt (een) object(en) op uit de database aan de hand van tabelnaam en selectiecriteria. • db.getAttribute(’naam’) Haalt attributen op van objecten. • db.setAttribute(’naam’,’waarde’) Past attributen van objecten aan. • db.save() Slaat aanpassingen op in de database. • db.remove() Verwijdert object uit de database. Een uitgebreide documentatie van de functies kan gevonden worden in hoofdstuk 5.4.
2.4
Gebreken
dbQuery kan gebruikt worden voor eenvoudige databasecommunicatie. Hierbij kunnen aanpassingen worden gedaan aan tabellen en er kunnen semantisch gelinkte objecten aangeroepen worden die op hun beurt weer als dbObject kunnen worden benaderd en aangepast. Hierbij is een groot gedeelte van functionaliteit van databases gedekt. Echter, er zijn functionaliteiten die dbQuery (nog) niet dekt: • Uitgebreide selectiecriteria In de huidige versie van dbQuery is het mogelijk om objecten uit de database te halen aan de hand van selectiecriteria. Zo kunnen alle ’klanten’ met leeftijd 23 uit de database worden gehaald, of alle ’winkels’ met ’soort’ ’supermarkt’ en ’zondagopen’ ’ja’. Echter, in de praktijk is het vaak nodig om ingewikkeldere selectiecriteria mee te geven. Bijvoorbeeld alle ’klanten’ van 23 tot 25 jaar. Of alle ’winkels’ met beschrijving lijkend op ’markt’. Deze uitgebreidere selectiecriteria zijn in de huidige versie van dbQuery niet mogelijk. In de huidige versie is het slechts mogelijk om ’=’ selectiecriteria mee te geven, gebundeld door ’en’. • n op n gelinkte tabellen In de praktijk zijn tabellen vaak n op n gelinkt. Hierdoor bestaat er een extra tabel met twee foreign keys. Het is in de huidige versie van dbQuery niet mogelijk om deze objecten op te halen. De n op n semantische links worden niet ondersteund. • Ingewikkelde select sets Vaak worden objecten uit een tabel geselecteerd door ingewikkelde selectiecriteria die meerdere tabellen overspannen. Zo kan het zijn dat alle ’klanten’ waarvan het ’adres’ (andere tabel) nog geen postcode bevat opgevraagd moeten worden. Dit wordt door de huidige versie van dbQuery nog niet ondersteund. Waar meerdere tabel overspannende selectiecriteria in direct MySQL in 1 query uitgevoerd kunnen worden, moet nu ge¨ıtereerd worden over alle rijen om te controleren of het adres een postcode bevat.
2.5
Toekomst en aanbevelingen
In volgende versies van dbQuery kunnen uitbereidingen worden gedaan om de huidige gebreken van dbQuery te verminderen. De plannen van Anan6 BV zijn om dbQuery uit te brengen en verder door te ontwikkelen. Enkele punten waarop dbQuery kan worden doorontwikkeld: 14
IN3405 - Bachelorproject - dbQuery
CHAPTER 2. EINDPRODUCT
• Selectiecriteria zijn nu slechts mogelijk met de ’=’ operator, gelinkt door ’en’ statements. In de toekomst moeten selectiecriteria in plaats van uit twee delen uit drie delen bestaan, zodat de selectie kan worden uitgebreid. Waar nu {’leeftijd’,’23’} een selectiecriterium is voor ’leeftijd=23’, moet het in de toekomst mogelijk zijn om {’leeftijd’,’<’,’23’} mee te geven, zodat alle personen onder de 23 jaar opgehaald worden. Ook moet het mogelijk zijn om text search te doen. Waar nu {’soort’,’supermarkt’} alle objecten met soort supermarkt ophaalt, moet het in de toekomst mogelijk zijn om {’soort’,’like’,’%markt%’} mee te geven, zodat ook een winkel met soort ’marktkraam’ wordt geselecteerd. • In de huidige versie worden callbacks in callbacks uitgevoerd. In de toekomst kunnen deze callbacks samen worden gevoegd tot ´e´en callback die pas uitgevoerd wordt als het diepste niveau is geinitialiseerd. Waar nu bij een driedubbel gelinkte getAttribute() drie aparte callbacks worden meegegeven, kan door javascript intern te manipuleren deze callbacks worden samengevoegd tot een enkele. Hiervoor is echter veel vooronderzoek nodig dat buiten dit project valt.
15
Chapter 3
Vooronderzoek Het te implementeren product is ontwikkeld na een uitgebreide onderzoeksfase. In deze fase zijn verschillende documenten geschreven en is veelvoudig overleg gepleegd. De documenten zijn in overleg met de opdrachtgever en betrokkenen geschreven door de ontwikkelaar. In de appendix zijn de documenten geschreven in het vooronderzoek te vinden.
3.1
Plan van aanpak
Het plan van aanpak bevat de globale gang van het project. De planning wordt uitgewerkt en afspraken tussen opdrachtgever en ontwikkelaar worden uitgewerkt.
3.2
Userstories
Om een indruk te krijgen wat het systeem moet kunnen zijn userstories geschreven. Dit zijn op een hoog abstractieniveau geschreven leesbare ’verhaaltjes’ waarin duidelijk wordt wat verwacht wordt van het systeem. Aan de hand hiervan is het mogelijk om verder onderzoek te verrichten naar de te implementeren globale functionaliteit.
3.3
Requirements Analysis
In de Requirements Analysis zijn de globale requirements van de userstories dieper uitgewerkt. Er wordt hier op een lager abstractieniveau uitgebreid uitgewerkt wat het systeem exact moet kunnen. Hierin worden de toekomstige functionaliteiten uitgewerkt in verschillende categori¨en. Ook worden de gevaren uitgewerkt. De Requirements Analysis dient als naslagwerk tijdens de ontwerpfase en implementatiefase. De gemaakte software moet voldoen aan de requirements.
3.4
Architectural Design
Het systeem wordt uitgewerkt in componenten en hun subcomponenten. De architectuur van de software wordt uitgewerkt. Hierbij wordt zorgvuldig rekening gehouden met de implementatiefase, maar vooral ook na de implementatiefase. De software moet goed onderhoudbaar en testbaar zijn. Door de componenten en subcomponenten zo zorgvuldig mogelijk
16
IN3405 - Bachelorproject - dbQuery
CHAPTER 3. VOORONDERZOEK
te ontwerpen kan coupling worden gereduceerd, afhankelijkheden worden geoptimaliseerd en kan ervoor worden gezorgd dat de software ook in de toekomst onderhoudbaar blijft.
3.5
Test and Implementation plan
In dit document wordt uitgewerkt wat er precies geimplementeerd gaat worden. Aan de hand van het MoSCoW model wordt aangetoond welke requirements uit de Requirements Analysis essentieel zijn voor het systeem, welke wenselijk zijn en welke zouden kunnen worden geimplementeerd mocht de tijd dat toelaten. Ook wordt uitgewerkt hoe de software getest gaat worden.
17
Chapter 4
Procesverslag In het komende hoofdstuk wordt per week van het project kort uitgelegd wat er gedaan is en hoe ik deze weken heb ervaren.
4.1
01/07/2012 - 06/07/2012
In de eerste week van het project is veelvuldig overleg gepleegd met de opdrachtgever. In deze gesprekken is naar voren gekomen wat de huidige situatie voor problemen oplevert. Ook is de visie besproken waarmee dit zou kunnen worden opgelost. Aan de hand van deze gesprekken heb ik userstories geschreven. Deze userstories zijn tot stand gekomen door gezamelijk overleg. Hierdoor was geen draft nodig, de final version is donderdag 5 juli getoond aan de opdrachtgever die het goed vond. Op vrijdag 6 juli ben ik begonnen met de requirements analysis.
4.2
08/07/2012 - 13/07/2012
Het was in deze week veel nodig om onderzoek te doen naar wat er mogelijk is en wat er onmogelijk is om te doen. Hiervan is de realiseerbaarheid van de idee¨en onderzocht en besproken. Aan de hand van deze besprekingen en de userstories zijn de requirements tot stand gekomen. Deze zijn uitgewerkt tot een requirements analysis. De draft heb ik op vrijdag (in overleg een dag nadat ik hem in zou leveren) ingeleverd bij de opdrachtgever.
4.3
15/07/2012 - 20/07/2012
In het weekend voorafgaand aan deze week heb ik onderzocht wat een architectural design moest bevatten, zodat ik maandag direct aan de slag kon. Aan de hand van de requirements analysis was het maken van een architectural design een aangename bezigheid die goed uitpakte. Ik heb hier in het weekend al een groot deel van geschreven. In datzelfde weekend had de opdrachtgever de requirements analysis doorgenomen. Na wat kleine aanpassingen heb ik dinsdag 13 juli de final version al kunnen overhandigen, waar de opdrachtgever erg blij mee was. De draft van het architectural design heb ik op de woensdag in de ochtend ingeleverd. Diezelfde middag heb ik met de opdrachtgever de architectural design besproken. Hij was positief verrast en vond de gevonden software architectuur erg goed. Na de bespreking ben ik begonnen met het schrijven van het test and implementation plan. Na 18
IN3405 - Bachelorproject - dbQuery
CHAPTER 4. PROCESVERSLAG
het schrijven van de requirements analysis en architectural design was dit niet lastig. Alles zat al in mijn hoofd. Op de donderdag heb ik de draft van het test and implementation plan ingeleverd. De opdrachtgever wilde meer weten over QUnit en PHPUnit, na deze onderwerpen te hebben besproken is de test and implementation plan goedgekeurd en kon begonnen worden aan de implementatiefase.
4.4
22/07/2012 - 27/07/2012
Het begin van de implementatiefase heeft voornamelijk bestaan uit het schrijven van de PHP testklassen en PHP klassen die gebruikt worden in dbQuery. Hierbij gaat het om dbObject.inc.php en dbAttribute.inc.php. De functionaliteit van de functies is geimplementeerd, waarbij steeds slimmere algoritmes elkaars werk opvolgden. Rekening is gehouden met het direct documenteren van de code. De gehele PHP klassen dbObject en dbAttribute zijn in de eerste week geimplementeerd. en testcases voor deze klassen zijn na de eerste week al geslaagd.
4.5
29/07/2012 - 03/08/2012
In het weekend voorafgaand aan deze week is de webservice q.php geschreven. Deze webservice maakt gebruik van dbObject.inc.php (en dbAttribute.inc.php) en is aanspreekbaar voor de buitenwereld en dus ook door javascript. Gedurende de week is de javascript van het project ontwikkeld. Dit begon met testklassen over de testdatabase. Na het implementeren van het javascriptdeel dat de webservice aanspreekt en dynamisch objecten aanmaakt, werden de verschillende methoden van dbquery geschreven. Hierna was het van belang datums goed af te handelen in javascript, daar javascript en PHP verschillend met datums om gaan. Het implementeren van dit laatste deel van het eidproduct was zeer motiverend. Deze week is vrijwel iedere avond tot zeer laat doorgewerkt omdat het heel bevredigend is steeds dichter bij het gewenste eindresultaat te komen. Vanaf vrijdag was dbquery bruikbaar in de console.
4.6
05/08/2012 - 10/08/2012
Deze week is de code gefinetuned. Documentatie van de code (zowel inline als in dit document) is verfijnd en algoritmen zijn versneld. Er is veel werk besteed aan het verfijnen van het eindverslag en het completeren van de code. Functionaliteit om callbacks uit te voeren is omgezet van timeouts (het stilleggen van de uitvoering van code tot andere code is uitgevoerd) naar sequentiele callbacks. Hier is veel tijd in gaan zitten maar het maakt de code veel schoner en onderhoudbaarder.
4.7
12/08/2012 - 17/08/2012
Omdat dbQuery opensource is, moet er een website zijn waar mensen de source-code kunnen downloaden. Zij moeten hier documentatie kunnen vinden van de code en zij moeten dbQuery kunnen uitproberen. In deze week is er veel aandacht besteed aan de website. Quentin Buiteman heeft een ontwerp gemaakt en samen met hem heb ik de website afgemaakt. Er is Engelstalige documentatie geschreven en een plaats waar mensen de code kunnen uitproberen (live). Tijdens het maken van de website is geconstateerd dat dbQuery niet werkte 19
IN3405 - Bachelorproject - dbQuery
CHAPTER 4. PROCESVERSLAG
onder Internet Explorer 7. Doordat deze webbrowser een zeer slechte error-console heeft, ben ik veel tijd kwijt geweest aan het oplossen van deze bug. Gedurende het gehele proces is gewerkt aan het verslag.
4.8
Reflectie
Tijdens de onderzoeksfase heb ik in samenwerking met Anan6 BV een heel ambitieus software plan geschreven. Tijdens de implementatiefase leek het er soms op dat deze plannen te ambitieus waren, maar doordat het architectural design goed gevolgd kon worden is de software volledig werkend opgeleverd. Vanaf het eerste moment dat een object uit de database kon worden gehaald (en er verder nog niets mee gedaan kon worden), groeide het enthousiasme enorm. Ik ben in de avonduren door gaan werken en heb veel kunnen doen. Het enthousiasme en de tevredenheid van opdrachtgever Larry Poort hielp er ook in mee. Ik heb het gevoel dat ik iets waardevols heb ontwikkeld en ben hier erg trots op.
20
Chapter 5
Documentatie In het volgende hoofdstuk wordt de documentatie van de clientside van javascript uitgewerkt. Voor iedere functie worden de parameters en de return values uitgewerkt. Voorafgaand hieraan wordt besproken hoe dbquery te installeren is op een server. Ook zullen er voorbeelden aanwezig zijn van hoe dbquery gebruikt kan worden.
5.1
Hoe dbQuery werkt
dbQuery is een javascript framework die het mogelijk maakt met elke MySQL database te communiceren door slechts gebruik te maken van normale javascript. Het is niet nodig om enige serverside programmeertaal te kennen om toegang te krijgen tot deze databases. Door de methode db() aan te roepen, kan de database worden benaderd. De db() functie retourneert een (collectie van) javascript object(en), met al zijn attributen geinitialiseerd zoals ze gedefinieerd zijn in de database. Het geretourneerde object bevat de methoden getAttribute(), setAttribute(), save() en remove(), welke het mogelijk maken om regels in de database toe te voegen, aan te passen en te verwijderen. Om een db() object te initialiseren, zijn er drie parameters nodig: • Selector De database en tabelnaam die aangesproken moeten worden. • Filter Een filterobject dat het mogelijk maakt om een selectie van rijen te maken uit de database. Standaard is dit de identifier. • Callback Deze functie wordt uitgevoerd wanneer alle rijen uit de database zijn gehaald en in javascript zijn geinitialiseerd. De parameter van de callback hangt af van de aanroep van de db() functie.
5.2
Installatie
Om dbQuery te gebruiken zijn er verschillende bestanden nodig:
Serverside • dbquery.config bevat informatie waarmee dbquery kan worden geconfigureerd (zie 5.3).
21
IN3405 - Bachelorproject - dbQuery
CHAPTER 5. DOCUMENTATIE
• dbObject.inc.php bevat de klasse dbObject, welk gebruikt wordt voor databasecommunicatie. • dbAttribute.inc.php bevat de klasse dbAttribute, welk gebruikt wordt door dbObject om attributen af te handelen. • q.php worden aanroepen naar gedaan vanaf de client. Dit bestand moet dan ook beschikbaar zijn voor buitenaf.
Clientside • jquery.js wordt gebruikt om ajax calls te maken naar de server. • dbquery.js handelt alle objecten en handelingen af. De bestanden dbObject.inc.php, dbAttribute.inc.php, dbquery.config en q.php moeten op de server worden geplaatst. In de HTML pagina’s waar dbQuery gebruikt gaat worden moeten jquery.js en dbquery.js worden geinclude in de header met de volgende code: 1 2 3 4
< head > < script type = " text / javascript " src = " / pad / naar / jquery . js " > < script type = " text / javascript " src = " / pad / naar / dbquery . js " >
5.3
Configuratie
In het bestand dbquery.config moet worden aangegeven hoe dbquery met de database kan communiceren. De volgende gegevens zijn aanpasbaar:
Verplicht • $__HOST_OF_THE_DATABASE Dit is de hostnaam van de database. Dit kan een IP-adres zijn, of een traceerbare URL. Standaardwaarde is localhost. • $__USER_OF_THE_DATABASE Dit is de databasegebruiker waarmee dbquery kan inloggen op MySQL. • $__PASSWORD_OF_THE_DATABASE Dit is het wachtwoord van de databasegebruiker waarmee dbquery kan inloggen op MySQL.
Optioneel • $__PATH_TO_DBOBJECT Wanneer dbObject.inc.php, dbAttribute.inc.php niet in dezelfde directory zitten als dbquery.config, moet dit veld worden ingevuld. • $__DEFAULT_DATABASE Om dbQuery zo eenvoudig mogelijk te gebruiken is het handig om een standaard database in te vullen (zie 5.4). 22
IN3405 - Bachelorproject - dbQuery
CHAPTER 5. DOCUMENTATIE
• $__READ_ONLY_MODE Wanneer dbQuery gebruikt wordt om slechts de database uit te lezen en geen aanpassingen te doen, moet dit op ’true’ worden gezet. Standaardwaarde is ’false’.
Objecten aanmaken 5.4
db()
De functie db is de hoofdfunctie van dbObject. db wordt gebruikt om objecten uit de database te maken of om een los object aan te maken. Hierbij zijn verschillende parameters mogelijk. Functie db( tabelnaam [, selectie ] [, handler(eventObject) ) Retourneert een array met objecten uit de database die aan de selectiecriteria voldoen, en een dbobject wanneer de selectie uit een integer bestaat. • tabelnaam Naam van de tabel waar een object van aangemaakt moet worden, optioneel voorafgegaan door de databasenaam gescheiden met een punt. • selectie De selectie heeft twee verschillende mogelijkheden: 1. Integer Een integer die het ID is van de op te vragen rij in de tabel. 2. Array Een array met selectiecriteria. De index is het attribuut, de waarde is de verplichte waarde. • handler(eventObject) Callback die wordt uitgevoerd wanneer de objecten uit de database zijn gehaald. Wanneer er slechts een enkel object uit de database moet worden gehaald, kan db als constructor worden gebruikt. Nu is de eventObject het object, in plaats van de array met objecten.
5.4.1
Voorbeeld 1
In dit eenvoudige voorbeeld wordt een klant uit de database gehaald en gelogd. Omdat het om slechts 1 klant gaat, wordt de constructor (new) gebruikt. 1 2 3
new db ( ’ klanten ’ ,1 , function ( klant ) { console . log ( klant ) ; }) ;
5.4.2
Voorbeeld 2
In dit voorbeeld wordt een klant uit de database gehaald en gelogd. Omdat nu de constructor niet gebruikt wordt, wordt een array geretourneerd. 1 2 3
db ( ’ klanten ’ ,1 , function ( klanten ) { console . log ( klanten [0]) ; }) ;
23
IN3405 - Bachelorproject - dbQuery
5.4.3
CHAPTER 5. DOCUMENTATIE
Voorbeeld 3
In dit voorbeeld worden alle klanten van 23 uit de database gehaald en wordt gelogd hoeveel dat er zijn. 1 2 3
db ( ’ klanten ’ , { ’ leeftijd ’: 23 } , function ( klanten ) { console . log ( klanten . length ) ; }) ;
5.4.4
Voorbeeld 4
In dit voorbeeld worden actieve klanten van 20 jaar oud uit de database gehaald en wordt hun bijnaam aangepast. 1 2 3 4 5
db ( ’ klanten ’ , { ’ leeftijd ’: 20 , ’ status ’: ’ actief ’ } , function ( klanten ) { klanten . forEach ( function ( klant , i ) { klant . setAttribute ( ’ bijnaam ’ , ’ 20 jarige nummer ’+ i ) . save () ; }) ; }) ;
Objecten gebruiken De volgende functies hebben betrekking tot objecten die in de callback van db kunnen worden gebruikt.
5.5
dbobject.getAttribute()
De functie getAttribute kan gebruikt worden om een attribuut van een object op te vragen. Wanneer dit attribuut een sleutel is naar een andere tabel, wordt dit object opgehaald. Functie .getAttribute( attribute, handler(eventObject) ) Retourneert het attribuut dat wordt opgevraagd. • attribute Op te vragen attribuut. • handler(eventObject) Een functie die wordt uitgevoerd wanneer het attribuut een sleutel is, waarin eventObject het opgevraagde dbObject is.
5.5.1
Voorbeeld 5
In dit voorbeeld wordt een klant aangemaakt en wordt zijn naam geoutput. Vervolgens wordt zijn adres opgevraagd, die in een aparte tabel staat. Hiervan wordt de postcode geoutput. 1 2 3 4 5 6 7 8
new db ( ’ klanten ’ ,1 , function ( klant ) { // normale attribuut opvraag console . log ( klant . getAttribute ( ’ naam ’) ) ; // adres linkt naar een andere tabel klant . getAttribute ( ’ adres ’ , function ( adres ) { console . log ( adres . getAttribute ( ’ postcode ’) ) ; }) ; }) ;
24
IN3405 - Bachelorproject - dbQuery
5.5.2
CHAPTER 5. DOCUMENTATIE
Voorbeeld 6
In dit voorbeeld wordt een callback vooraf gedefinieerd, en losgelaten op ieder adres van de klant met leeftijd 23. 1 2 3 4 5 6 7 8 9
var remove = function ( adres ) { adres . remove () ; } db ( ’ klanten ’ ,{ ’ leeftijd ’: ’ 23 ’} , function ( klanten ) { klanten . forEach ( function ( klant ) { // gebruik vooraf gedefinieerde callback klant . getAttribute ( ’ adres ’ , remove ( adres ) ) ; }) ; }) ;
5.6
dbobject.setAttribute()
De functie setAttribute kan gebruikt worden om een attribuut van een object aan te passen. Functie .setAttribute( attribute, newValue) Retourneert het dbObject waarop de functie wordt aangeroepen, zodat de functie gechained kan worden. • attribute Aan te passen kolomnaam. • newValue Nieuwe waarde van de kolom.
5.6.1
Voorbeeld 7
In dit voorbeeld wordt klant met identifier 1 uit de database gehaald. Zijn naam, voornaam en opleiding worden aangepast. 1 2 3 4 5 6
new db ( ’ klanten ’ ,1 , function ( klant ) { // aanpassingen maken klant . setAttribute ( ’ naam ’ , ’ Plak ’) . setAttribute ( ’ voornaam ’ , ’ Rutger ’) . setAttribute ( ’ opleiding ’ , ’ Technische Informatica ’) ; }) ;
5.7
dbobject.save()
De functie save kan gebruikt worden om gemaakte aanpassingen in een object op te slaan. Functie .save( handler(eventObject)) Retourneert het dbObject waarop de functie wordt aangeroepen, zodat de functie gechained kan worden. Doordat bij een save() een call naar de database moet worden gemaakt en soms informatie uit de database moet worden gehaald, is een callback beschikbaar.
25
IN3405 - Bachelorproject - dbQuery
5.7.1
CHAPTER 5. DOCUMENTATIE
Voorbeeld 8
In dit voorbeeld wordt klant met identifier 1 uit de database gehaald. Zijn naam, voornaam en opleiding worden aangepast. Vervolgens worden deze wijzigingen opgeslagen in de database. 1 2 3 4 5 6 7 8
new db ( ’ klanten ’ ,1 , function ( klant ) { // aanpassingen maken klant . setAttribute ( ’ naam ’ , ’ Plak ’) . setAttribute ( ’ voornaam ’ , ’ Rutger ’) . setAttribute ( ’ opleiding ’ , ’ Technische Informatica ’) // opslaan . save () ; }) ;
5.7.2
Voorbeeld 9
In dit voorbeeld wordt een nieuwe klant aangemaakt. Zijn naam, voornaam en opleiding worden aangepast. Vervolgens worden deze wijzigingen opgeslagen in de database. De identifier van de nieuwe klant is nodig, deze is pas beschikbaar na de databasecall. De identifier wordt gelogd in de callback. 1 2 3 4 5 6 7 8 9 10
new db ( ’ klanten ’ ,0 , function ( klant ) { // aanpassingen maken klant . setAttribute ( ’ naam ’ , ’ Plak ’) . setAttribute ( ’ voornaam ’ , ’ Rutger ’) . setAttribute ( ’ opleiding ’ , ’ Technische Informatica ’) // opslaan . save ( function ( klant ) { console . log ( klant . getAttribute ( ’ klant ’) ) ; }) ; }) ;
5.8
dbobject.remove()
De functie remove1 kan gebruikt worden om een object uit de database te halen. Functie .remove(handler(eventObject)) Na het verwijderen van een object uit de database, moeten er soms op een pagina taken worden uitgevoerd. Bij het verwijderen van een entry in een tabel, moet deze entry nadat hij uit de database is verwijderd ook van de pagina worden verwijderd. Hiervoor is een callback beschikbaar. eventObject is hierbij de identifier van het verwijderde object.
5.8.1
Voorbeeld 10
In dit voorbeeld wordt de klant met identifier 1 uit de database verwijderd. 1 2 3 4
new db ( ’ klanten ’ ,1 , function ( klant ) { // klant met id 1 uit de database halen klant . remove () ; }) ; 1 In
de initiele versie van dbQuery heette deze functie delete(), maar die functienaam was gereserveerd in Internet Explorer 7 en gaf problemen.
26
IN3405 - Bachelorproject - dbQuery
5.8.2
CHAPTER 5. DOCUMENTATIE
Voorbeeld 11
In dit voorbeeld wordt een klant verwijderd aan de hand van een verwijder button in een tabel 1 2 3 4 5 6 7 8 9 10
$ ( ’# myTable tr td . remove ’) . bind ( ’ click ’ , function () { // dbobject aanmaken van klanten met het id van de tr new db ( ’ klanten ’ ,$ ( this ) . parent () . attr ( ’ id ’) , function ( klant ) { // klant met id van de tr uit de database halen klant . remove ( function ( klantid ) { // tr met klantid verwijderen $ ( ’ tr # ’+ klantid ) . remove () : }) ; }) ; }) ;
5.9
Uitgebreid voorbeeld
In het volgende voorbeeld wordt een idee geschetst hoe een combinatie van de functies kan worden gebruikt om eenvoudig een tabel aan te maken van een databasetabel met selectiecriteria, en hoe aan de hand van user-events de database kan worden aangepast. Het scenario is volledig fictief en niet praktijkgericht. • customers • orders • orderitems • products De aanwezige foreign keys zijn: • orderitems.product → products • orderitems.order → orders • orders.customer → customers 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// Alle orderitems uit de database halen met een willekeurig s e l e c t i e c r i t e r i u m ( amount = 1 , in dit geval ) db ( ’ orderitems ’ , { ’ amount ’: 1} , function ( orderitems ) { var oTable = document . createElement ( ’ table ’) ; // voor ieder van de orderitems orderitems . forEach ( function ( orderitem ) { var oTr = document . createElement ( ’ tr ’) ; oTr . setAttribute ( ’ id ’ , orderitem . getAttribute ( ’ orderitem ’) ) ; var oTd = document . createElement ( ’ td ’) ; oTd . innerHTML = " Orderitem nummer : " + orderitem . getAttribute ( ’ orderitem ’) oTr . appendChild ( oTd ) ; // product ophalen orderitem . getAttribute ( ’ product ’ , function ( product ) { var oTd = document . createElement ( ’ td ’) ; oTd . innerHTML = product . getAttribute ( ’ name ’) + ’ : ’+ product . getAttribute ( ’ description ’) ; oTr . appendChild ( oTd ) ; }) ;
27
IN3405 - Bachelorproject - dbQuery
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
CHAPTER 5. DOCUMENTATIE
// order ophalen orderitem . getAttribute ( ’ order ’ , function ( order ) { var oTd = document . createElement ( ’ td ’) ; oTd . innerHTML = ’ aankoop gedaan op ’+ order . getAttribute ( ’ date ’) ; oTr . appendChild ( oTd ) ; // klant ophalen order . getAttribute ( ’ customer ’ , function ( customer ) { var oTd = document . createElement ( ’ td ’) ; oTd . innerHTML = customer . getAttribute ( ’ firstname ’) ; oTr . appendChild ( oTd ) ; var oTd = document . createElement ( ’ td ’) ; oTd . innerHTML = customer . getAttribute ( ’ street ’) ; oTr . appendChild ( oTd ) ; var oTd = document . createElement ( ’ td ’) ; oTd . innerHTML = customer . getAttribute ( ’ zipcode ’) ; oTr . appendChild ( oTd ) ; }) ; }) ; // wanneer er op de tr geklikt wordt , moet delOrder worden uitgevoerd . oTr . setAttribute ( ’ onClick ’ , delOrder ( this ) ) ; oTable . appendChild ( oTr ) ; }) ; document . g e t E l e m e n t s B y T a g N a m e ( ’ body ’) [0]. appendChild ( oTable ) ; }) ; // functie om de order en het orderitem te verwijderen en de klant aan te passen var delOrder = function ( oTr ) { // opvragen om welke orderitem het gaat var iId = oTr . id ; db ( ’ orderitems ’ , iId , function ( orderitem ) { // order opvragen orderitem . getAttribute ( ’ order ’ , function ( order ) { // klant opzoeken order . getAttribute ( ’ customer ’ , function ( customer ) { // klant aanpassen customer . setAttribute ( ’ name ’ , ’ nieuwe naam ’) . setAttribute ( ’ firstname ’ , ’ nieuwe voornaam ’) . save () ; }) ; // order verwijderen order . remove () ; }) ; // orderitem verwijderen orderitem . remove () ; }) ; // rij verwijderen uit tabel oTr . parentNode . removeChild ( oTr ) ; }
Dit voorbeeld bestaat uit het halen van orderitems uit de database. Hierbij worden alleen de orderitems waarbij 1 object is besteld uit de database gehaald. Voor ieder orderitem wordt een regel aangemaakt in een tabel. Het bestelde product wordt opgehaald en in de regel gezet. Ook de datum waarop de order is gedaan en de klant worden in dezelfde regel verwerkt. Daarnaast is er nog een verwijderknop, die een functie aanroept waarbij de order verwijderd wordt.
28
Chapter 6
Testen Voor het onderhoudbaar maken van software dienen testcases voldoende aanwezig te zijn. Wenselijk is het dat alle functionaliteit van de software getest wordt. Alle functies moeten worden getest, evenals alle branches binnen deze functies. Tevens moet de onderlinge samenhang van de software worden getest. Componenten moeten goed samenwerken. Ten slot is het van belang dat de software voor de eindgebruiker werkt zoals de eindgebruiker verwacht. Voor PHP is gekozen voor PHPUnit als testsoftware. Voor Javascript is gekozen voor QUnit als testsoftware. In het volgende hoofdstuk wordt uitgewerkt hoe de tests geschreven zijn en hoe het testproces verlopen is.
6.1
Unit testing
Bij unit testing worden individuele units van de software getest. Hierbij wordt iedere functie van een klasse getest. Er worden handelingen op objecten uitgevoerd en er wordt gecontroleerd of de verwachte output wordt bereikt.
6.1.1
PHP
In PHP zijn dbObject.inc.php en dbAttribute.inc.php getest. Hierbij worden alle functionaliteiten van dbObject.inc.php getest, waar dbAttribute aan hangt. Voor iedere functie van dbObject.inc.php zijn een of meerdere testcases geschreven, die alle mogelijkheden binnen de functies dekken. Hierbij is een line-coverage score van 100% behaald voor zowel dbObject.inc.php als dbAttribute.inc.php. Hiervoor is gebruik gemaakt van PHPUnit coverage:
Figure 6.1: Coverage report voor dbObject.inc.php & dbAttribute.inc.php
29
IN3405 - Bachelorproject - dbQuery
CHAPTER 6. TESTEN
Figure 6.2: Coverage report van dbObject.inc.php
Figure 6.3: Coverage report van dbAttribute.inc.php
Figure 6.4: Deel van de code in het coverage report Er volgt nu een van de testcases om een beeld te schetsen over testcases voor de PHP klassen binnen dbQuery: 1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* * * function to check if floats can be set * check if the float save function works */ public function testFloat () { $oObject = new dbObject ( ’ dbquerytestdb . products ’ ,1) ; $fOldValue = $oObject - > getAttribute ( ’ price ’) ; $oObject - > setAttribute ( ’ price ’ ,10.50) ; $oObject - > save () ; $oObject = new dbObject ( ’ dbquerytestdb . products ’ , $oObject - > getAttribute ( ’ product ’) ) ; $this - > assertEquals (10.50 , $oObject - > getAttribute ( ’ price ’) ) ; $oObject - > setAttribute ( ’ price ’ , $fOldValue ) ; $oObject - > save () ; }
Deze testcase is geschreven om te controleren of een float correct wordt opgeslagen in de database. Er wordt een object aangemaakt en zijn prijs wordt gezet. Het object wordt opgeslagen en de float moet goed in de database komen. Om dat te controleren wordt het object weer uit de database gehaald en wordt de waarde van de prijs gecontroleerd. Voor iedere functie zijn zulke testcases geschreven.
30
IN3405 - Bachelorproject - dbQuery
6.1.2
CHAPTER 6. TESTEN
Javascript
Omdat dbQuery een javascript plugin betreft, is er slechts ´e´en bestand dat getest moet worden. Dit bestand is dbobject.js. Hierin bestaan verschillende functies. De constructor db(), zoals omschreven in de documentatie, kan worden gebruikt om objecten aan te roepen uit de database. Vervolgens kunnen met deze objecten verschillende handelingen worden verricht. Door al deze handelingen in testcases uit te voeren en de output te vergelijken met de verwachtte output, kan de software worden getest. Nu haalt de javascript van dbQuery alle data uit de database. Omdat de serverside software onafhankelijk moet worden onderhouden van de javascript software, is het belangrijk dat de testcases van javascript niet falen als de server niet werkt. De componenten moeten onafhankelijk worden getest. Om dit te bereiken wordt de server gemocked aan de hand van MockJax. Er worden berichten teruggestuurd die aan de vooraf gedefinieerde standaarden voldoen. Hierdoor zullen javascript testcases gewoon slagen wanneer de server niets terugstuurt. De onderlinge samenwerking van de server en de client wordt behandeld bij integration testing. Voor alle functionaliteit van de javascript zijn testcases geschreven. Hierbij is een linecoverage van 84% behaald. Deze berekening is gemaakt door JSCoverage:
Figure 6.5: Coverage report voor dbquery.js & afhankelijke libraries (niet getest)
Figure 6.6: Deel van de code om een beeld te schetsen van een coverage report Er volgt nu een van de testcases om een beeld te schetsen over testcases voor de javascript binnen dbQuery: 1 2 3 4 5 6 7 8
/* * * tests if invalid dates are ignored */ new db ( ’ orders ’ , 0 , function ( order ) { var shopDate = new Date () ; order . setAttribute ( ’ date ’ , shopDate ) ; order . setAttribute ( ’ date ’ , ’ test ’) ; // Als de datum nog gelijk is aan shopDate , is de tweede setAttribute genegeerd
31
IN3405 - Bachelorproject - dbQuery
CHAPTER 6. TESTEN
Figure 6.7: Visuele output van qUnit 9 10 11
equal ( order . getAttribute ( ’ date ’) , shopDate , ’ Invalid date is ignored ’) ; start () ; }) ;
In deze functie doet db() een aanroep naar de server. Deze wordt gemocked door MockJax: 1 2 3 4 5 6 7 8 9 10 11
12
/* * NEW ORDER * */ $ . mockjax ({ url : ’q . php ’ , data : { ’ action ’: ’ get ’ , ’ data ’: { ’ tablename ’: ’ orders ’ , ’ identifier ’: null } }, responseText : ’ {" status ":" success " ," msg ":" The requested item ( s ) ha ( s ) ( ve ) been succesfully loaded from the database ." ," action ":" get " ," result ":{" found ":"" ," resultset ":[{" sDataBase ":" dbquerytestdb " ," sTablename ":" orders " ," sKeyName ":" order " ,... ’ // volledige response zoals vooraf gedefinieerd }) ;
32
IN3405 - Bachelorproject - dbQuery
6.2
CHAPTER 6. TESTEN
Integration testing
De software bestaat uit meerdere componenten en zal pas goed werken wanneer de componenten goed samenwerken. De samenwerking tussen deze componenten moet dus worden getest. Omdat de clientside van de software altijd de communicatie tussen client en server zal initieren, worden integration tests in javascript geschreven. Dezelfde functionaliteit als bij de javascript unit tests worden gebruikt, maar de communicatie tussen de client en de server wordt niet gemocked. Wanneer de server de juiste informatie terugstuurd, zullen de testcases slagen. Wanneer de server onjuiste informatie terugstuurt, of informatie op een verkeerde manier ingepakt, zullen de testcases falen. Ook wanneer javascript op een verkeerde manier informatie naar de webservice stuurt, zullen de testcases falen daar de webservice dan onverwachte output genereert.
6.3
Acceptance testing
Quentin Buiteman, een niet zeer ervaren javascript programmeur, bouwt de website van dbQuery en maakt hierbij gebruik van dbQuery. Er is gekozen voor Quentin Buiteman als acceptance tester omdat het systeem gebruikt moet kunnen worden door iedereen, met of zonder veel ervaring. Ook Mathieu Levens, programmeur bij moederbedrijf van Anan6, Com6, zal dbQuery in de praktijk gaan testen. Larry Poort wordt geen acceptance tester, daar hij javascript perfect beheerst en wellicht onlogische dingen als logisch kan ervaren, omdat hij weet hoe javascript code intepreteert. Gedurende het maken van de website hebben veel mensen buiten Anan6 zich ge¨ınteresseerd getoond in dbQuery. Acceptance testing zal voortduren totdat de software opensource op het internet verschijnt.
33
Part II
Vooronderzoek
34
Appendix A
Plan van aanpak A.1
Voorwoord
In dit hoofdstuk worden de afspraken, gemaakt tussen opdrachtgever Larry Poort van Anan6 en de ontwikkelaar Rutger Plak, uitgewerkt. De probleemstelling wordt behandeld, met uitwerking waarom dit een probleem is. Verder zal dit document de afspraken tussen de projectleden behandelen, evenals de planning van het project.
A.2
Samenvatting
In de stageperiode wordt onderzoek gedaan naar de efficientie van verschillende mogelijkheden om de gewenste Javascript plugin te ontwikkelen. Hierbij wordt een vooronderzoek bestaande uit userstories en drie rapporten vastgesteld. Deze rapporten worden onderzocht door de opdrachtgever en de input van de opdrachtgever wordt verwerkt en goedgekeurd. Hierna wordt aan de hand van deze rapporten in de ontwikkelfase in korte iteraties werkende software geleverd.
A.3
Introductie
Om tot een overeenstemming te komen tussen de opdrachtgever en de ontwikkelaar is dit hoofdstuk samengesteld. De mondelinge afspraken worden hier op papier gezet. Er worden afspraken gemaakt en eisen gesteld. De ontwikkelaar kan zich beroepen op dit document wanneer hij aan de eisen heeft voldaan, de opdrachtgever kan zich beroepen op dit document wanneer er niet aan de eisen is voldaan.
A.3.1
Aanleiding
Om in javascript met een database te communiceren is het gebruik van een Ajax call naar een PHP server vereist. Programmeurs moeten omslachtig veel code schrijven om data uit een database te halen en data aan te passen in de database. Door de hoogdrempeligheid van het gebruik van databasecommunicatie in javascript worden aanpassingen via javascript vaak vermeden. Dit is zeer onwenselijk. Databasecommunicatie in javascript is in veel gevallen een pre, vooral met de huidige web 2.0 technologie¨en.
35
IN3405 - Bachelorproject - dbQuery
A.3.2
APPENDIX A. PLAN VAN AANPAK
Accordering en bijstelling
Dit plan van aanpak is tot stand gekomen door veelvoudig overleg tussen opdrachtgever Larry Poort, betrokkenen Quentin Buiteman, Peter Verschoor, Martine Vrijburg en ontwikkelaar Rutger Plak. De wensen van de betrokkenen zijn naast de doelen van de opdrachtgever gelegd, de realiseerbaarheid van de wensen is onderzocht en het practisch nut van de wensen is overlegd. Gezamelijk zijn de wensen, planning en eisen overlegd en uitgewerkt in dit document.
A.3.3
Toelichting op de opbouw van het plan
In het komende hoofdstuk wordt de opdracht in al zijn facetten uitgewerkt. Er zullen geen implementatiedetails worden behandeld, het te bereiken doel en alle middelen die gebruikt gaan worden bij het behalen van dit doel worden w´el uitgewerkt.
A.4
Projectopdracht
Om databasecommunicatie eenvoudiger te maken wordt een javascript library geschreven die databasecommunicatie op een eenvoudige manier mogelijk maakt. De programmeur heeft geen kennis nodig van de structuur van de database. Hij hoeft alleen de tabelnamen te weten en de syntax van de library. Met deze library moeten objecten kunnen worden opgehaald uit de database, aangemaakt kunnen worden en aangepast kunnen worden. Semantisch gelinkte tabellen moeten het mogelijk maken om aan een uit de database gehaald object deze gekoppelde objecten te vragen. In de userstories wordt de verwachte functionaliteit uitgewerkt, zonder in te gaan op de implementatie van deze functies.
A.4.1
Projectomgeving
Met het huidige systeem kunnen databasehandelingen niet direct vanuit javascript worden afgehandeld. Javascript, dat op de clientside draait, doet een aanroep naar de server en geeft hier parameters aan mee. Deze parameters definieren de uit te voeren actie en de data die moet worden verwerkt of opgevraagd. Op de server draait PHP, dat de data extraheert en de databaseafhandeling regelt aan de hand van MySQL. Vervolgens wordt er door PHP een output gegenereerd die weer aan de clientside wordt gebruikt. Een javascriptprogrammeur die met een database wil communiceren, moet hierdoor zowel over javascript kennis beschikken, als PHP en MySQL.
A.4.2
Doelstelling project
De in de vorige sectie behandelde huidige situatie, waarin onnodig veel kennis en tijd vereist is voor databasecommunicatie met javascript, vormt een drempel die onnodig hoog ligt. Idealiter zou een javascript programmeur in javascript functies kunnen aanroepen die de volledige databasecommunicatie zouden afhandelen. De te ontwerpen en implementeren javascript library zou deze drempel stukken lager maken. Hierdoor zou veel tijdswinst optreden, programmeurs hoeven niet meer onnodig veel tijd te besteden aan de databaseafhandeling aan serverkant.
36
IN3405 - Bachelorproject - dbQuery
A.4.3
APPENDIX A. PLAN VAN AANPAK
Opdrachtformulering
Vanuit de opdrachtgever wordt een helder vooronderzoek verwacht om de doelstelling van het project te behalen. De afwegingen en keuzes die gemaakt zijn moeten gemotiveerd zijn met de daarbij behorende referenties en conclusies. Hiernaast moet er na het onderzoek een implementatiefase ingestart worden en moet er een product geleverd worden. De tijdsindeling van het vooronderzoek en de implementatiefase zal gaanderweg moeten worden bepaald. Regelmatig zal overleg plaatsvinden om de voortgang van het project te bespreken, eventuele wensen of vragen te communiceren en idee¨en uit te wisselen.
A.4.4
Op te leveren producten en diensten
Aan het eind van het project moet er een javascript plugin / library bestaan, die na installatie van additionele software (met behulp van NodeJS of PHP) op de server databasecommunicatie vanuit javascript mogelijk maakt. Deze software moet voorzien zijn van volledige documentatie. De software moet goed onderhoudbaar zijn en zal volledig open source beschikbaar zijn. Het vrijgeven van de software aan publiek zal gebeuren op servers van Anan6.
A.4.5
Eisen en beperkingen
De volgende eisen worden aan het eindproduct verbonden: • Er moet een javascript plugin / library zijn die de functies bevat om databasecommunicatie mogelijk te maken. • Er moet software zijn op de server (PHP of NodeJS) die de uiteindelijke communicatie met de database afhandeld. • De volledige code moet goed gedocumenteerd, onderhoudbaar en begrijpbaar zijn. • Afwegingen gemaakt bij het ontwerpen van de software moeten zwart op wit staan. Er zijn ook beperkingen: • NodeJS is niet ver genoeg ontwikkeld om volledige databasecommunicatie mogelijk te maken. Hierdoor bestaat de kans dat er PHP moet draaien op de server. Dit zal blijken tijdens de onderzoeksfase van het project. • Iedere databasetaal heeft een andere syntax. In eerste instantie zal de software moeten werken op MySQL (innoDB) of Firebird. De keuze wordt gemaakt in de onderzoeksfase. De software zal zo moeten worden ontwikkeld dat het mogelijk is om te porten naar andere databasesoorten als PostGRE SQL, etc.
A.4.6
Voorwaarden
Om succesvol de eisen te kunnen verwezelijken, is het belangrijk dat er veel overleg plaatsvindt. Dit is de verantwoordelijkheid van zowel de opdrachtgever als de ontwikkelaar. De ontwikkelaar moet ten alle tijden de mogelijkheid hebben om op korte termijn antwoord te krijgen op vragen. De opdrachtgever heeft op zijn beurt het recht om ten alle tijden de voortgang van het project na te vragen.
37
IN3405 - Bachelorproject - dbQuery
A.5
APPENDIX A. PLAN VAN AANPAK
Planning
Het is van belang dat er een planning bestaat en dat hier aan gehouden wordt. Er zijn in overleg data vastgesteld waaraan zowel de opdrachtgever en ontwikkelaar zich aan moeten houden. Fase Deadline Onderzoeksfase: Userstories (in overeenstemming) 05/07/2012 12/07/2012 Requirements Analysis (draft) Requirements Analysis (feedback) 14/07/2012 Requirements Analysis (final) 16/07/2012 16/07/2012 Architectural Design (draft) Architectural Design (feedback) 18/07/2012 Architectural Design (final) 20/07/2012 Test & Implementation plan (draft) 18/07/2012 20/07/2012 Test & Implementation plan (feedback) Test & Implementation plan (final) 22/07/2012 Ontwikkelfase: Iedere week demonstratie 25/07/2012 - 20/08/2012 Op te leveren product 25/08/2012
38
Appendix B
Userstories B.1
Introductie
In dit document worden de userstories uitgewerkt voor dbQuery. Deze software wordt ontwikkeld door Rutger Plak in opdracht van Larry Poort, Anan6. De userstories omvatten de functionaliteit waaraan de software moet voldoen, in een eenvoudig leesbare en te begrijpen manier.
B.2
Globale beschrijving
Om in javascript met een database te communiceren is het gebruik van een Ajax call naar een PHP server vereist. Programmeurs moeten omslachtig veel code schrijven om data uit een database te halen en data aan te passen in de database. Door de hoogdrempeligheid van het gebruik van databasecommunicatie in javascript worden aanpassingen via javascript vaak vermeden. Om databasecommunicatie eenvoudiger te maken wordt een javascript library geschreven die databasecommunicatie op een eenvoudige manier mogelijk maakt. De programmeur heeft geen kennis nodig van de structuur van de database. Hij hoeft alleen de tabelnamen te weten en de syntax van de library. Met deze library moeten objecten kunnen worden opgehaald uit de database, aangemaakt kunnen worden en aangepast kunnen worden. Semantisch gelinkte tabellen moeten het mogelijk maken om aan een uit de database gehaald object deze gekoppelde objecten te vragen. In de userstories wordt de verwachte functionaliteit uitgewerkt, zonder in te gaan op de implementatie van deze functies.
B.3
Userstories
Objecten Een entry in een databasetabel kan worden gezien als een object. Informatie die in de tabel staat is van toepassing op de rest van de regel. Iedere databaseregel is een alleenstaande ’brok’ informatie. Deze informatie kan worden gezien als een object met zijn eigenschappen. Deze objecten zouden eenvoudig opvraagbaar moeten zijn vanuit de database, zodat deze objecten te gebruiken zijn. Ook moet het mogelijk zijn een collectie objecten op te vragen door een zoekfunctie aan te roepen met eigenschappen. Alle objecten met de bepaalde eigenschap(pen) moeten dan als collectie worden geretourneerd.
39
IN3405 - Bachelorproject - dbQuery
APPENDIX B. USERSTORIES
1. Object uit de database halen Als een gebruiker, wil ik objecten uit de database kunnen halen, door de tabelnaam en de primary key als parameters mee te geven.
2. Lege objecten aanmaken Als een gebruiker, wil ik een nieuw object kunnen aanmaken van een databasetabel, die alle eigenschappen heeft van de tabel, door slechts de tabelnaam mee te geven als parameter.
3. Verzameling objecten uit de database halen Als een gebruiker, wil ik verzameling objecten uit de database kunnen halen, door de tabelnaam en selectiecriteria als parameter mee te geven.
In een semantisch gelinkte en genormaliseerde database worden databasetabellen gelinkt. Een tabel ’personen’ is in een genormaliseerde database gelinkt aan een tabel ’adressen’. Een object van type ’persoon’ heeft dan een relatie met een object van het type ’adres’. Deze semantische links moeten dynamisch opvraagbaar zijn.
4. Objecten aan objecten vragen Als een gebruiker, wil ik van een object[1] semantisch gelinkte objecten kunnen ophalen uit de database, door een functie van het object aan te roepen, met als parameter de gewenste link.
Eigenschappen In iedere databasetabel staat informatie. Deze informatie bestaat veelal uit eigenschappen. Wanneer een gebruiker een object heeft aangemaakt[1][2][3], moeten deze eigenschappen voor de gebruiker ook extraheerbaar zijn vanuit het object. Dit wordt bewerkstelligd door een functie te maken waarmee een eigenschap van een object kan worden opgevraagd. De eigenschappen van het object moeten op eenzelfde manier gewijzigd kunnen worden.
5. Eigenschappen ophalen Als een gebruiker,
40
IN3405 - Bachelorproject - dbQuery
APPENDIX B. USERSTORIES
wil ik van objecten[1] eigenschappen ophalen uit de database.
6. Eigenschappen aanpassen Als een gebruiker, wil ik van objecten[1] eigenschappen kunnen wijzigen.
Opslaan Wanneer van een object[1][2][3]eigenschappen worden aangepast[6], moeten deze aanpassingen kunnen worden opgeslagen. Ook wanneer er een nieuw object is aangemaakt[2]en de eigenschappen zijn ingevoerd[6], moet dit object als nieuwe regel kunnen worden opgeslagen in de database.
7. Wijzigingen opslaan Als een gebruiker, wil ik van objecten[1] gemaakte wijzigingen[6] kunnen opslaan in de database.
Gebruiksgemak Om goed gebruik te kunnen maken van dbQuery, zou het omslachtig zijn om bij ieder gebruik opnieuw alle instellingen te moeten meegeven. Een apart configuratiebestand dat slechts eenmaal ingevuld hoeft te worden zou voldoende moeten zijn om overal deze instellingen te kunnen gebruiken. De server zal slechts eenmaal geconfigureerd te hoeven worden om gebruik te kunnen maken van dbQuery.
8. Instellingen Als een gebruiker, wil ik na eenmaal een configuratiebestand te hebben ingevuld, nooit meer serverinstellingen te hoeven wijzigen.
41
Appendix C
Requirements Analysis C.1
Introductie
Het systeem zal javascript programmeurs op een eenvoudige manier mogelijk maken om met een database te communiceren. Zonder enige kennis van PHP en MySQL zal een javascript programmeur met een database kunnen communiceren om data uit de database te halen en data in een database aan te passen of toe te voegen. De vereenvoudiging van de databasecommunicatie staat centraal. Om dit doel te bereiken moet een systeem worden gemaakt via waar een javascript functie aan de hand van PHP[F.2]met de database communiceert zonder dat de javascript programmeur dit weet of hier last van heeft. Een eenmalige installatie en configuratie zal voldoende zijn om op verschillende plaatsen de database te kunnen benaderen vanuit javascript.
C.2
Overzicht
De applicatie zal uit twee delen bestaan; aan de clientside zal een javascript plugin draaien en op de serverkant een service die luistert naar deze client.
C.2.1
Clientside
• Er zal een javascript bestand zijn dat ge¨ınclude moet worden in de HTML-source. • De code in javascript draait op de computer van de client, oftewel degene die de site / intranet bezoekt. • Deze code zal alle functionaliteit omvatten van de javascript objecten en zal aanroepen doen naar de serverkant.
C.2.2
Serverside
• Op de serverside zal een PHP bibliotheek aanwezig zijn. • Deze omgeving zal de aanroepen vanaf de clientside ontvangen en verwerken. • Deze omgeving zal de database aanspreken om de communicatie met de database vanaf de client-side volledig te maken.
42
IN3405 - Bachelorproject - dbQuery
APPENDIX C. REQUIREMENTS ANALYSIS
• Er zal data worden (terug)gestuurd vanaf de server richting de client, om de client van feedback en/of informatie vanuit de database te voorzien. Deze twee omgevingen samen zullen dbQuery vormen.
C.3 C.3.1
Functional requirements Objecten
(req 1) Door een tabelnaam en een primary key mee te geven aan een functie wordt een entry uit de database gehaald en wordt hier een object van gemaakt. (req 2) Door een tabelnaam mee te geven aan een functie wordt een object aangemaakt die alle eigenschappen bezit van een databasetabel, zodat dit lege object een nieuwe databaseregel voorstelt. (req 3) Door een tabelnaam en een eigenschap van het object mee te geven aan een functie, wordt een verzameling objecten uit de database gehaald die aan deze restrictie voldoet. Zo kunnen alle objecten van type x met eigenschap y gelijk aan z in een collectie worden opgevraagd. (req 4) Door een functie van een object[19]aan te roepen, kan dit object uit de database worden verwijderd.
C.3.2
Semantische links
(req 5) Wanneer in tabel x een foreign key bestaat in de database, linkt deze sleutel naar tabel y. Wanneer aan een object van type x [19]aan de hand van een functie de tabelnaam van y wordt gevraagd, wordt een object aangemaakt van type y en wordt deze teruggestuurd. Zo kan een gebruiker aan de hand van (pseudocode) ”x.get(’y’);” een object van type y ontvangen, zonder iets te weten van dit object. (req 6) Wanneer in tabel x een foreign key bestaat in de database, linkt deze sleutel naar tabel y. Wanneer aan een object van type y aan de hand van een functie de tabelnaam van x wordt gevraagd, wordt een set objecten aangemaakt van type x die een foreign key hebben naar de betreffende entry van type y, en wordt deze collectie teruggestuurd. Zo kan een gebruiker aan de hand van (pseudocode) ”y.get(’x’);” een set objecten van type x ontvangen, zonder iets te weten van deze objecten.
C.3.3
Eigenschappen van objecten
(req 7) Wanneer een object aan is gemaakt van type x [19], waarbij een identifier is meegegeven, zijn aan dit object eigenschappen op te vragen. Iedere kolom in de tabel is een eigenschap die via een functie opvraagbaar is. (req 8) Wanneer een object aan is gemaakt van type x [19], waarbij een identifier is meegegeven, zijn alle eigenschappen van dit object aanpasbaar. Iedere kolom in de tabel kan door middel van een functie worden aangepast in dit object. (req 9) Wanneer een object van type x [19], waar in tabel x in de database een foreign key bestaat naar y [5], is aangemaakt, kan dit object worden gekoppeld aan een object van type y. 43
IN3405 - Bachelorproject - dbQuery
C.3.4
APPENDIX C. REQUIREMENTS ANALYSIS
Opslaan
(req 10) Wanneer een nieuw object is aangemaakt[2], kan dit object nadat de eigenschappen zijn ingevoerd worden opgeslagen in de database als een nieuwe regel. (req 11) Wanneer een object uit de database is gehaald[19], waarna eigenschappen zijn aangepast[8], kunnen deze wijzigingen in de database worden opgeslagen.
C.3.5
Installatie
(req 12) Er bestaat een configuratiebestand waar de inloggegevens, de open mysql-poort en de databasenaam worden opgeslagen. Deze wordt automatisch ingeladen, zodat de gebruiker deze kan hergebruiken zonder steeds opnieuw de instellingen in te voeren. (req 13) De client-side code zal downloadbaar zijn om op eigen server te hosten. Ook zal er een link bestaan die altijd naar de laatste versie wijst, zodat deze kan worden geinclude. (req 14) De updates zullen ten alle tijden backwards compatible worden gehouden. De gebruiker zal geen code hoeven aanpassen wanneer hij de plugin update naar de nieuwste versie.
C.4 C.4.1
Quality requirements Toegankelijkheid
(req 15) De code zal open source zijn en vrij downloadbaar. Het is voor een gebruiker ook mogelijk om in de HTML-source een link te leggen naar een door Anan6 gehost bestand. Dit bestand zal altijd de laatste versie van de software bevatten. (req 16) De updates van de software zullen ten alle tijden backwards compatible zijn. De gebruiker kan ten alle tijden de dynamische link gebruiken[15], zonder zich zorgen te hoeven maken dat zijn code niet werkt door een nieuwe versie van de software. (req 17) Er zal een open en uitgebreide documentatie zijn van de code. Zowel van de door de gebruiker aan te roepen functionaliteit, als de interne code. (req 18) De software zal op zowel desktop websites als op mobiele apparaten werken, daar er gebruik wordt gemaakt van javascript en jQuery aan de clientside. Deze code zal draaien op gerenomeerde webbrowsers (Google Chrome, Mozilla Firefox, Safari, IE), en op de mobiele varianten van deze webbrowsers.
C.4.2
Prestaties / Efficientie
(req 19) Wanneer een object wordt aangemaakt vanuit de database[19], wordt in de interne tabellen van MySQL gekeken naar de structuur van deze tabel. Dit kost slechts 2 MySQL queries, welke door de index van de interne tabel in lineaire tijd kunnen worden opgehaald. (req 20) De objecten[19]zullen op de clientside worden bewaard, totdat deze worden opgeslagen[10][11]. Dit kost 1 MySQL query, welke door index van de tabel in lineaire tijd kan worden uitgevoerd.
44
IN3405 - Bachelorproject - dbQuery
C.4.3
APPENDIX C. REQUIREMENTS ANALYSIS
Onderhoudbaarheid
(req 21) Er zal een open documentatie beschikbaar zijn van zowel de interne code als de code aanroepbaar door gebruikers. Deze documentatie zal online te raadplegen zijn. (req 22) In de code zal op uitgebreide wijze commentaar zijn aangebracht zodat het eenvoudig is om erachter te komen wat de code doet.
C.4.4
Betrouwbaarheid en beschikbaarheid
(req 23) De software zal open beschikbaar zijn en voor iedereen gratis te downloaden. (req 24) De betrouwbaarheid van de software aan de client-side zal afhankelijk zijn van de computer van een bezoeker van de dbQuery gebruikende software. (req 25) De betrouwbaarheid van de updates zal worden gegarandeerd door eerst een beta versie uit te rollen en uitgebreid tests uit te voeren voordat de update de releaseversie haalt.
(req 26) De software wordt iedere nacht gebackupt naar een andere server. In een eventueel geval van serverfalen zal er altijd een backup beschikbaar zijn op een andere server.
C.4.5
Security
(req 27) Doordat er geen directe queries uitgevoerd kunnen worden zal alles gecontroleerd worden door de software die op de server draait. Deze software is voor een externe niet beschikbaar en niet te manipuleren. Slechts de software aan de client-side kan worden gemanipuleerd. Deze manipulaties worden gecontroleerd door de software aan de serverkant.
C.4.6
Precisie en accuraatheid
(req 28) De software werkt aan de hand van de tabelnamen en primary keys van de database. Semantische links worden uitgelezen aan de hand van de foreign keys, tabelnamen en primary key waarden. Wanneer deze waarden consequent zijn zal het systeem accuraat werken. Wanneer inconsistenties bestaan in de database, en/of de database is op een onjuiste manier uitgenormaliseerd, kan de precisie van dbQuery niet worden gewaarborgd.
C.5 C.5.1
Design requirements Constraint gebaseerd op het type gebruiker
(req 29) In het configuratiebestand[12]kan een beheerder aangeven in welke mate updates mogen worden uitgevoerd. Hier is aan te geven of op de betreffende website ’removes’[4]mogen worden uitgevoerd, ’saves’[10][11]mogen worden uitgevoerd, of dat er alleen data uitgelezen mag worden[19].
45
IN3405 - Bachelorproject - dbQuery
C.6 C.6.1
APPENDIX C. REQUIREMENTS ANALYSIS
Process requirements Ontwikkelmethode
(req 30) De software zal ontwikkeld worden aan de hand van ontwikkelmethode SCRUM. (req 31) Het versiebeheer van de software zal aan de hand van een SVN omgeving plaatsvinden.
C.6.2
Kosten en opleveringsdatum
(req 32) Het systeem wordt medio augustus 2012 opgeleverd. (req 33) Het systeem wordt gratis opgeleverd en is kostenloos te gebruiken en/of te downloaden.
C.7
Constraints
(req 34) De back-end van de software zal moeten draaien op een server waar Apache webserver op draait, in combinatie met een MySQL database en PHP. (req 35) De front-end van de software zal werken op de computer van de client. Deze code is geschreven in Javascript en zal werken op gerenomeerde webbrowsers (Google Chrome, Mozilla Firefox, Safari, Internet Explorer) en op mobiele apparaten. In (zwaar) verouderde webbrowsers kan javascript code anders geintepreteerd worden. Er is geen garantie dat de software zal werken op verouderde browsers.
46
IN3405 - Bachelorproject - dbQuery
C.8 C.8.1
APPENDIX C. REQUIREMENTS ANALYSIS
Analysis models Use case models
Use case: Object uit database halen Actor Doel
Triggers Precondities
1. 2.
Stappen
3. 4. 1. 2.
Postcondities Samenvatting
Programmeur De programmeur op een eenvoudige wijze een regel uit een database laten halen. Deze regel moet als object in javascript behandeld kunnen worden om op een natuurlijke manier de data te kunnen uitlezen. De programmeur roept een functie aan met de tabelnaam en de identifier in de database. (Pseudocode: db(’klanten’,10);) De programmeur weet welke tabel in de database de informatie bevat. De programmeur weet de identifier van de op te vragen regel in de database. De server is goed ingesteld voor het gebruik van dbQuery. De dbQuery javascript is geinclude in de HTML aan de clientside. De programmeur roept de dbQuery functie aan met de tabelnaam en de identifier. De programmeur slaat het geretourneerde object op in een variabele. De programmeur heeft een object in javascript die alle informatie bevat die in de betreffende regel in de database staat. De programmeur geeft aan dbQuery door welke databaseinformatie hij wil gebruiken en krijgt deze informatie eenvoudig aangereikt in de vorm van een object
47
IN3405 - Bachelorproject - dbQuery
APPENDIX C. REQUIREMENTS ANALYSIS
Use case: Selectie objecten uit de database halen Actor Doel
Triggers Precondities
1. 2. 3.
Stappen
4. 5. 1. 2.
Postcondities
Samenvatting
Programmeur De programmeur op een eenvoudige wijze een set regels uit een database laten halen die aan bepaalde selectiecriteria voldoen. Deze regels moeten als verzameling objecten in javascript behandeld kunnen worden om op een natuurlijke manier de data te kunnen uitlezen. De programmeur roept een functie aan met de tabelnaam en de selectiecriteria. (Pseudocode: db(’klanten’,’leeftijd¿30’);) De programmeur weet welke tabel in de database de informatie bevat. De programmeur weet hoe de kolom in de database heet die de selectie moet behandelen. De programmeur geeft de criteria door op een door dbQuery makkelijk te intepreteren en nader te bepalen syntax. De server is goed ingesteld voor het gebruik van dbQuery. De dbQuery javascript is geinclude in de HTML aan de clientside. De programmeur roept de dbQuery functie aan met de tabelnaam en de selectiecriteria. De programmeur slaat de geretourneerde verzameling objecten op in een variabele. De programmeur heeft een verzameling objecten in javascript die alle informatie bevatten die in de betreffende regels in de database staan. De programmeur geeft aan dbQuery door welke databaseinformatie hij wil gebruiken en krijgt deze informatie eenvoudig aangereikt in de vorm van een verzameling objecten.
48
IN3405 - Bachelorproject - dbQuery
APPENDIX C. REQUIREMENTS ANALYSIS
Use case: Aanpassingen maken aan objecten en deze in de database opslaan Actor Doel
Triggers
Precondities
1. 2. 3.
Stappen
4. 5. 6. 1. 2.
Postcondities
Samenvatting
Programmeur De programmeur op een eenvoudige wijze wijzigen aanbrengen aan (bestaande of nieuwe) objecten, gebaseerd op de database. Deze wijzigingen moeten opgeslagen kunnen worden. De programmeur roept een functie van het object aan om informatie te wijzigen en een andere functie om de wijzigingen op te slaan in de database. (Pseudocode: object.setAttribute(’leeftijd’,’30’); object.save();) De programmeur heeft een dbQuery object. De programmeur weet hoe de kolom heet die gewijzigd moet worden. De programmeur weet in welke waarde de kolomwaarde veranderd moet worden. De server is goed ingesteld voor het gebruik van dbQuery. De instellingen laten het toe om entries aan te passen. De dbQuery javascript is geinclude in de HTML aan de clientside. De programmeur roept de functie aan om een eigenschap te wijzigen. De programmeur roept de functie aan om de wijzigingen in het object op te slaan. De informatie in de database is gewijzigd. De betreffende kolom is aangepast naar de nieuwe waarde. Als het om een nieuw object gaat is er een nieuwe regel aan de database toegevoegd met alle eigenschappen die aan het object zijn toegewezen. Zonder enige databasekennis kan een javascript programmeur datbaseinformatie wijzigen en opslaan.
49
IN3405 - Bachelorproject - dbQuery
APPENDIX C. REQUIREMENTS ANALYSIS
Use case: Semantisch gelinkte objecten ophalen Actor Doel
Triggers
Precondities
Stappen
1. 2. 3. 4. 1. 2.
Postcondities Samenvatting
Programmeur De programmeur gelinkte tabellen op een natuurlijke manier waarin het lijkt of de database niet uitgenormaliseerd is te laten gebruiken. De programmeur roept een functie van het object aan om informatie op te halen. Hij geeft als parameter op wat hij wil hebben en aan de hand van deze parameter zoekt dbQuery uit of het een 1 op x relatie is, een x op 1 relatie, of een 1 op 1 relatie. (Pseudocode: winkel.getAttribute(’klanten’); zoekt in tabel ’klanten’ waar de foreign key ’winkel’ naar het id van winkel wijst en retourneer de set klanten als objecten. klant.getAttribute(’winkel’); kijkt waar de foreign key ’winkel’ van klant heen wijst en retourneert dit object.) De programmeur heeft een dbQuery object. De databasebenamingen zijn consistent en de database is goed uitgenormaliseerd. De server is goed ingesteld voor het gebruik van dbQuery. De dbQuery javascript is geinclude in de HTML aan de clientside. De programmeur roept de functie aan om de gewenste objecten op te halen. De programmeur slaat de geretourneerde (set) object(en) op in een variabele. De programmeur ontvangt de gewenste gekoppelde databaseregel(s) in de vorm van een (verzameling) object(en). Op een eenvoudige wijze kan een volledig relationele set data worden opgehaald en worden bewerkt.
Use case: Objecten uit de database verwijderen Actor Doel Triggers Precondities
Stappen Postcondities Samenvatting
1. 2. 3. 4. 1.
Programmeur De programmeur eenvoudig regels uit een databasetabel laten verwijderen. De programmeur roept een verwijderfunctie aan op het te verwijderen object. De programmeur heeft een dbQuery object. De server is goed ingesteld voor het gebruik van dbQuery. De instellingen laten het toe om entries te verwijderen. De dbQuery javascript is geinclude in de HTML aan de clientside. De programmeur roept de functie aan om het object te verwijderen. De betreffende databaseregel(s) is/zijn verwijderd. Verwijderen van database entries eenvoudig mogelijk maken.
50
IN3405 - Bachelorproject - dbQuery
APPENDIX C. REQUIREMENTS ANALYSIS
Figure C.1: Sequencediagram van eenvoudige database communicatie met dbQuery.
C.8.2
Dynamische weergave dataverkeer
Sequencediagram eenvoudige update Omschrijving van figuur [C.1]. De gebruiker vraagt een object aan via de clientside code van dbQuery. Deze code doet een call naar de server, waar PHP deze call vertaalt naar een database aanroep. De database geeft de data terug, die door de code aan de serverside wordt vertaald naar een door javascript interpreteerbare json string. De code aan de clientside maakt hier een object van. Dit object is aan te passen als elk ander object. Wanneer de gebruiker wijzigingen heeft aangebracht en wil opslaan, stuurt dbQuery het object in json formaat door naar de server, met in de string verwerkt dat er een save uitgevoerd moet worden. Dit wordt door de code op de clientside vertaald naar een database aanroep.
51
Appendix D
Architectural Design D.1
Introductie
Dit document beschrijft de structuur van de software, bestaande uit verschillende softwarecomponenten. De relaties tussen deze componenten, de communicatie hiertussen en de eigenschappen van deze componenten wordt besproken. Deze informatie wordt geanalyseerd aan de hand van de (externe) kenmerken van de verschillende softwarecomponenten.
D.1.1
Doel van het systeem
De te implementeren software heeft als doel om een javascriptprogrammeur de volgende mogelijkheden te geven: • Informatie uit een database eenvoudig te benaderen. • Informatie uit een database eenvoudig aan te passen. • Databaseinformatie op een intuitieve en objectgeorienteerde manier te gebruiken. • Relaties tussen objecten in een database te gebruiken zonder expliciet deze links te leggen in de code.
D.2
Voorgestelde software architectuur
Het systeem is onder te verdelen in drie delen. Software aan de kant van de client, software aan de kant van de server, en een database. In het komende hoofdstuk worden deze drie componenten uitgewerkt. Hierbij zal ingegaan worden op de interne kenmerken van de software, de designkeuzes en de relaties tussen te componenten, evenals de communicatie en de afhankelijkheden tussen deze componenten.
D.2.1
Ontwerppatroon
De software zal bestaan uit een Client-Server model. Aan de clientside draait javascript in een webbrowser. Deze javascript software kan gebruikt worden door een javascript programmeur. De software maakt een verbinding met de server. Op de server draait PHP software die de databaseafhandeling doet.
52
IN3405 - Bachelorproject - dbQuery
APPENDIX D. ARCHITECTURAL DESIGN
• Clientside: Javascript bestand dat software bevat dat gebruikt kan worden om informatie vanuit de server te gebruiken. • Serverside: PHP software die calls van de client afhandelt en met de database communiceert. Aan de hand van calls van de clientside stuurt deze software informatie terug naar de client. Er is voor een Client-Server model gekozen daar een databaseafhandeling op afstand van zichzelf al een Client-Server karakter heeft. Er is software dat op afstand draait en iets op een machine ergens anders wil benaderen en/of veranderen. Hierbij is de software (computer) die de datawisseling initieert de client. De software (server) die de database afhandeling doet fungeert hierbij als server. Hierbij kunnen meerdere clients bestaan, die allen gebruik maken van dezelfde server.
Figure D.1: Bron: evansville.edu
D.2.2
Ontwerpprincipes
• Abstraction-Occurrence pattern Iedere regel in een databasetabel heeft dezelfde eigenschappen. Deze eigenschappen hebben echter andere content. Van ieder van deze regels kan een object worden aangemaakt. Het is zelfs mogelijk om tweemaal hetzelfde object aan te maken en bij een van deze objecten een eigenschap aan te passen. Ieder van deze objecten linkt naar meerdere eigenschappen (attributen). Hierbij is er een abstractie van het type ”object”, welke op zichzelf een link heeft met x attributen. Een attribuut van dit object kan ook een object zijn, welke weer attributen heeft. Hierbij heeft object x een 1 op n relatie met object(en) van type y, welke allen ook een 1 op n relatie hebben met attribuut z. Relaties zijn in deze context optioneel, het is goed mogelijk dat object x geen item van type z heeft terwijl een ander object daar 10 van heeft. • Divide and conquer De software zal geautomatiseerd om iedere databasetabel een klasse maken waarvan objecten kunnen worden aangemaakt. Door deze abstractie van een grote database worden alle eigenschappen verdeeld over de klassen. Dit versterkt de kracht van de objecten omdat zij alleen kunnen wat ze hoeven te kunnen. • Deligation pattern Om uiteindelijke save calls uit te voeren aan de serverside, moet een MySQL query worden geschreven. Het is mogelijk om dit vanuit het object zelf te doen, maar het is ook mogelijk om hiervoor per attribuut een regel te schrijven. Echter,
53
IN3405 - Bachelorproject - dbQuery
APPENDIX D. ARCHITECTURAL DESIGN
een dbObject en een dbAttribute hebben geen subclass / superclass karakter. Hierbij wordt het schrijven van de save call van het object gedelegeerd aan de attributen. • Read-only interface pattern Het is mogelijk om op de server in te stellen wie wel en wie geen rechten hebben om te schrijven in de database. Wanneer iemand alleen rechten heeft om te lezen uit de database, worden alle setters gedisabled. Hiermee wordt een read-only omgeving gecree¨erd. • Design for flexibility Door bij alle get-functies de aangevraagde objecten direct terug te sturen is het mogelijk om functies door te linken. Hierbij lijkt een call als (pseudocode) x.get(’y’).get(’z’).set(’a’,’b’).save() een groot object te zijn. Echter, er komen 2 aparte database-objecten aan bod, drie attribuut-objecten en een save-call die gebruik maakt van attribuut-objecten voor ieder attribuut van het object. Door deze klassen gescheiden te houden behoudt de software de flexibiliteit die het nodig heeft om eenvoudig aangepast te kunnen worden zonder dat andere componenten er last van hebben. Door json te gebruiken om te communiceren tussen de server en de client, kunnen hele softwarepakketten worden aangepast zonder dat de client of server dit door hebben. Zolang zowel server als client zich aan het contract houden dat alles via correcte json wordt overgezonden, kunnen deze onafhankelijk van elkaar volledig veranderen.
D.2.3
Systeemcomponenten
Het systeem bestaat uit drie componenten.
• Client-side software Draait in een webbrowser op een javascript engine met ’onzichtbare’ calls naar de server. Krijgt informatie terug vanaf de server. • Server-side software Draait in PHP op een webserver en is aanspreekbaar door de client. Ontvangt berichten van de client wat voor informatie er gerequest wordt, of wat er naar de server gepushed wordt. Stuurt informatie en/of feedback terug naar de client. • Database Draait als een MySQL database op de server en is aanspreekbaar door de PHP op de server. Krijgt informatie van de server en geeft informatie terug aan de server.
Figure D.2: Systeemcomponenten
54
IN3405 - Bachelorproject - dbQuery
D.2.4
APPENDIX D. ARCHITECTURAL DESIGN
Subsysteem decompositie
De software aan de serverkant en aan de clientside lijken veel op elkaar. Door de klassen zo gelijk mogelijk te houden wordt de communicatie tussen server en client vergemakkelijkt. Voor de server is het van belang te weten wat wel en niet mag in de database. Voor de client is dit niet van belang, daar deze feedback krijgt van de server wanneer er onmogelijke aanpassingen worden geprobeerd te maken. Clientside javascript De software aan de clientside zal Javascript software zijn die uit twee delen bestaat. • Een functie dbObject (die als object fungeert) die een object weergeeft. • Een functie dbAttribute (die als object fungeert), die een attribuut van een object weerspiegelt in al zijn diversiteiten. De functies die databasecommunicatie nodig hebben zullen een aanroep doen naar de serverside via Ajax calls.
Figure D.3: Klassendiagram client-side
1. dbObject vertegenwoordigt een databaseregel. Voor de clientside is het van belang dat het object al zijn attributen bezit voor iedere kolom in de database. Er zijn vijf functies. • get Hiermee kan een set objecten worden opgehaald. Deze functie maakt een call naar de server. • constructor Hiermee kan een object worden aangemaakt. Deze functie maakt een call naar de server. • getAttribute Hiermee kan een attribuut worden opgehaald. • setAttribute Hiermee kan een attribuut worden gezet. • save Wijzigingen kunnen worden opgeslagen in de database. Deze functie maakt een call naar de server. 55
IN3405 - Bachelorproject - dbQuery
APPENDIX D. ARCHITECTURAL DESIGN
2. dbAttribute representeert een attribuut van een object, oftewel een kolom van een databasetabel. Deze attributen kunnen worden opgehaald en aangepast. Bij een save functie van dbObject zullen deze attributen meegestuurd worden naar de server. • setValue Deze functie wordt door dbObject gebruikt om een waarde te setten. • getValue Deze functie wordt door dbObject gebruikt om een waarde te getten. Serverside software De software aan de serverkant zal PHP software zijn die uit drie delen bestaat. • Een API die aanspreekbaar is voor de buitenwereld. • Een klasse dbObject, die een regel in een database weergeeft. • Een klasse dbAttribute, die een attribuut van een object weerspiegelt in al zijn diversiteiten.
Figure D.4: Klassendiagram server-side
1. q is de webservice die kan worden aangesproken door javascript. Hieraan moet een action worden meegegeven, eventueel data en een token voor authenticatie. 2. dbObject vertegenwoordigt een databaseregel. Voor het object is het van belang dat hij weet wat zijn tabelnaam is, evenals in welke database hij zit. Ook is het voor de semantische links van belang dat de naam van zijn primary key bij hem bekend is. Iedere kolom in een tabel representeert een attribuut van het object, deze worden in een array opgeslagen. Er zijn vijf functies. • get Hiermee kan een set objecten worden opgehaald. • constructor Hiermee kan een object worden aangemaakt.
56
IN3405 - Bachelorproject - dbQuery
APPENDIX D. ARCHITECTURAL DESIGN
• getAttribute Hiermee kan een attribuut worden opgehaald. • setAttribute Hiermee kan een attribuut worden gezet. • save Wijzigingen kunnen worden opgeslagen in de database. 3. dbAttribute representeert een attribuut van een object, oftewel een kolom van een databasetabel. Om geautomatiseerd goed om te kunnen gaan met zijn eigenschappen, is het van belang dat het attribuut weet wat hij wel en niet mag. Hierom weet het attribuut of hij nullable is, wat voor type het is, wat zijn maximale lengte is, wat de characterset is, wat het kolomtype is en wat de default is. Voor het opslaan van data is het ook van belang dat hij weet wat zijn naam is. Vanzelfsprekend heeft een attribuut ook een waarde. dbAttribute heeft drie interne functies. • formatQuotes Deze functie wordt intern gebruikt bij het opslaan. Er moet een MySQL update query worden geschreven. Het is hierbij van belang dat de quotes goed staan, dat data goed geformat is en dat er geen syntax errors optreden. Hiervoor gebruikt dbAttribute zijn attributen als hierboven beschreven. • setValue Deze functie wordt door dbObject gebruikt om een waarde te setten. • getValue Deze functie wordt door dbObject gebruikt om een waarde te getten. Database De software wordt ontwikkeld voor MySQL databases. Door dbObject aan te passen is het portable naar andere database soorten. Om dbQuery zo fijn mogelijk te gebruiken zijn de volgende conventies een goed uitgangspunt: • Tabelnamen zijn meervouden van wat zij representeren (”klanten”, ”winkels”). • Primary keys zijn enkelvouden van wat zij representeren (”klant”, ”winkel”). • Foreign keys zijn enkelvouden van waar zij naar linken (”klant”, ”winkel”). Met deze conventies is het eenvoudig om op een natuurlijke manier met de objecten om te gaan. Zo is new dbObject(’klant’) zoeken naar een tabel met als primary key ’klant’ en hiervan een object teruggeven. Ook het doorlinken gaat op deze manier intuitief: winkel.getAttribute(’klanten’) zoekt hierbij naar alle entries in de tabel ’klanten’ die foreign key ’winkel’ hebben die naar de betreffende winkel linken. Zo kan een selectie objecten worden teruggestuurd. klant.getAttribute(’winkel’) retourneert de winkel waarnaar zijn foreign key ’winkel’ heen linkt.
D.2.5
Hardware / Software mapping
• De serverside software zal ontwikkeld worden op een Debian server met Apache webserver, PHP en MySQL. De software zal zo worden ontworpen dat het op alle machines draait met PHP >= 5, MySQL en Apache. • De clientside software zal ontwikkeld worden voor gerenomeerde webbrowsers (Google Chrome, Mozilla Firefox, Safari, IE >=8) en zal gebruik maken van jQuery.
57
IN3405 - Bachelorproject - dbQuery
D.2.6
APPENDIX D. ARCHITECTURAL DESIGN
Persistent Data Management
Wanneer een servercall is gemaakt om objecten op te halen uit de database, zullen deze objecten aan de client-side bewaard worden. Alle aanpassingen die worden gedaan zullen aan de clientside worden bewaard, evenals nieuwe objecten (structuur wordt van de server gefetched). Pas op het moment dat er een save wordt uitgevoerd, zal de data worden teruggestuurd naar de server, om daar in de database te worden opgeslagen. Aan de hand van dit systeem zal de server slechts belast worden voor de daadwerkelijke databasecommunicatie. Dit kan vooral serverrust opleveren bij read-only software. Tijdens het fetchen van een object uit de database zullen er twee MySQL queries worden uitgevoerd. Een om de structuur van de tabel uit de interne tabellen van MySQL te halen, en een om de daadwerkelijke data te vullen in het object. Bij het opslaan van een object zal slechts 1 query voldoende zijn om de data in de database te verwerken.
D.2.7
Global Resource handling en Access Control voor verschillende actoren
Er zijn twee soorten clients met dbQuery. • Read-only gebruiker • Volledige gebruiker Het zal op de server instelbaar zijn of het read-only applicaties betreft, of dat een client volledig toegang krijgt tot de database en ook save functies kan uitvoeren.
D.2.8
Randvoorwaarden
Back-end Het kan voorkomen dat een webserver of een webservice vastloopt of crasht. Echter, dbQuery zal deze kans niet verhogen. Wanneer er normaliter databasecommunicatie plaatsvindt, zal dit niet exponentieel toenemen met gebruik van dbQuery: 1. Er worden efficiente queries gebouwd door de automatische uitnormalisering in het semantisch doorlinken van objecten. Hierdoor zal dbQuery de MySQL load niet verhogen. 2. Data wordt opgeslagen aan de clientside. Hierdoor wordt de server niet belast met informatieafhandeling waar de server in principe niet voor nodig is. Slechts voor de databasecommunicatie zelf zal de server gebruikt worden. Mocht onverhoopt de server falen, zal dbQuery vanuit de clientside geen succesvolle calls kunnen maken naar de server, en kan geen databasecommunicatie plaatsvinden. Wanneer een object al is uitgelezen en teruggestuurd naar de client, kan de gebruiker bij falen van de server gewoon het object blijven gebruiken omdat er geen databasecommunicatie nodig is vanaf het moment dat het object bekend is. Aanpassingen maken is ook mogelijk, slechts het opslaan zal dan niet werken.
58
IN3405 - Bachelorproject - dbQuery
APPENDIX D. ARCHITECTURAL DESIGN
Front-end Uit veiligheidsoverwegingen is het door de auteurs van javascript niet mogelijk gemaakt om cross-origin servercalls uit te voeren. Dit betekent dat dbQuery alleen kan werken wanneer zowel de pagina zelf (de DOM) van dezelfde server is geladen als waar de call naartoe gaat. Het is hierdoor niet mogelijk om vanaf machine x een pagina te laden, die met dbQuery een database op machine y aanstuurt. Met allow-origin regels in de Apache configuratie is dit te vermijden. Echter, de veiligheid van de server kan dan niet gewaarborgd worden.
59
Appendix E
Test and Implementation plan E.1
Introductie
In dit hoofdstuk zal worden uitgewerkt hoe de ontwikkelfase van het project in zijn gang gaat. Uitgelegd zal worden wat in welke volgorde geimplementeerd gaat worden. Ook zal worden aangegeven hoe tijdens de implementatiefase de software getest zal worden.
E.2
Requirements met MoSCoW
Om gebruik te kunnen maken van de plugin is er een minimaal aantal eisen waaraan het systeem moet voldoen. Andere eisen zouden het gebruik van de plugin optimaliseren. Deze requirements zijn onderverdeeld aan de hand van een MoSCoW model, waarin de prioriteiten duidelijk worden.
E.2.1
Must
• Objecten uit de database halen [requirement 19] • Nieuwe objecten aanmaken met eigenschappen uit de database [requirement 2] • Eigenschappen ophalen [requirement 7] • Eigenschappen aanpassen [requirement 8]
E.2.2
Should
• Aanpassingen opslaan [requirement 11] • Nieuwe objecten opslaan [requirement 10] • Verzameling objecten ophalen [requirement 3] • Item verwijderen [requirement 4] • Semantische links [requirement 5][requirement 6] • Configuratiebestand [requirement 12] • Goed gedocumenteerd [requirement 21]
60
IN3405 - Bachelorproject - dbQuery APPENDIX E. TEST AND IMPLEMENTATION PLAN
• Goed gecommentariseerde code [requirement 22] • Efficient opslaan [requirement 20] • Interne SQL structuur ophalen [requirement 19] • Cross-browser [requirement 18]
E.2.3
Could
• Koppelingen aanleggen [requirement 9] • Downloadbaar [requirement 13][requirement 15][requirement 23] • Backwards-compatibility [requirement 14] • Verschillende gebruikers [requirement 29]
E.2.4
Won’t
• NodeJS webservice op de server [zie F.2]
E.3
Implementatie en tests
De implementatie van de software zal aan de hand van SCRUM lopen. Na iedere iteratie is er werkende software die getoond wordt aan de opdrachtgever. Deze zal worden besproken, eventueel zullen aanpassingen worden gemaakt en de volgende features worden toegevoegd. Het systeem zal met iedere iteratie groeien richting het gewenste eindresultaat.
E.3.1
Volgorde voor het implementeren van de features
Het MoSCoW schema[E.2]geeft een redelijke indicatie in welke volgorde de requirements geimplementeerd zullen worden; van should naar could. Echter, het systeem bestaat uit een serverkant en een clientside. Deze worden apart geimplementeerd. Eerst zal de webservice aan de serverkant worden geimplementeerd alvorens de clientside javascript software te schrijven. Hierbij wordt rekening gehouden met de standaardisatie van communicatie tussen de client en de server. Per iteratie worden een of meerdere requirements geimplementeerd. Iteraties 1. 22/07/2012 - 26/07/2012 • Configuratiebestand [requirement 12]op de webserver. • Interne SQL structuur ophalen [requirement 19] • Objecten uit de database halen [requirement 19]op de webserver. • Eigenschappen ophalen [requirement 7]op de webserver. • Eigenschappen aanpassen [requirement 8]op de webserver. 2. 29/07/2012 - 03/08/2012 • Aanpassingen opslaan [requirement 11]op de webserver.
61
IN3405 - Bachelorproject - dbQuery APPENDIX E. TEST AND IMPLEMENTATION PLAN
• Nieuwe objecten aanmaken met eigenschappen uit de database [requirement 2]op de webserver. • Nieuwe objecten opslaan [requirement 10]op de webserver. • Clientside javascript die de geimplementeerde webservice aanspreekt om: – Objecten uit de database te halen [requirement 19]in javascript. – Eigenschappen op te halen [requirement 7]in javascript. – Eigenschappen aan te passen [requirement 8]in javascript. 3. 06/07/2012 - 13/08/2012 • Verzameling objecten ophalen [requirement 3]op de webserver en via javascript. • Item verwijderen [requirement 4]op de webserver. • Clientside javascript uitbreiden die de geimplementeerde webservice aanspreekt om: – Aanpassingen op te slaan [requirement 11]in javascript. – Items te verwijderen [requirement 4]via javascript. – Nieuwe objecten aan te maken met eigenschappen uit de database [requirement 2]in javascript. – Nieuwe objecten op te slaan [requirement 10]in javascript. 4. 13/07/2012 - 20/08/2012 • Problemen verhelpen, bugs oplossen, afmaken definitieve code en aanvullen documentatie (prio 1). • Mocht de tijd het toelaten: – Koppelingen aanleggen en kunnen opslaan [requirement 9] – Downloadbaar [requirement 13][requirement 15][requirement 23] – Backwards-compatibility [requirement 14] – Verschillende gebruikers [requirement 29] Milestones Iedere iteratie levert een werkend systeem op, dat op zich een kleine milestone is. De grote milestones zijn: • Volledige webservice aan de serverkant die is aan te spreken (via cUrl / wget). • Volledige javascript plugin die gebruikt kan worden voor databasecommunicatie.
E.3.2
Documentatie
Iedere functie zal volledig gedocumenteerd worden. Daarnaast zal in de code overal uitgelegd worden waar wat gebeurt. De documentatie zal op de website staan en zal in een document downloadbaar zijn.
62
IN3405 - Bachelorproject - dbQuery APPENDIX E. TEST AND IMPLEMENTATION PLAN
E.3.3
Unit testen
Tijdens de implementatiefase zal bij iedere te implementeren feature eerst een of meerdere testcase(s) geschreven. Deze testcases zullen falen, totdat de feature volledig is geimplementeerd. Aan de hand van deze ontwikkelmethode worden fouten sneller vermeden dan wanneer testcases nadien worden geschreven. Deze tests zullen zowel voor de client- als de serverkant worden geschreven. QUnit voor clientside software Voor het testen van de javascript aan de clientside zal gebruik worden gemaakt van QUnit. Om communicatie met de database te mocken wordt gebruik gemaakt van Mockjax. Dit zal gebeuren door een javascriptbestand via nodejs te laten draaien. Dit javascript test bestand gebruikt QUnit, Mockjax en alle geimplementeerde features en zal deze testen. De testoutput wordt geschreven naar een bestand en dit zal iedere commit geautomatiseerd plaatsvinden. Mocht een test falen wordt dat herkend door het systeem en zal de commit gerevert worden. Tijdens de ontwikkelfase zullen tests (logischerwijs) falen, dan zal deze automatische revert niet plaatsvinden. PHPUnit voor serverside software De software aan de serverkant zal getest worden met PHPUnit. Er zullen PHP test bestanden zijn die bij iedere commit worden aangeroepen. Mocht er een test falen, wordt de commit gerevert. Evenals bij de tests aan de clientside wordt deze functionaliteit tijdens de implementatiefase uitgezet. Integratietesten Voor het testen van de succesvolle samenwerking tussen javascript aan de clientside en PHP aan de serverside zal gebruik gemaakt worden van QUnit. Er zullen objecten worden geextraheerd uit de database. Deze gegevens zullen worden aangepast en opgeslagen om vervolgens dit bestand uit de database uit te lezen en te matchen. Acceptatietesten Wanneer alle testcases slagen en de software volledig geimplementeerd is, worden acceptatietesten uitgevoerd. Dit zal gebeuren door alle betrokkenen, door bij het ontwikkelen van javascript software dbQuery te gebruiken waar zij normaal gesproken een eigen webservice zouden schrijven. Nu wordt de software gebruikt zoals gebruikers het zouden gebruiken, dit kan eventueel leiden tot onverwachtse manouevres die niet door de testcases zijn ontdekt.
E.4
Risico analyse
Er wordt gebruik gemaakt van een client-server model. Informatie wordt hierbij overgezonden over een netwerk. Communicatie kan hierbij worden onderschept of er kan een DoS aanval worden gestart om de webserver offline te krijgen. Dit is een veiligheidsrisico. Aan de hand van een goede firewall en door queries op de serverkant te controleren worden de risico’s geminimaliseerd.
63
Appendix F
Afwegingen Tijdens het ontwerp en de implementatie van de software zijn veel afwegingen gemaakt. In dit hoofdstuk worden de gemaakte keuzes beargumenteerd en worden de voor- en nadelen van de opties uitgewerkt.
F.1
Databasesoort
Ieder bedrijf werkt met zijn eigen databasesoort. Wanneer een plugin voor javascript wordt ontwikkeld voor databasecommunicatie, moet een keuze worden gemaakt voor welke databasetaal de plugin wordt ontwikkeld.
Firebird vs. MySQL De opdrachtgever maakt voornamelijk gebruik van MySQL1 voor interne software. Software die gecree¨erd wordt voor klanten maakt veelal gebruik van Firebird2 . Andere databasesoorten worden (vrijwel) niet gebruikt. De keuze moet gemaakt worden tussen deze twee databasesoorten. De plugin kan in eerste instantie voor slechts ´e´en van de databasetalen werken. Eventueel kan de software geport worden naar de andere taal. Omdat de plugin voornamelijk voor interne software zal worden gebruikt is in overleg gekozen om de plugin in eerste instantie voor MySQL te ontwikkelen.
F.2
PHP vs. NodeJS
Om met een database te communiceren vanuit javascript op de clientside moet er software zijn die op de server draait die kan communiceren met de database. Deze software moet aanspreekbaar zijn door de client en moet informatie naar de client kunnen terugsturen. Hierbij is gedacht aan PHP en NodeJS.
F.2.1
PHP
PHP is een dynamische programmeertaal die op de server kan draaien. Hiermee kan een webservice gebouwd worden die kan communiceren met de database en aanroepbaar is door een client. PHP heeft een zeer goed ingebouwd MySQL communicatie mechanisme. Deze 1 http://www.mysql.org 2 http://www.firebirdsql.org/
64
IN3405 - Bachelorproject - dbQuery
APPENDIX F. AFWEGINGEN
is zeer eenvoudig en snel in gebruik. Verschillende encodings kunnen met PHP eenvoudig worden omgezet, evenals datatypes en multibyte documenten. Dit maakt PHP een geschikte taal om te gebruiken voor dbQuery. Voordelen PHP • PHP is stabiel. • PHP heeft een uitgebreide set functies voor MySQL-databasecommunicatie. • PHP is eenvoudig te testen. • PHP heeft een goede foutafhandeling. • PHP is zeer goed gedocumenteerd. • PHP is zeer dynamisch. • PHP is volledig objectgeorienteerd te gebruiken. • PHP heeft voor vrijwel alle datatypes conversiefuncties. • PHP heeft zwakke datatypes, waarmee het eenvoudig is om voor databasecommunicaties datatypes uit de database voor de mens leesbaarder te maken. • PHP is goed geschikt voor webservers, daar het aangeroepen kan worden van buitenaf en directe communicatie met de database kan plaatsvinden. Nadelen van PHP • Slechts uitvoerbaar op een server, niet aan de clientside. Dit maakt PHP voor de client niet dynamisch. • PHP heeft zwakke datatypes. Hoewel dit voor dbQuery een voordeel kan zijn, kunnen ambiguiteiten optreden door onjuist gebruik van anders gedeclareerde variabelen.
F.2.2
NodeJS
NodeJS is in 2009 ontwikkeld om een event-driven asynchrone webservice te kunnen maken op servers. NodeJS maakt het mogelijk om javascript op een server te laten draaien en ’luisteren’ naar inkomende aanroepen. Doordat NodeJS event-driven is en calls asynchroon lopen, kunnen deze calls snel en dynamisch worden afgehandeld. Doordat NodeJS nog in de beginfase van ontwikkelen is, zijn databasecommunicatie en met name de foutafhandeling ervan niet optimaal. Voordelen van NodeJS • Alle berekeningen kunnen aan de kant van de client gedaan worden. De server wordt slechts belast met het opslaan en/of ophalen van de data. • Doordat de clientside op javascript werkt, kan data eenvoudig in javascript encoded json strings worden overgezonden. Hierdoor kan data exact worden gebruikt aan de clientside zoals deze vanuit de server worden verzonden. • Responses worden teruggestuurd als stream en niet als blok data. 65
IN3405 - Bachelorproject - dbQuery
APPENDIX F. AFWEGINGEN
• NodeJS is bruikbaar met javascript. Er is nu slechts 1 programmeertaal nodig voor een programmeur om een applicatie te schrijven. • Doordat NodeJS draait op javascript en er vijf bedrijven (Google, Mozilla, Apple, Microsoft Opera) strijden om de sneltste javascript interpreter, heeft NodeJS een snelle ondergrond. • NodeJS is asynchroon, waardoor het veel aanroepen snel kan afhandelen. Nadelen van NodeJS • NodeJS verzamelt troep (garbage-collection). Deze moet eens in de x tijd geleegd worden, waardoor de applicatie stopt. • NodeJS is nog in de beginfase van de ontwikkeling. Hierdoor zijn er nog geen uitgebreide bibliotheken voor triviale zaken als database communicatie. • NodeJS heeft een slechte API en is vaak niet volledig backwards compatible bij updates. • Javascript kan pas sinds 3 jaar op een server draaien. Daarvoor waren XML / SQL / ... libraries niet nodig omdat het op de clientside werkte. De libraries die er nu zijn gekomen zijn allen in beginfase en vaak niet stabiel en slecht gedocumenteerd. • NodeJS is asynchroon. Hoewel dit ook een voordeel is, maakt het de geschreven code minder makkelijk onderhoudbaar door het extensieve gebruik van callback functies.
F.2.3
PHP vs. NodeJS
Om beide programmeertalen met elkaar te kunnen vergelijken zijn verschillende stappen ondernomen. Deze vergelijking is gemaakt met oog op dbQuery en geldt niet in het algemeen voor de programmeertalen. De lijst is tot stand gekomen door voor beide programmeertalen een webservice te schrijven die een database uitleest. Tijdens de (midden in de vooronderzoekfase) implementatie van deze twee webservices[G]zijn de ervaringen besproken en gedeeld. Aan de hand hiervan is de volgende lijst samengesteld. PHP NodeJS Databasecommunicatie ++ Foutafhandeling ++ + Schaalbaarheid ++ Onderhoudbaarheid + Testbaarheid ++ Contact met clientside – ++
F.2.4
Conclusie
Om een stabiele webservice te bouwen die alle datatypes in een database goed kan behandelen en opslaan met een goede foutafhandeling is het nodig dat de serverside software deze kwaliteit kan garanderen. NodeJS schiet hierbij te kort. Er zijn (doordat javascript voor 2009 nooit op een server draaide) niet voldoende goede libraries en/of interne functies die de kwaliteit van datatypes (als een DATETIME of een tekst in een andere encoding) kan waarborgen. In PHP bestaan hier echter allerlei uitgebreid geteste functies voor die deze kwaliteit w´el kunnen waarborgen. Er is gekozen om voor de ontwikkeling van dbQuery
66
IN3405 - Bachelorproject - dbQuery
APPENDIX F. AFWEGINGEN
PHP te gebruiken als serverside software. Mocht in de toekomst NodeJS deze datatypes wel kunnen behandelen, zal overwogen worden om de serverside software te porten naar NodeJS.
F.3
Standaardisatie berichten
Over en weer worden berichten gestuurd van de client naar de server en van de server naar de client. De berichten vanaf de server naar de client worden verstuurd als JSON encoded strings. De berichten van de client naar de server zijn Ajax POSTS. Voor deze berichten moet een standaard worden aangelegd zodat er geen ambiguiteiten en/of inconsistente berichtgevingen kunnen worden overgestuurd. Wanneer beide partijen (server en client) zich aan de standaarden houden, kunnen zij onafhankelijk worden doorontwikkeld. In overleg tussen de opdrachtgever en ontwikkelaar zijn deze standaarden samengesteld.
F.3.1
Server naar client
De server zal berichten sturen naar de client in JSON formaat. In deze json encoded string zullen de volgende items zijn opgenomen:
• status: ”succes” of ”error”, geeft aan of de server de request heeft kunnen uitvoeren. • msg: Een leesbare string die aangeeft wat er is uitgevoerd of wat er fout is gegaan. • action: De actie die is uitgevoerd. • result: Array – [found]: Totaal aantal gevonden objecten. – [resultset]: Array ∗ [0]: Eerste gevonden object. · [’Naam eerste attribuut’]: Waarde van eerste attribuut · [’Naam tweede attribuut’]: Waarde van tweede attribuut · [’Naam ...e attribuut’]: Waarde van ...e attribuut ∗ [1]: Tweede gevonden object. ∗ [...]: ...e gevonden object.
F.3.2
Client naar server
Berichten vanaf de server naar de client zullen verlopen via Ajax POSTS. Deze berichten zullen naar ’q.php’ gestuurd worden met de volgende parameters:
• action: ”get”, ”sav”, ”del”. Geeft de actie aan die uitgevoerd moet worden. • data: Array
67
IN3405 - Bachelorproject - dbQuery
APPENDIX F. AFWEGINGEN
– [tablename]: Tabelnaam waaruit de informatie gehaald moet worden (verplicht bij action ”get”). – [identifier]: Sleutel van het opgevraagde object (verplicht bij ”get” zonder selectiecriteria, een ”sav” of een ”del”). – [select]: Array (verplicht bij ”get” zonder identifier). ∗ [’een attribuut’]: Vereiste waarde van het attribuut. ∗ [’nog een attribuut’]: Vereiste waarde van dit attribuut. – [orderby]: Sortering – [size]: Aantal objecten dat opgevraagd wordt – [offset]: Offset van de resultset – [attributes]: Array (verplicht bij ”sav”). ∗ [’een attribuut’]: Waarde van het attribuut ∗ [’nog een attribuut’]: Waarde van dit attribuut ∗ [...]: ...e attribuut
F.4 F.4.1
Chaining Introduction
Om de eindgebruiker een zo eenvoudig mogelijke syntax te geven is het van belang dat de eindgebruiker functies kan ’chainen’. Hiermee wordt bedoeld dat functies aan elkaar gekoppeld kunnen worden. Echter, door asynchroniteit van javascript worden functies aangeroepen tijdens het laden van andere functies (objecten). Hierdoor kan chainen lastig zijn. In de komende paragrafen zal uit worden gegaan van dit stuk ’klassieke’ code: 1 2 3 4 5 6 7 8
var klant = new db ( ’ klanten ’ ,1) ; klant . setAttribute ( ’ naam ’ , ’ Plak ’) ; klant . setAttribute ( ’ voornaam ’ , ’ Rutger ’) ; klant . setAttribute ( ’ opleiding ’ , ’ Technische Informatica ’) ; var adres = klant . getAttribute ( ’ adres ’) ; adres . setAttribute ( ’ postcode ’ , ’ 2611 BT ’) ; adres . save () ; klant . save () ;
F.4.2
Directe chaining
Bij directe chaining worden functies gekoppeld door eenvoudigerwijs direct achter een functieaanroep de volgende functieaanroep te doen. Chaining is een van de krachten van jQuery. Om van een object ’klant’ verschillende dingen aan te passen kan de ’getAttribute’ functie worden gechained. De voorbeeldcode zou er met gebruik van directe chaining alsvolgt uit zien: 1 2 3 4
// iedere func tieaanr oep komt direct na de vorige new db ( ’ klanten ’ ,1) . setAttribute ( ’ naam ’ , ’ Plak ’) . setAttribute ( ’ voornaam ’ , ’ Rutger ’)
68
IN3405 - Bachelorproject - dbQuery
5 6 7 8 9 10
APPENDIX F. AFWEGINGEN
. setAttribute ( ’ opleiding ’ , ’ Technische Informatica ’) . save () // nieuw object wordt aangemaakt ( relationele link ) . getAttribute ( ’ adres ’) . setAttribute ( ’ postcode ’ , ’ 2611 BT ’) . save () ;
Voordelen directe chaining • Overzichtelijke code voor de eindgebruiker. • De eindgebruiker hoeft minder code te schrijven. • Eenvoudig te begrijpen. Nadelen directe chaining • Ondoenlijk wanneer een object moet worden ingelezen door de asynchroniteit van javascript. • Lastiger te onderhouden code voor de backend
F.4.3
Callbacks
Doordat javascript asynchroon de code uitvoert, zijn objecten voor ze volledig uit de database zijn uitgelezen nog niet bruikbaar. In de tussentijd wordt er echter wel al andere code uitgevoerd. In de tussentijd kunnen de objecten nog niet gebruikt worden. Aanpassingen aan de objecten kunnen pas plaatsvinden op het moment dat het object gedefinieerd is. De functies die aan dit object worden aangeroepen moeten door javascript dus pas worden uitgevoerd na het volledig inladen van de objecten. Dit kan aan de hand van callbacks. Een callback is een functie gedefinieerd door de eindgebruiker. Deze functie wordt door de plugin uitgevoerd nadat het object is ingelezen. Het voorbeeld van hierboven zou er dan zo uit komen te zien: 1 2 3 4 5 6 7 8 9 10 11 12 13 14
// de derde parameter is de callback functie // hierin refereert ’ klant ’ naar het aangemaakte object new db ( ’ klanten ’ ,1 , function ( klant ) { klant . setAttribute ( ’ naam ’ , ’ Plak ’) ; klant . setAttribute ( ’ voornaam ’ , ’ Rutger ’) ; klant . setAttribute ( ’ opleiding ’ , ’ Technische Informatica ’) ; klant . save () ; // object ’ adres ’ wordt aangemaakt , tweede parameter is de callback // hierin refereert ’ adres ’ naar het aangemaakte object klant . getAttribute ( ’ adres ’ , function ( adres ) { adres . setAttribute ( ’ postcode ’ , ’ 2611 BT ’) ; adres . save () ; }) ; }) ;
Voordelen van callbacks • Zekerheid dat code niet vroegtijdig wordt uitgevoerd. • Overzichtelijke codeblokken met per object een functie. • Eenvoudige backend code. 69
IN3405 - Bachelorproject - dbQuery
APPENDIX F. AFWEGINGEN
Nadelen van callbacks • Bij diepe relationele relaties wordt code onoverzichtelijk. • Callbacks zijn in eerste instantie lastig te begrijpen voor de eindgebruiker.
F.4.4
Combinatie van beide
Zowel callbacks als directe chaining hebben voordelen die onmisbaar zijn voor een goed bruikbare plugin. Echter, directe chaining is niet te gebruiken voor nog in te laden objecten. Hierom is gekozen om een combinatie van callbacks en directe chaining te gebruiken. Wanneer een object moet worden ingelezen, dit gebeurt bij een nieuw object of bij een relationeel gelinkt object, wordt er gebruik gemaakt van een callback. Wanneer een object wel is ingelezen, kunnen functies worden gechained. De code komt er alsvolgt uit te zien: 1 2 3 4 5 6 7 8 9 10 11 12 13
// de derde parameter is de callback functie new db ( ’ klanten ’ ,1 , function ( klant ) { // hierin refereert ’ klant ’ naar het aangemaakte object // de functies binnen ’ klant ’ kunnen worden gechained klant . setAttribute ( ’ naam ’ , ’ Plak ’) . setAttribute ( ’ voornaam ’ , ’ Rutger ’) . setAttribute ( ’ opleiding ’ , ’ Technische Informatica ’) . save () . getAttribute ( ’ adres ’ , function ( adres ) { adres . setAttribute ( ’ postcode ’ , ’ 2611 BT ’) . save () ; }) ; }) ;
70
Appendix G
Voorbeeldcode In zowel NodeJS als PHP is een webservice geschreven die uit de database variabele print in een JSON formaat. Dit om te kunnen vergelijken wat fijner werkt. De onderstaande code is geen defnitieve code of code die in dbQuery terecht komt. Deze code is slechts geschreven om te ’testen’ hoe fijn een programmeertaal werkt voor databasecommunicatie aan de hand van een webservice.
G.1
NodeJS
In NodeJS is een webservice geschreven die luistert op poort 8124 en variabelen ’action’, ’tablename’ en ’id’, om een regel uit een database te halen. Zo levert een aanroep naar: http://127.0.0.1:8124/?action=get&tablename=webusers&id=2 resultaat [”webuser”:2, ”webgroup”:99, ”name”:”Plak”, ”firstname”:”Rutger”, ”login”:”rutger”, ”password”:”**”, ”email”:”
[email protected]”, ”role”:”Administrateur”] op, omdat de webservice uitzoekt welke identifier bij ’webusers’ hoort en de regel met de identifier ’2’ leest. 1 2 3 4 5 6 7 8 9 10 11 12 13 14
var var var var var
http = require ( ’ http ’) ; mysql = require ( ’/ etc / mysql / node_modules / db - mysql / db - mysql . js ’) ; querystring = require ( ’ querystring ’) ; url = require ( ’ url ’) ; db = null ;
// establish database connection var db = new mysql . Database ({ hostname : ’ localhost ’ , user : ’ root ’ , password : ’ ***** ’ , database : ’ a6doc ’ }) . on ( ’ error ’ , function ( error ) { console . log ( ’ Foutje !: ’ + error ) ;
71
IN3405 - Bachelorproject - dbQuery
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
APPENDIX G. VOORBEELDCODE
}) . on ( ’ success ’ , function ( server ) { console . log ( ’ Connected to server ’ + server . hostname + ’ ( ’ + server . version + ’) ’) ; }) . connect ( function ( error ) { db = this ; }) ; // create the webservice http . createServer ( function ( request , response ) { // set content type for the response response . writeHead (200 , { ’ Content - Type ’ : ’ text / plain ’ }) ; // parse request variables var pquery = querystring . parse ( url . parse ( request . url ) . query ) ; // If no action is passed , terminate if (! pquery . action ) response . end ( ’ ERROR -- No action passed ’) ; // check action switch ( pquery . action ) { case ’ get ’: // create callback to output the result var callback = function ( result ) { console . log ( ’ get function terminated , returning result :\ n ’+ result ) ; response . end ( JSON . stringify ( result ) ) ; } // use get function and add callback var result = doGet ( pquery , callback ) ; break ; case ’ set ’: doSet ( pquery ) ; break ; case ’ default ’: response . end ( ’ ’) ; } // listen to port 8124 }) . listen (8124) ; // get function , handles database connection function doGet ( pquery , callback ) { // if not enough arguments are passed , terminate if (! pquery . tablename ) { callback ( " ERROR -- no tablename defined " ) ; return false ; } if (! pquery . id ) { callback ( " ERROR -- no id defined " ) ; return false ; } var entry_index = pquery . id ; var table = pquery . tablename ; console . log ( pquery ) ; // find out what the identifier is db . query () . execute ( ’ select * from i n f o r m a t i o n _ s c h e m a . COLUMNS where
72
IN3405 - Bachelorproject - dbQuery
APPENDIX G. VOORBEELDCODE
TABLE_NAME = " ’+ table + ’ "; ’ , function ( error , rows , cols ) { 73 74 75 76 77 78 79 80 81 82 83 84
// error handling if ( error ) { console . log ( ’ error ’+ error ) ; } // console feedback console . log ( ’ Query " ’+ this . sql () + ’" succesvol uitgevoerd ’) ; // if the tablename is invalid , exit with error if ( rows . length ==0) callback ( " Error -- Invalid tablename , ’" + table + " ’ is not an existing table " ) ;
85 86 87 88 89 90 91 92 93 94 95
// for each column in the table for ( var i = 0; i < rows . length ; i ++) { console . log ( ’| __ ’+ rows [ i ][ ’ COLUMN_NAME ’ ]+ ’ wordt onderzocht ... ’) ; // check if it ’s the identifier if ( rows [ i ][ ’ COLUMN_KEY ’ ]== ’ PRI ’) { identifier = rows [ i ][ ’ COLUMN_NAME ’ ]; console . log ( ’| ____ > De identifier is " ’+ identifier + ’" ’) ; // collect object db . query () . execute ( ’ select * from ’+ table + ’ where ’+ identifier + ’ = ’+ entry_index + ’; ’ , function ( error , rows , cols ) { if ( error ) { console . log ( ’ error ’+ error ) ; } // console feedback console . log ( ’ Query " ’+ this . sql () + ’" succesvol uitgevoerd ’) ;
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
// return information console . log ( ’ result : ’) ; console . log ( rows ) ; if (! rows || rows . length ==0) callback ( " Error -- There is no entry in ’" + table + " ’ with ’" + identifier + " ’ ’" + entry_index + " ’. " ) ; // return resultset callback ( rows ) ; }) ; // break if the identifier is found break ; } } }) ; } function doSet () { } console . log ( ’ server listening at 127 .0.0.1:8 124 ’) ;
73
IN3405 - Bachelorproject - dbQuery
G.2
APPENDIX G. VOORBEELDCODE
PHP
In PHP is een webpagina geschreven die na een aanroep output print. De benodigde variabelen zijn hetzelfde als bij NodeJS; ’action’, ’tablename’ en ’id’ zijn verplicht om een regel uit een database te halen. Zo levert een aanroep naar http://127.0.0.1/phpnodetest.php?action=get&tablename=webusers&id=2 resultaat [”webuser”:2, ”webgroup”:99, ”name”:”Plak”, ”firstname”:”Rutger”, ”login”:”rutger”, ”password”:”**”, ”email”:”
[email protected]”, ”role”:”Administrateur”] op, omdat de webpagina uitzoekt welke identifier bij ’webusers’ hoort en de regel met de identifier ’2’ leest. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
’. $sInfSql ) ; // error handling if ( count ( $aRows ) ) == 0) die ( " Error -- Invalid tablename , ’" . $sTableName . " ’ is not an existing table " ) ; // for each column in the table while ( $aRow = m y s q l _ f e t c h _ a r r a y ( $aRows ) ) {
74
IN3405 - Bachelorproject - dbQuery
37 38 39 40 41 42 43 44 45 46
// check if it ’s the identifier if ( $aRow [ ’ COLUMN_KEY ’ ]== ’ PRI ’) $sKeyName = $aRow [ ’ COLUMN_NAME ’ ]; } // error handling if ( count ( $this - > aAttributes ) == 0) die ( " TABLE " . $sDataBase . " . " . $sTableName . " DOES NOT EXIST " ) ; // collect object $sSql = " SELECT * FROM " . $sTableName . " WHERE " . $this - > sKeyName . " = " . $iId . " ; "; $oResult = new Object () ;
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
APPENDIX G. VOORBEELDCODE
// error handling $aRows = mysql_query ( $sSql ) or die ( $sSql . ’ ’. mysql_error () ) ; // store resultset in object if ( $aRow = m y s q l _ f e t c h _ a r r a y ( $aRows ) ) { foreach ( $aRow as $key = > $value ) { $oResult - >{ $key } = $value ; } // error handling } else die ( " Error -- There is no entry in ’" . $sTableName . " ’ with ’" . $sKeyName . " ’ ’" . $sId . " ’. " ) ; // return the information in json style die ( json_encode ( $oResult ) ) ; } ?>
75
Bibliography [1] John Resig, Pro JavaScript Techniques. Apress, 2006. [2] Timothy C. Lethbridge, Robert Lagani`ere, Object-Oriented Software Engineering. McGraw Hill, 2nd Edition, 2005. [3] Mauro Pezz`e, Michal Young, Software Testing and Analysis: Process, Principles and Techniques. Wiley, 2007. [4] Douglas Crockford, Prototypal Inheritance in JavaScript. crockford.com, 2008. [5] Douglas Crockford, Private members in JavaScript. crockford.com, 2001. [6] Mike Cantelon, T.J. Holowaychuk, Nathan Rajlich, Node.js in action. Manning, 2012. [7] Ir. J.H. ter Bekke, Database ontwerp. Stenfert Kroese, Tweede editie 1988.
76