Lang de fleste udviklingsprojekter gemmer data i en SQL-relationsdatabase som MySQL, mens aktive data typisk behandles i en eller anden form for objekter i koden. Det er en udfordring er at få orden, konsistens og form på koden. Ofte har man disse udfordringer:
- Ensartet navngivning af tabeller og felter.
- Konsistent navngivning af et felt i både databasen og koden.
- Konsistente data hvis en række fra en tabel bruges i flere sammenhænge.
Under de sidste mange PHP-projekter har vi udviklet vores egen ORM – Object/relational mapping, kaldet DFC – Data Framework Classes. Vi har løst en del hyppige udfordringer på en elegant facon:
- Et dataobjekt svarer 1:1 til en record fra en tabel
- Et dataobjekt vil kun findes i memory én gang. Dvs. hvis en records trækkes ud med flere forskellige SQL-forespørgsler, vil det være det samme objekt der rent faktisk kommer retur. Dette sparer hukommelse og giver dataintegritet i forbindelse med opdateringer af data.
- Alle trivielle SQL-forespørgsler kan klares med enkle og effektive API-kald.
- Navngivning på formularfelter så man ikke skal overveje navnestruktur i en formular.
- Centraliseret og nedarvet validering hvor samme server-side validering kan anvendes både i normal submit/response eller i AJAX-kald.
- Kickstart-scripts der hurtigt kan give formularer, visninger eller listevisninger.
Der er også meget andet godt, men først et lille eksempel.
Simpelt eksempel
Her har vi en ultra-simpel datamodel med nogle personer der kan tilhøre en organisation.
CREATE TABLE IF NOT EXISTS `person` ( `uid` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100), `email` varchar(100), `organisation_uid`, PRIMARY KEY (`uid`), KEY `name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_danish_ci AUTO_INCREMENT=1 ; CREATE TABLE IF NOT EXISTS `organisation` ( `uid` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100), `website` varchar(100), `comment` text, PRIMARY KEY (`uid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_danish_ci AUTO_INCREMENT=1 ;
For at få adgang til data skal vi oprette nogle klasser:
class Person extends DfcRecord {
}
class Organisation extends DfcRecord {
}
Nej, der er faktisk ikke brug for mere til at starte med. Der er nogle navngivningkrav:
- Lower-case af klassenavnet svarer til tabel-navnet.
- En relation skal have navnet på relationer med “_uid” på. Eksempelvis peger Person på Organisation med feltet organisation_uid.
Træk en person ud baseret på navnet i variablen $person_name:
$person = Person::getBy('name', $person_name);
Hvis man så skal have fat i personens organisation:
$organisation = $person->getParent('Organisation');
Skal man have fat i alle organisationens personer kan det også klares:
$persons = $organisation->getChildren('Person');
Skulle man få lyst til at tilknytte personen til en anden organisation er det noget tilsvarende:
$person->attachTo($other_organisation); $person->save();
Disse typer af udtræk og skrivninger dækker over 90% af SQL’en i de fleste programmer.
Visning af data
Et andet punkt er præsentation af data, især i formularer, på en bestemt måde. I en klassisk måde at navngive felterne i en formular kunne man måske let blive lokket til at skrive noget i denne retning:
<div> <label>Navn:</label> <input type="text" name="name" value="<?= $person->name ?>" /> </div>
Problemet kommer hvis man gerne vil redigere både en organisation og en person, for de har jo begge et felt navngivet “name”. Eller hvad hvis man vil redigere to eller flere personer på én gang, eller to eller flere personer, samt en organisation på én gang?
Vi har løst dette ved at give hvert felt i databasen et unikt navn som trækkes fra record-objektet:
<div>
<label>Navn:</label>
<input type="text" name="<?= $person->getFieldName('name') ?>" value="<?= $person->name ?>" />
</div>
Dette vil blive til noget html-kode i denne retning:
<div> <label>Navn:</label> <input type="text" name="Person[23][name]" value="Jens Hansen" /> </div>
Når man skal læse HTTP-POST-værdierne man nu læse og behandle alle records af alle typer på ensartet vis. Formen vil være:
$_POST[Klassenavn][record-id][feltnavn]
Udvikling med Dfc ORM
Vi synes det er rigtigt hurtigt og let læseligt at modellere data med DfcRecord. Den er dog kun M’et i et normalt MVC-setup. Der er også andre små trick til at reducere udviklingstiden og gøre koden mere fleksibel. Noget vi formentlig vil skrive om i fremtidigt blog-indlæg. Vi overvejer også at udgive vores DfcRecord-ORM som en del af en lidt større værktøjskasse, som også indeholder klasser til at skrive i fuld MVC.
