OOAF : principe de fonctionnement du framework applicatif orienté objet

Cette page décrit le principe de fonctionnement de OOAF, fondation sur laquelle a été développé Gloux.

Les avantages d'OOAF, et à quoi ça sert : OOAF est un framework de développement qui permet de faire pas mal de choses de façon automatique : en gros, vous définissez la structure de votre base de données, et OOAF va automatiser pas mal de la gestion des données, la génération des formulaires, des listings, la génération des requêtes SQL de mise à jour, suppression, etc ...

OOAF est vraiment bien pour vous si vous êtes mauvais en SQL ou que vous n'aimez pas cela.

Les inconvénients d'OOAF : ça a été développé il y a quelques années, sans doute au fond d'une cave par un ou plusieurs programmeurs autistes ne sachant manifestement pas écrire autre chose que du code vu la légèreté de la documentation qu'il a ou qu'ils ont laissés derrière eux.

OOAF définit une classe de base, qui est ensuite étendue par une autre classe, qui elle même est étendue par une autre classe, qui elle même ... ce qui fait que quand on a un message d'erreur, on se retrouve souvent à sauter de classe fille en classe mère, pour au final tomber sur la classe initiale, dont le traitement est si générique, qu'on a du mal à comprendre ce que notre erreur peut bien avoir comme rapport avec.

Il n'y a aucun doute qu'OOAF est un système de qualité, et a été développé par une entité organique ayant une très bonne expérience de la programmation orienté objet. Mais bon, allez-y voir, c'est tout de même légèrement documenté.


Soit la table sql suivante :

CREATE TABLE `countries` (
  `id` varchar(3) NOT NULL default '',
  `name` varchar(255) NOT NULL default '',
  `creationDate` datetime default NULL,
  `updateDate` datetime default NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `name` (`name`),
  KEY `creatorLogin` (`creatorLogin`),
  KEY `updaterLogin` (`updaterLogin`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Pour insérer, modifier ou supprimer des enregistrements dans cette table, il est nécéssaire :

  1. De définir la classe qui définie l'objet de type dbTable "countries" en spécialisant la classe dbTable.
  2. De définir la classe qui définie l'objet de type dbRecord "country" en spécialisant la classe dbRecord.

1/ La spécialisation de dbTable

Un objet de type dbTable contient la description la structure d'une table sql (colonnes, clefs primaires, index unique, clefs étrangères, etc). La spécialisation de dbTable se fait en surchargeant la méthode protégée dbTable::createTable() de la manière suivante :

<?php

#Class
class countries extends dbTable
{
	#Protected methods
	protected function createTable()
	{
		parent::createTable(); #Ne pas oublier

		#Définition du client de base de données à utiliser pour manipuler la table sql
		$this->dbClient(dbClient::connect(dbClientUrl));

		#Définition du nom de la base de données dans laquelle est la table sql
		$this->database = $this->dbClient->getDefaultDatabase();

		#Définition des colonnes de la table sql
		$this->varchar('id', '', 3, false, 'ID')->setMinLength(2);
		$this->varchar('name', '', 255, false, 'Name')->setNotEmpty();

		#Définition des indexs de la table sql
		$this->primaryKey(array('id'), 'ID %s is already defined');
		$this->uniqueIndex('name', array('name'), 'Name \'%s\' is already defined');
	}
}

?>

NOTE : un objet dbTable est un singleton. Pour obtenir une instance de cet objet, il est donc nécéssaire de faire :

<?php

$countries = dbTable::create('countries);

?>

2/ La spécialisation de dbRecord

Un objet de type dbRecord permet de manipuler un enregistrement d'une table sql, à l'aide de la description fournie par un objet dbTable. La spécialisation de dbRecord se fait en surchargeant son constructeur de la manière suivante :

<?php

#Class
class country extends dbRecord
{
	#Constants
	const dbTable = 'countries';

	#Public methods
	public function __construct($init = null, $lock = null)
	{
		parent::__construct(dbTable::create(self::dbTable), $init, $lock);
	}
}

?>

Afin que l'objet dbTable countries sache quel objet dbRecord doit être utilisé pour manipuler la table sql, il est nécéssaire d'ajouter dans la classe countries la méthode suivante :

<?php

class countries extends dbTable
{
...

	public function getDbRecord()
	{
		return new country();
	}

...
}

?>

3/ Mise en oeuvre

3.1/ Insertion

Pour insérer un enregistrement dans la table sql countries :

<?php

$country = new country();

$country->id = 'FR';
$country->name = 'FRANCE';

if ($country->insert() == true)
{
	echo 'Pays inséré avec succés';
}
else
{
	foreach ($country->getErrors() as $error)
	{
		echo $error . '<br />';
	}
}

?>

La méthode dbRecord::insert() se charge de vérifier la cohérence des données de l'objet par rapport à la description de la table définie dans l'objet countries (vérification du type, du format, de l'unicité des clefs, de la présence des éventuelles clefs étrangéres dans la base, etc). L'insertion est réalisée dans une transaction sql.

Si l'insertion n'est pas réalisée suite à un probléme (clef primaire non unique, format non valide, etc), la transaction n'est pas commitée. Attention, les transactions ne sont efficaces que si le type de la table est innodb.

3.2/ Modification

Pour modifier un enregistrement dans la table sql countries, il est nécéssaire de tout d'abord chargé dans l'objet country le contenu du record à modifier, puis de le modifier, et enfin de sauvegarder les modifications :

<?php

$country = new country();

if ($country->load(array('id' => 'FR')) == false)
{
	echo 'Impossible de charger le pays avec l\'id \'FR\'';
}
else
{
	$country->name = 'FRANCE UPDATED';

	if ($country->update() == true)
	{
		echo 'Pays modifié avec succés';
	}
	else
	{
		foreach ($country->getErrors() as $error)
		{
			echo $error . '<br />';
		}
	}
}

?>

3.3/ Suppression

Pour supprimer un enregistrement dans la table sql countries, il est nécéssaire de tout d'abord charger dans l'objet country le contenu du record à modifier, puis de le modifier, et enfin de sauvegarder les modifications :

<?php

$country = new country();

if ($country->load(array('id' => 'FR')) == false)
{
	echo 'Impossible de charger le pays avec l\'id \'FR\'';
}
else if ($country->delete() == true)
{
	echo 'Pays supprimé avec succés';
}
else
{
	foreach ($country->getErrors() as $error)
	{
		echo $error . '<br />';
	}
}

?>