J'ai une webapp écrite en PHP en utilisant un backend de base de données MySQL. Cette question pourrait tout aussi bien s'appliquer à n'importe quelle langue et application essayant d'utiliser une base de données SQL et une conception MVC OOP.Quelles sont les meilleures pratiques pour empêcher le fluage SQL?
Comment garder votre code SQL restreint au modèle?
Il y a une histoire plus longue spécifique à mon cas derrière la question. Comme mentionné précédemment, je travaille sur un site web PHP/MySQL/AJAX. Je l'ai conçu en utilisant les principes de conception OOP et MVC - avec un modèle, vue et contrôleur. J'ai réussi à garder les éléments de la vue - tels que le balisage et le style - entièrement restreints à la vue et à les rendre facilement réutilisables. Je pensais que j'avais fait la même chose avec le code SQL. Mais au fur et à mesure que le travail progresse, il devient évident que le modèle a besoin d'être sérieusement refactorisé.
La méthode que j'avais trouvée pour conserver le SQL dans le modèle consistait à encapsuler chaque requête SQL dans son propre objet de requête. Et puis, quand j'ai eu besoin d'appeler du SQL dans la vue ou le contrôleur, j'accédais à la requête via une usine. Aucun code SQL n'existe dans le contrôleur ou la vue.
Mais cela est devenu extrêmement fastidieux. Je ne pense pas que je gagne quelque chose en faisant cela et je passe beaucoup trop de temps à créer des requêtes avec des noms comme "SelectIdsFromTableWhereUser". L'usine pour les requêtes approche des milliers de lignes. Un peu de recherche dans Eclipse a révélé que la grande majorité de ces requêtes sont utilisées dans un ou deux endroits et jamais plus. Pas bon.
Je sais que dans un bon MVC vous voulez garder le SQL complètement séparé du contrôleur ou de la vue. Mais à ce stade, il me semble qu'il aurait été préférable de simplement placer le SQL là où il était nécessaire dans le code et ne pas essayer d'enterrer le code de base de données dans le modèle. Ces requêtes ne sont utilisées qu'une seule fois, pourquoi s'embêter à les encapsuler?
Est-il si important de séparer le SQL du contrôleur ou de la vue? Qu'est-ce qui est gagné en faisant cela? Que perd-on en lui permettant de se propager? Comment pouvez-vous résoudre ce problème?
Modifier Par demande, voici un peu plus de détails sur mon modèle.
Il y a deux parties. La partie Tables et la partie Requêtes. La partie Tables contient les objets de domaine - principalement conçus comme des wrappers autour d'objets de classe qui sont des analogues exacts des tables de la base de données. Par exemple, il peut y avoir une table de base de données Foo
qui a les champs id
, name
et type
. Il y aura un objet Table (class FooTable
) qui a un tableau avec les champs 'id', 'name' et 'type'. Il ressemblerait à ceci:
class FooTable extends MySQLTable {
private $id;
private $data;
private $statements;
public function __construct($id, $data=NULL, $populate=false) {
// Initialize the table with prepared statements to populate, update and insert. Also,
// initialize it with any data passed in from the $data object.
}
public function get($field) {}
public function set($field, $value) {}
public function populate() {}
public function update() {}
public function insert() {}
}
S'il y a une table de base de données fooBar
qui a un à plusieurs rapport (un Foo
beaucoup Bars
) avec des champs id
, fooID
et bar
alors il y aura un objet Tableau FooBar
(class FooBarTable
) qui ressemblera à peu près au même que ci-dessus FooTable
.
Le FooTable
et plusieurs objets FooBarTable
seront tous deux contenus dans l'objet Foo
. Attribuez un ID à la fabrique d'objets Foo
à un tableau Foo
et il se remplit avec les données Foo
et tous ses Bar
s et leurs données.
Les objets de requête sont utilisés pour extraire les ID Foo
dans l'ordre dans lequel ils sont nécessaires. Donc, si je veux que les objets Foo
soient classés par date, par vote ou par nom, j'ai besoin d'un objet de requête différent pour le faire. Ou si je veux sélectionner tous les objets Foo
qui ont un Bar
dans une certaine gamme. J'ai besoin d'un objet de requête.
La plupart du temps, j'utilise les objets Table (les wrappers, pas les tables de base) pour interagir avec la base de données. Mais quand il s'agit de sélectionner les objets de la table, c'est là que les requêtes entrent.
Lors de la conception originale, je ne pensais pas qu'il y aurait trop de requêtes et je pensais qu'ils allaient être réutilisés. Comme il peut y avoir plusieurs endroits où je voudrais que les Foo
s dans l'ordre de la date. Mais cela n'a pas fonctionné de cette façon. Il y en a beaucoup plus que prévu et la plupart d'entre eux sont à un coup, utilisés une fois dans une vue ou un commandement, et plus jamais. Je pensais aussi que les requêtes pouvaient encapsuler un SQL assez complexe et qu'il serait bon de les avoir comme objets de sorte que je serais toujours sûr de leur donner les données dont ils avaient besoin et que ce serait un environnement relativement sain pour tester la requête SQL elle-même . Mais encore une fois, cela n'a pas fonctionné de cette façon. La plupart d'entre eux contiennent un langage SQL assez simple.
Je pense que vous avez déjà répondu à votre propre question. Vous avez suivi ce qui semblait être une «meilleure pratique» raisonnable, seulement pour constater que cette pratique est devenue pénible pour un avantage marginal. C'est bien d'être "correct", mais il vaut mieux être "efficace". –
@Robert J'ai compris, mais je me demandais si la façon dont je suivais la «meilleure pratique» était ce qui me causait des problèmes. Existe-t-il d'autres moyens de limiter l'accès au modèle au modèle que d'encapsuler des requêtes qui seraient meilleures pour de nombreuses requêtes à usage unique? –
@Robert Je pense qu'il est un peu prématuré de jeter l'abstraction au vent juste parce que Daniel ne fait pas de bonnes abstractions. – timdev