IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Contribuez / Téléchargez Sources et Outils PHP Discussion :

Classe de gestion de BDD


Sujet :

Contribuez / Téléchargez Sources et Outils PHP

  1. #1
    Futur Membre du Club
    Inscrit en
    Août 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Août 2007
    Messages : 14
    Points : 6
    Points
    6
    Par défaut Classe de gestion de BDD
    Bonjour

    Oui encore une comme qui dirait, en fait j'ai surtout besoin de vos retours que je sais être pointus pour avoir lu beaucoup par ici. Cette classe a été écrite avec pour but de me simplifier la vie lorsqu'il me fallait faire appel a la bdd pour demander une requete select et a chaque fois subir le while etc de pdo.

    J'ai donc créé cette petite classe, avec mes compétences limitées (à savoir surtout, non professionnelles) qui évolue au fur et a mesure de mes besoins, pour l'instant elle ne supporte que les select, mais vu la vitesse d'avancement de mon projet je la rallongerai bien sur pour supporter les create update et autres joyeusetés.

    Son fonctionnement est simple. C'est un singleton, et les appels a ses fonctions sont clairs. La méthode d'appel permet surtout d'éviter les erreurs de syntaxe puisque c'est la fonction qui recrée la requete sql en fonction du tableau contenant les différents arguments du select. Elle parse ensuite la ressource obtenue et la renvoit sous forme de tableau.

    Plutot que des grands discours :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    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
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    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
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    <?php
     
    /***********************
    //   CLASS.SQL.PHP
    //   BALLUAS MAXIME
    //   COPYRIGHT 2012
    /************************/
     
     
     
    class zSql
    {
    	// conteneur du singleton
    	private static $instance;
     
    	// attributs de configuration
    	private $config;
    	private $login;
    	private $password;
    	private $database;
    	private $server;
     
    	// attributs de fonctionnement
    	private $conDB;					// connexion courante à PDO
    	private $nbQueries = 0;			// nombre de requetes sur la page
    	private $prefix;				// préfixe inhérent à toute ressources du site
    	private $queries = array();		// stockage de toutes les requetes SQL de la page
     
    	private function __construct($config, $queryCountSave = 0)
    	{
    		// si on est en train de juste reseter la classe, alors on garde le précédent compteur de requetes, sinon on le laisse à 0
    		if ($queryCountSave != 0)
    			$this->nbQueries = $queryCountSave;
     
    		// le constructeur en lui-même
    		$this->config	= 	$config['constant'];
    		$this->server 	= 	$config['database']['server'];
    		$this->database = 	$config['database']['database'];
    		$this->login 	= 	$config['database']['login'];
    		$this->password = 	$config['database']['password'];
    		$this->prefix 	= 	$config['constant']['prefix'];
     
    		// connexion a la BDD
    		$this->conDB = $this->connect();
    	}
     
    	public static function getInstance($config)
    	{
    		if (is_null(self::$instance))
    			self::$instance = new zSql($config);
     
    		return self::$instance;
    	}
     
     
     
     
    	/************************/
    	// CONNECT
    	// Connexion à la BDD via PDO
    	//
    	// --- Gestion des erreurs
    	//
    	private function connect()
    	{
    		$con = FALSE;
     
    		try
    			{$con = new PDO('mysql:host='.$this->server.';dbname='.$this->database, $this->login, $this->password);}
    		catch (Exception $exc)
    			{die('Erreur : '.$exc->getMessage());}
     
    		return $con;
    	}
     
     
     
    	/************************/
    	// EXEC
    	// Execution de la requete par PDO
    	//
    	// --- conversion de l'array en requete
    	// --- soumission requete a PDO
    	// --- découpage de l'objet en un tableau manipulable
    	//
    	public function exec($query = array(), $echoQuery = FALSE)
    	{
    		// conversion requete sous forme d'arguments en tableau en requete sql eecrite
    		$q = $this->arrayToSql($query);
     
    		// si l'argument echoQuery est mis sur True, le code d'origine demande ensuite de l'afficher
    		if ($echoQuery)
    			echo $q;
     
    		// enregistrement dans le registre des requetes de la page
    		$this->queries[] = $q;
     
    		// execution de la requete sql par pdo sur la bdd
    		$data = $this->conDB->query($q) or die(print_r($this->conDB->errorInfo()));
    		$this->nbQueries++;
     
    		// decoupage et mise en tableau
    		$data = $this->fetch($data);
     
    		// si un seul enregistrement, on retourne sans le sous tableau, sinon on retourne tout tel quel pour que ce soit parcouru apres
    		if (count($data) == 1)
    			return $data[0];
    		else
    			return $data;
    	}
     
    	/************************/
    	// ARRAYTOSQL
    	// Conversion du tableau d'arguments en requete SQL ecrite et syntaxée
    	//
    	// --- reconstruit la requete demandée en sélectionnant un par un les items du tableau passé
    	//
    	private function arrayToSql($query)
    	{
    		// création requete selon données de $query (array)
    		$q = '';
     
    		// select
    		$q .= 'SELECT ';
    		$count = 1;
    		foreach ($query['select'] as $s)
    		{
    			if ($count != 1)
    				$q .= ', ';
     
    			// si un argument est passé a coté du champ (count, sum, autre) on en entoure le champ
    			// sinon on ajoute juste le nom du champ
    			$s = explode('@',$s);
     
    			if (isset($s[1]))
    				$q .= $s[1].'('.$s[0].')';
    			else
    				$q .= $s[0];
     
    			$count++;
    		}
     
    		// from
    		$q .= ' FROM ';
    		$count = 1;
    		foreach ($query['from'] as $f)
    		{
    			if ($count != 1)
    				$q .= ', ';
     
    			$q .= $this->prefix.$f;
     
    			$count++;
    		}
     
    		// join
    		if (isset($query['join']))
    		{
    			foreach ($query['join'] as $j => $on)
    			{
    				$on = explode('@',$on);
    				$onFrom = $this->prefix.$on[0];
    				$onTo 	= $this->prefix.$on[1];
     
    				$q .= ' JOIN '.$this->prefix.$j.' ON '.$onFrom.' = '.$onTo;
    			}
    		}
     
    		// where
    		if (isset($query['where']))
    		{
    			$count = 1;
    			foreach ($query['where'] as $w)
    			{
    				if ($count == 1)
    					$q .= ' WHERE ';
    				else
    					$q .= ' AND ';
     
    				$q .= $w;
     
    				$count++;
    			}
    		}
     
    		// order by
    		if (isset($query['order by']))
    		{
    			$ob = explode('@',$query['order by'][0]);
     
    			$q .= ' ORDER BY '.$ob[0].' '.$ob[1];
    		}
     
    		// limit
    		if (isset($query['limit']))
    			$q .= ' LIMIT '.$query['limit'][0];
     
    		return $q;
    	}
     
    	/************************/
    	// FETCH
    	// Extrait un tableau de données de l'objet PDO retourné
    	//
    	// --- pour chaque item de l'objet PDO, construit un tableau
    	// --- vire chaque doublon enregistré par un nombre
    	//
    	private function fetch($data)
    	{
    		$array = array();
     
    		while ($datum = $data->fetch())
    		{
    			$count = (count($datum)/2);
    			for ($i=0; $i<$count; $i++)
    				unset($datum[$i]);
     
    			$array[] = $datum;
    		}
     
    		return $array;
    	}
     
     
    	// retourne le nombre de requetes utilisées
    	public function getNbq()
    	{
    		return $this->nbQueries;
    	}
     
    	// retourne le tableau des requetes faites
    	public function getQueries()
    	{
    		return $this->queries;
    	}
    }
     
    ?>
    Elle permet aussi de compter le nombres de requete utilisées pour la page et stocke les requetes sql (pas leur ressources) passées par elle.

    Son utilisation se fait ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $this->zSql = zSql::getInstance($this->config);
    $q = array(
    			'select'	=> array('id_content','name_content','id_user','name_user'),
    			'from'		=> array('contents'),
    			'join'		=> array('users' => 'contents.author_content@users.id_user'),
    			'where'		=> array('type_content = "forum"'),
    			'order by'	=> array('posttime_content@DESC'),
    			'limit'		=> array('0,8')
    			);
     
    $data = $this->zSql->exec($q);
    Pour les count() sum() etc, il suffit pour le select de mettre 'count@champ'.

    Si on sait qu'on a plusieurs résultats :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    foreach ($data as $datum) {
    // on utilise $datum['nom_du_champ'] pour récupérer la donnée
     
    }
    Si on a sait qu'on qu'un seul résultat :
    Dans cette utilisation $this->config vaut au minimum
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $this->config['constant']['prefix'] 	= $prefix;
    $this->config['database']['server']		= $server;
    $this->config['database']['login'] 		= $login;
    $this->config['database']['password'] 	= $password;
    $this->config['database']['database'] 	= $database;
    Apres libre a chacun de modifier la syntaxe selon les variables à utiliser.

    A savoir que je l'utilise couplée à une libraire de mise en cache faite maison si ca en intéresse certains...

    J'attends vos avis constructifs !

  2. #2
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Salut,

    j'ai parcouru rapidement ta classe et voici mon avis :
    l'approche retenue ne me semble pas bonne : cette classe a trop de responsabilités : tu mélanges un peu tout : connexion, exécution, construction de la ressource.
    Et bizarrement ta classe n'offre aucune sécurité quant à l'injection des données en provenance de l'utilisateur.
    Tu devrais au moins préparer ta ressource et ensuite seulement l'exécuter.
    Bref, je t'invite à bouquiner un peu les subtilités de PDO.

    Tu trouveras sur le net des tas et des tas de classes offrant ce genre de service : prend le temps de les décortiquer et inspire-toi en.

    Bonne continuation.

  3. #3
    Futur Membre du Club
    Inscrit en
    Août 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Août 2007
    Messages : 14
    Points : 6
    Points
    6
    Par défaut
    Bonsoir,

    Merci de ta réponse rapide.
    Maintenant concernant les responsabilités de cette classe, je dois avouer qu'il s'agit là d'une logique qui m'échappe, que celle qui serait de compartimenter entre plusieurs classes, ce qu'une seule peut réunir.

    Je m'explique. Mon but pour cette classe a justement été de réunir tous mes besoins quant à la bdd, donc en fait la connexion à pdo, et les executions de requetes via des appels simplifiés (ou au moins clarifiés). Je voulais justement que cette classe de gestion soit le plus possible "autonome", et que sa finalité soit pour le code qu'il l'utilise autour, une magnifique simplification de l'utilisation de pdo qui m'a parue, à l'instar de mysql_ a son époque, peu claire pour le néophyte, et franchement chiante à utiliser dans le code de tous les jours.

    C'est la raison pour laquelle elle fait tout, connexion, soumission des requetes, parsing des ressources, pour renvoyer des données le plus simple d'accès possible.
    Voilà en gros ma vision des choses, qui je le concède est peut être erronée aux yeux de codeurs avec plus d'expériences. Mais dans ce cas, j'aimerais qu'on me partage cette vision des choses de la même façon que je l'ai faite. Je ne demande qu'à comprendre

    Pour la sécurité, my bad je l'ai pas ajoutée, vu que pour l'instant elle fonctionne seulement en local je n'ai pas encore songé à l'intégrer, mais je devrais le faire c'est vrai. Mais c'est plus quelque chose que j'imaginais intégrer à l'extérieur, donc aux appels des requetes, comme je le fais déjà pour la définition du mode et de la page, des id possibles etc. Chacun est controlé via une liste prédéfinie dans la config, il n'y a que certains noms de pages autorisées, tout autre nom renvoyant à la page d'erreur. Il n'ya que certains mode autorisés (lire ecrire editer), tout autre renvoyant au mode par défaut (donc lire ou présentation générale du contenu de cette page). Et quand est id est passé, il est traité avec intval() auparavant. De cette manière je sais que quelque soit la requete passée a la classe, aucune ne peut contenir d'injection. N-T-U-I :p

    Pour la préparation des requetes suivies de execute, j'avais imaginé à la base pour mon code, et mon moteur de contenu, de ....comment dire, "prévoir", quelles ressources seront nécessaires à l'affichage de la page, et donc in fine quelles requetes seront nécessaires. De cette façon le moteur de contenu aurait pu balancer ces requetes à PDO avec prepare, et le moment venu à l'affichage du template final, récupérer les données d'un coup avec execute() pour les remplacer dans le template.
    J'ai finalement opté pour la solution que j'utilise actuellement, les requetes directes à la volée selon les besoins, combiné avec un systeme de cache qui fonctionne assez simplement, en stockant l'array renvoyé par cette classe dans un fichier avec serialize et un nom passé en argument au système de cache (appelé avec cacheThis($namefile, $data) et getCache($namefile)).

    Je suis passé à PDO assez récemment (disons 2 mois), et j'ai vite fait le tour de ses possibilités dès le départ, je ne la maitrise pas dans le sens ou j'en connais les plus fines subtilités, mais je pense en avoir saisi les possibilités générales.

    Il s'agit simplement de ce dont je jasais dans le début de ce post, à savoir une vision des choses différentes entre nous. Différend que je pense, je gagnerai beaucoup a me voir expliqué arguments à l'appui.

    Je ne demande qu'à apprendre, et ton post est déjà pour moi, par les questions qu'il me fait me poser, un autre pas vers un meilleur code. Si quelqu'un, ou toi-même peut donc m'éclairer, je vous en prie, faites ^^

    EDIT : j'oubliais un élement auquel je voulais réagir. J'ai suivi tes conseils et me suis penché sur les autres classes similaires de ci de la sur le web. Bien souvent en effet le seul but de la classe est d'ouvrir/maintenir la connexion, et cela est bien souvent réalisé comme la mienne via singleton. Mais je n'arrive pas à comprendre l'usage "limité" de ces classes. Certaines héritent PDO, ce qui est aussi un concept intéressant mais alors elles deviennent comme la mienne, complétant les fonctions intrinsèques de cette librairie par leurs propres ajouts, leurs simplifications de syntaxe.

  4. #4
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Salut,

    Si ton objectif c'est de faire une classe utilitaire, il faut qu'elle soit vraiment utile or ton code est trop simplifié pour qu'elle le soit.

    Je m'explique :

    - vu qu'elle est basée sur PDO qui est déjà une abstraction de l'accès aux bases de données, ta connexion doit au moins offrir les même possibilités que PDO. Or dans ton cas de figure ce n'est pas le cas. La gestion de la connexion devrait être séparée de manière par exemple à les regrouper en pool et offrir un paramétrage exhaustif à l'ouverture de connexion donc pour moi tu devrais créer une classe Connection.
    Chaque connexion devrait avoir un identifiant unique de manière à pouvoir la solliciter au besoin.

    - ensuite dis toi bien que créer un SELECT est différent d'un INSERT ou d'un UPDATE et je ne te parle même pas d'une procédure stockée... C'est encore un métier à part : tes ressources ainsi que leur assemblage devraient être gérées individuellement. Dans ce sens tu peux aussi descendre d'un cran au niveau des clauses qui sont factorisables : par exemple un WHERE sera géré de la même manière pour un SELECT ou un UPDATE...

    - enfin en ce qui concerne l'exécution, c'est un métier à part : cette classe devra faire le lien entre la connexion, la ressource et le résultat de l'exécution.

    Ne pas mélanger les les torchons et les serviettes est une règle d'or en POO.
    Tu débutes, c'est normal de ne pas isoler correctement les métiers mais n'oublie pas de peser le pour et le contre de chaque option retenue.

    Voici une idée d'ébauche :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class Connection { }
     
    abstract 
    class Resource { 
        abstract public function build();
    }
     
    class Select extends Resource { }
    class Insert extends Resource { }
    class Update extends Resource { }
    class Delete extends Resource { }
    class StoredProc extends Resource { }
     
    class Engine {
     
        public static function select(Select $res, $cnx_id) { }
        public static function insert(Insert $res, $cnx_id) { }
        public static function update(Update $res, $cnx_id) { }
        public static function delete(Delete $res, $cnx_id) { }
        public static function storedProc(StoredProc $res, $cnx_id) { }
     
    }
    Tu verras, intellectuellement c'est corsé. Prends d'entrée de jeu un joli tableau Veleda ça aide pour la compréhension et le suivi de enchaînements.

    Allez bon courage.

  5. #5
    Futur Membre du Club
    Inscrit en
    Août 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Août 2007
    Messages : 14
    Points : 6
    Points
    6
    Par défaut
    Bonjour,

    Ok je comprends bien mieux maintenant. Le but est donc de diviser les différentes étapes et fonctions pour les spécialiser un maximum. Au final il est vrai qu'il est encore plus facile pour le code environnant de se voir simplifiés les appels si tout cela est bien construit.

    Pas besoin de tableau je vois déjà la structure du code que je vais devoir mettre en place ^^
    Je vais suivre ta structure globale, et factoriser les clauses communes entre types de requetes est une excellent idée.

    Merci de ces infos, je devrais vous revenir prochainement

  6. #6
    Membre régulier Avatar de Alcide_
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2008
    Messages
    79
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juin 2008
    Messages : 79
    Points : 114
    Points
    114
    Par défaut
    Si tu veux sur-coucher PDO, renseigne-toi sur ce qui a déjà été fait, je prendrai 2 exemples qui sont Zend_Db et Doctrine.

    Beaucoup de design-patterns existent pour répondre aux problèmes de conception de l'abstraction des données.

    • Adapter
    • DataMapper
    • ActiveRecord
    • Table/Row Data Gateway
    • ...


    L'implémentation proposée par rawsrc est intéressante mais j'ai du mal à percevoir l'apport de la séparation des type de requêtes sous forme de classes (ce sont des string builder ?), surtout si en plus il y a une méthode distincte pour chaque type dans la classe Engine, annulant de fait l'intérêt que pourrait avoir une classe abstraite Ressource.

    De même la classe statique Engine me pose problème car elle implémente des méthodes pouvant être spécifiques à certains type de SGBD, hors ici aucune distinction n'est possible et une classe statique n'aide pas à l'injection ou au découplage...

    Une petite remarque en passant :
    Le nommage des classes, à mon avis, mériterait d'être plus explicite, un SELECT est un type de requête et non une ressource, la ressource serait à la limite l'objet de connexion.
    «Engine» n'est pas très explicite, Moteur de quoi? Je ne sais pas si «Engine::select()» est très parlant dans un code source...

    Si l'on regarde ce qui à été fait dans la majorité des ORM web, on voit que les méthodes de la classe Engine sont plutôt présente au niveau des classes de type Adaptateur et c'est plutôt la construction de la requête SQL (au sens large) qui se voit encapsulée dans une classe à part.

    Je mets en pièce jointe un squelette (très) simplifié d'une implémentation à la Zend_Db, si ça peu aider. Il manque pas mal de chose mais pour une première approche, l'essentielle me paraît là, au moins pour la compréhension.

    Pour info, le composant Zend_Db(1.12) est utilisable de manière indépendante.

    à bon entendeur,
    Salut!
    Fichiers attachés Fichiers attachés

  7. #7
    Futur Membre du Club
    Inscrit en
    Août 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Août 2007
    Messages : 14
    Points : 6
    Points
    6
    Par défaut
    Merci pour le fichier joint, j'ai regardé ca avec intérêt

    Il m'a fallu un peu de temps pour distinguer le cheminement de la logique de cette organisation. Codant de facon assez basique j'ai du creuser dans mes souvenir pour me rappeler des interfaces mais c'est bon j'ai pigé le principe de ton exemple.

    Apres, ca reste pour moi une influence, ou dirais-je un enrichissement. C'est très intéressant de voir comment d'autres systèmes fonctionnent, mais malgré l'adage qui voudrait qu'on ne réinvente pas sans cesse la roue, moi j'ai mon adage-réponse qui dit qu'on maitrise mieux sa propre roue que celle des autres

    Je n'ai pas encore commencé la réécriture du moteur sql que j'utilise, me focalisant pour l'instant sur le moteur de contenu qui me prend pas mal de temps et d'énergie. Par contre je vois déjà ce que je veux au final pour cette classe, grace a tes explications et celles de rawsrc, comme la classe de construction de requête à la volée, la gestion de plusieurs connexions, la manipulation des ressources.
    A propos de cette dernière fonctionnalité, j'en connais une implémentation dans le code de Dotclear je l'utilisais encore avant de développer la mienne parce que je ne voyais pas les avantages de leur code. Toute une classe pour gérer un curseur sur un tableau était peut-être un avantage au sein de la structure de Dotclear, mais dans mon code ca n'avait aucun autre but que celui de lancer une machine à gaz.

    Pour le multiSGBD, j'hésite bcp pour la bonne raison que je me suis pas mal renseigné pour le choix a faire dans l'optique d'un gros projet. SourceForge utilisaient il y a peu encore MySQL et ont du switcher sur PostGreSQL pour voir un gros boost de leurs performances, leur BDD mettant à genoux le serveur avec mysql. Depuis les choses ont bien changées et il semblerait que les gains en perf de MySQL le rendent un peu plus compétitifs. Je vais donc me focaliser sur lui.

    Je mettrais la classe à jour lorsque le moment sera venu. Merci encore pour ce retour d'experience.

  8. #8
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Bonjour,

    Citation Envoyé par Alcide_ Voir le message
    L'implémentation proposée par rawsrc est intéressante mais j'ai du mal à percevoir l'apport de la séparation des type de requêtes sous forme de classes (ce sont des string builder ?), surtout si en plus il y a une méthode distincte pour chaque type dans la classe Engine, annulant de fait l'intérêt que pourrait avoir une classe abstraite Ressource.

    De même la classe statique Engine me pose problème car elle implémente des méthodes pouvant être spécifiques à certains type de SGBD, hors ici aucune distinction n'est possible et une classe statique n'aide pas à l'injection ou au découplage...

    Une petite remarque en passant :
    Le nommage des classes, à mon avis, mériterait d'être plus explicite, un SELECT est un type de requête et non une ressource, la ressource serait à la limite l'objet de connexion.
    «Engine» n'est pas très explicite, Moteur de quoi? Je ne sais pas si «Engine::select()» est très parlant dans un code source...

    Si l'on regarde ce qui à été fait dans la majorité des ORM web, on voit que les méthodes de la classe Engine sont plutôt présente au niveau des classes de type Adaptateur et c'est plutôt la construction de la requête SQL (au sens large) qui se voit encapsulée dans une classe à part.
    Effectivement, le but final est toujours le même : élaborer automatiquement une chaine SQL valide. L'intérêt d'utiliser des classes de cette manière c'est de pouvoir regrouper tes requêtes dans un entrepôt et de les paramétrer à l'exécution (pour peu que le système soit conçu de manière à te permettre de le faire facilement et très finement). La classe ainsi construite s'occupe de toute la validation, échappement et de la remontée des erreurs avant qu'elle ne soit exécutée. Bref, ni plus ni moins que ce que font les autres ORM sauf que l'approche diffère dans la mesure ou celui qui ne s'occupe pas de la base de données, ne s'en occupe pas du tout. Il utilise ces ressources qui sont mises à sa disposition sans se préoccuper de quoi que ce soit d'autre. Il n'a même pas à connaitre les noms des tables...

    Bref, tu te rapproches du fonctionnement par procédures stockées tout en conservant la souplesse de faire du SQL.

    Je te l'accorde le nommage aurait pu être mieux trouvé : Statement à la place de Resource.

    Une dernière chose, ne me parle pas d'injection de quoi que ce soit au niveau du moteur de la base de données. Il ne faut pas tout mélanger : utiliser un framework d'injection pour pouvoir utiliser sa base de données est une ineptie.
    Faut vraiment arrêter avec cette mode de vouloir tout injecter : une base de données fait partie des fondations de n'importe quel développement et avoir un lien raisonnablement fort entre elle et le code n'est pas si problématique.

  9. #9
    Membre régulier Avatar de Alcide_
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2008
    Messages
    79
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juin 2008
    Messages : 79
    Points : 114
    Points
    114
    Par défaut
    Oui oui, concernant l'injection, je parlais des classes statiques en général, d'éviter d'en faire quand on le peu (de même pour les Singleton en passant ).
    Mais bien sûr que parlez ici d'injection pour un simple accès à son SGBD n'a pas d’intérêt.

    Par contre je ne suis pas tout à fait d'accord pour le lien fort entre la persistance et le domaine métier, je trouve, quand c'est possible (et quand la taille du projet le justifie un minimum) que le découplage (avec l'IoC ou non) est vraiment le bienvenue. Ce n'est pas pour rien que Doctrine a abandonné ActiveRecord pour utiliser DataMapper.

  10. #10
    Futur Membre du Club
    Inscrit en
    Août 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Août 2007
    Messages : 14
    Points : 6
    Points
    6
    Par défaut
    J'utilise un singleton pour avoir accès à l'objet zSql dans tous les objets créés par l'objet principal. Au début je passais carrément l'objet principal à l'objet créé (- $instance = new objet($this) -), et le pattern singleton permet de se passer de cette disgrace.

    Je ne maitrise pas assez le php pour m'affranchir de cette limitation. Je sais lire et comprendre du php supérieur à mon niveau personnel, mais coder à ce niveau j'ai beaucoup de mal je me sens tout perdu.

    Par exemple votre discussion actuelle je suis largué parce que je ne connais pas les conséquences des termes sur le code qui en résulte.
    Il m'a fallu consulter ca :
    http://fr.wikipedia.org/wiki/Inversion_de_contr%C3%B4le
    http://fr.wikipedia.org/wiki/Injecti...C3%A9pendances
    http://code.anonymation.com/zend-fra...d-application/
    Pour comprendre un peu ou vous vouliez en venir.
    Le dernier article est d'ailleurs très intéressant. Je me rends compte qu'en fait ce que je faisais au début, en passant $this a mon objet, bah ca s'appelle techniquement de l'injection par constructeur. Seulement vu que $this dans mon cas contient une variable $this->content dont la taille peut faire plusieurs ko, j'imagine meme pas le massacre sur le serveur.

    J'ai personnellement toujours fait le voeu de coder léger, et même pour mon CMS (qui me sert en fait a l'élaboration d'une plate-forme participative, pour être prolixe) je dois dire qu'avoir recours a ces framework m'effraie au plus haut point. Ma peur première étant l'inconnu évidemment.

    Je n'exclue pas un jour de m'y mettre, mais pour l'instant je préfère grandement m'inspirer du concept de ces framework pour faconner le visage de mon code. Pour info en gros, la structure de mon code est la suivante :

    index.php
    - inclut les classes
    - instance l'objet principal
    - affiche la variable $objetprincipal->template;

    l'objet principal lui a cette forme exacte (je vous passe les attributs)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    public function __construct()
    	{
    		// chrono
    		$startTime = explode(" ",microtime());
    		$startTime = $startTime[1] + $startTime[0];
     
    		// config et récup données
    		$this->forceConfig = TRUE;  //true pour dev, a virer + tard
    		$this->setConfig();
    		$this->processForm();
     
    		// moteurs auxiliaires
    		$this->zStats 	= zStats::getInstance($this->config);
    		$this->zSql 	= zSql	::getInstance($this->config);
    		$this->zUser 	= zUser	::getInstance($this->config);
    		$this->zCache 	= zCache::getInstance();
    		$this->zHook	= zHooks::getInstance($this->thisAttr());
     
    		// définition page et mode, + structure affichage
    		$this->setPage();
    		$this->setMode();
    		$this->setTemplate();
    		$this->setContents();
     
    		// récupération des contenus en fonction de la liste précédemment définie par $this->setContents
    		$this->getCoreBoxes();
    		$this->getCraftedBoxes();
    		$this->getSimpleBoxes();
    		$this->getComplexBoxes();
     
    		// on complète les deux dernières {CORE_VAR} du template, qui sont:
    		// - le temps estimé pour la construction de cette page
    		// - le nombre de requetes utilisées pour cette page
    		// on le fait maintenant, à la fin, parce que :
    		// - pour le temps passé, si il est calculé plus tôt on le sous-estime grandement comparé à la réalité
    		// - pour le nombre de requêtes, s'il est extrait avec les {VARS} de la coreBox "general", il ne prend pas en compte les requetes qui suivent, donc il est faux
    		$actualTime = explode(" ",microtime());
    		$actualTime = $actualTime[1] + $actualTime[0];
    		$totalTime = $actualTime - $startTime;
    		$totalTime = number_format($totalTime,4,',','');
    		$this->content['{MICROTIME}'] 	= $totalTime;
    		$this->content['{NBR_REQ}']		= $this->zSql->getNbq();
     
    		// remplacement des variables par les valeurs
    		$this->template = str_replace(array_keys($this->content), array_values($this->content), $this->template);
     
    		// -----------------------------------------------
    		// fin du constructeur de cette classe principale	
    	}
    Concernant les box elles sont un concept de gestion de contenus dont j'ai accouché pour gérer la modularité des contenus du site, et les plugins.
    Les coreBoxes sont partie intégrante du coeur du CMS et font l'objet d'une seule classe.
    Les craftedBoxes sont des contenus dynamiques reliés à la BDD, avec un mini-template (item) propre qui est modifé selon les infos de la BDD. Une classe seule les gère en fonction de leur config :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    <?php
    // configuration générale
     
    // - $dynamique = variable a utiliser dans ce fichier
     
    // - $name 		= nom complet du type de contenu
    // - $template	= noms du template a charger
    // - $req		= requete à préparer pour ce contenu
    // - $cache		= noms des cache spécifique à chaque req ci dessus (utilisation de variables tels que {ID} pour définition de nom dynamique
    // - $replace	= remplacement des {VARS} du template avec les valeurs des données récupéres + actions dessus si nécessaire
    // - $hooks		= méthodes et classes ajoutées qui viennent éditer le contenu apres construction (array('objet1' => 'classe1','objet2' => 'classe2')
    // autant d'items au tableau que de hooks dispo pour ce contenu
     
    $config['name']			= 'Slider -A La Une-';
    $config['template']		= 'item';
    $config['req']			= array(
    								'select' 	=> array('id_content','name_content','desc_content','type_content','pictype_content','id_user','name_user'),
    								'from' 		=> array('contents'),
    								'join'		=> array('users' => 'contents.author_content@users.id_user'),
    								'where'		=> array('alaune_content = "oui"'),
    								'order by'	=> array('posttime_content@DESC'),
    								'limit'		=> array('0,8')
    						);
    $config['cache']		= 'slider_alaune';
    $config['replace']		= array(
    								'{LINK_TITLE}' 	=> array('id_content', array('before@?page={CONTENT_TYPE}&amp;lire=','switch@{CONTENT_TYPE}@type_content','topermalink@name_content')),
    								'{TITLE}' 		=> array('name_content'),
    								'{AUTHOR}' 		=> array('name_user'),
    								'{LINK_AUTHOR}'	=> array('id_content', array('before@?page=profil&amp;id=')),
    								'{IMG}' 		=> array('pictype_content', array('toimg@id_content@{CONTENT_TYPE}','switch@{CONTENT_TYPE}@type_content')));
     
    $config['addafteritem'] = "\n";
    $config['hooks']		= array();
    ?>
    Les simpleBoxes sont des contenus ...simples, comme un simple message d'accueil par ex, mais modifiables depuis le panel-admin. Ils peuvent faire appel a des craftedBoxes. Une classe seule les gère.
    Enfin les complexBoxes sont des contenus particuliers, qui en plus de pouvoir utiliser tous les autres contenus pour se construire, ont leur fonctionnement propre, car étant l'objet d'une classe qui est propre à chaque complexBox (par exemple le forum est codé dans la classe buildForum extends complexBox).
    A noter qu'il ya une gestion sommaire des "hooks" pour chaque contenu.

    Bref, vous voyez ici un aperçu de mon niveau, hautement critiquable je n'en doute pas, mais qui je pense est exploité a son maximum dans ce CMS.

    C'est pourquoi venir ici, vous lire, recevoir vos critiques et vos apprentissages, est pour moi quelque chose de très intéressant, parce qu'il me permet de faire évoluer ce niveau.

    Mais comme je le disais : j'aime ma roue mieux que celle des autres. Du coup ce qui existe me sert d'inspiration plus que de support direct à mon code.

    ...bon j'ai écrit tout ca et j'appréhende ce que vous en pensez...

Discussions similaires

  1. Organisation class pour gestion BDD
    Par momo l'plus bô dans le forum Langage
    Réponses: 2
    Dernier message: 29/08/2012, 20h29
  2. Classe de gestion de BDD par formulaire
    Par vacknov dans le forum Langage
    Réponses: 32
    Dernier message: 17/04/2007, 13h50
  3. gestion de BDD
    Par Bûth dans le forum Bases de données
    Réponses: 11
    Dernier message: 09/06/2004, 18h28
  4. Gestion de bdd MySql
    Par carlito dans le forum Débuter
    Réponses: 2
    Dernier message: 30/03/2004, 14h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo