PHP quelle couche d'abstraction MySQL ?

WRInaute accro
Bonjour

Pour terminer ce module de paiement Paypal pour mon site partenaire, je me suis mis en tête de programmer une couche d'abstraction MySQL.

J'utilise déjà PDO, et j'envisage une classe singleton ( Database ) pour les connexions, plus quelques fonctions ( = méthodes ) de classe :

Connexion : $conn = Database::getConnect();

Eventuellement des paramètres qui vont bien, ou des constantes.

Pour les résultats à une seule ligne ( select, insert, update, delete ) :

$array_result = $conn->num_fetch_row($sql) ( select : indices de champs par numéros ),
ou
$array_result = $conn->assoc_fetch row($sql); ( select : indices par noms de colonnes ).

( pour les autres que select, rendrait le code de retour ).

Pour les résultats à plusieurs lignes ( select uniquement ) :

Idem ( rows au lieu de row ) avec num_fetch_rows($sql) et assoc_fetch_rows($sql).

Dans le premier cas, un query ou un exec ( suivant type de requête ), suivi éventuellement ( select ), d'un fetch et cursorClose().

Dans le deuxième cas : prepare() => bindParam() => execute().

Le problème, serait de fixer le paramètre de prepare(), et ceux de bindParam().

Il faudrait donc, à partir de la requête MySQL, produire une chaîne traduisant les valeurs de critères après le(s) WHERE ( non suivis par SELECT ), en leur équivalents noms de colonnes préfixés par deux-points ( : ), et puis une array rendant les paramètres des bindParam().

Je comptais parser la requête MySQL, ( avec la librairie PHP-SQL-Parser-master de Github ) vers un arbre ( tree ), et pour chaque feuilles de type [expr_type]=const, remplacer [base_expr] par le [base_expr] de [expr_type] précédent ( = nom de la colonne comparée ).

J'ai mis dans un fichier texte, toutes les requêtes MySQL de mon site, je vais les classer par types ( complexité, syntaxe ) puis évaluer l'algorithme que je vais utiliser.

Merci de me dire de quelle manière vous avez résolu ce problème de couche d'abstraction SQL.

Respectueusement.
 
WRInaute accro
Je te conseille en librairie "standalone" Eloquent de Laravel:
https://github.com/illuminate/database
http://www.edzynda.com/use-laravels-eloquent-orm-outside-of-laravel/

PHP:
<span class="syntaxdefault">$posts&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxdefault">Post</span><span class="syntaxkeyword">::</span><span class="syntaxdefault">all</span><span class="syntaxkeyword">();<br />foreach&nbsp;(</span><span class="syntaxdefault">$posts&nbsp;</span><span class="syntaxkeyword">as&nbsp;</span><span class="syntaxdefault">$post</span><span class="syntaxkeyword">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;</span><span class="syntaxdefault">$post</span><span class="syntaxkeyword">-></span><span class="syntaxdefault">title</span><span class="syntaxkeyword">;<br />&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;</span><span class="syntaxdefault">$post</span><span class="syntaxkeyword">-></span><span class="syntaxdefault">category</span><span class="syntaxkeyword">-></span><span class="syntaxdefault">name</span><span class="syntaxkeyword">;<br />&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;</span><span class="syntaxdefault">$post</span><span class="syntaxkeyword">-></span><span class="syntaxdefault">user</span><span class="syntaxkeyword">-></span><span class="syntaxdefault">email</span><span class="syntaxkeyword">;<br />}&nbsp;</span><span class="syntaxdefault"></span>
 
WRInaute accro
Bonjour spout

Je vais voir si celà permet de faire des requêtes imbriquées, des requêtes complexes avec agrégats et je ne sais plus quoi.

J'ai rangé tous les types de requêtes select de mon site dans un fichier.

Je cherche ( surtout ) la performance en vitesse et la simplicité d'utilisation.

Merci beaucoup de ton aide.

Respectueusement.
 
WRInaute accro
Ca fait ORM et query builder et on px faire du "raw SQL" donc ça fait absolument tout.
Evidemment ce sera plus lent qu'autre chose car il "hydrate" (transforme) les resultset en objets PHP.
 
WRInaute accro
Bonjour spout

J'ai du traitement MySQL parfois pas très léger sur mon site.

J'ai mis dans un fichier ( et réduit ) toutes les syntaxes possible de select figurant dans mon site.

Je vais faire des parcours de l'arbre représentant la requête MySQL, en preorder récursif, pour reconstituer la requête MySQL traduite, en passant les valeurs intermédiaires dans une array à trois indices racines : Le tree intermédiaire, la requête MySQL traduite du prepare(), et l'array des paramètres de bindParam().

Je passerai en paramètre de cette fonction récursive, cette array triple, plus l'indicateur avant/après where, etc...

Je verrai çà ce week-end.

Respectueusement.
 
WRInaute accro
J'ajoute

Je suis mal barré, car je vais être obligé de découpler les prepare(), des bindParam() et execute().

En fin de compte, la seule fonctionnalité qu'il me reste à implémenter, est la production ( à partir du $query de départ ), des masques pour les prepare() et les bindParam().

Pensez-vous, que celà nécessite de transformer ce $query en arbre ( tree ) , ou que c'est possible algorithmiquement, de lire ce $query séquentiellement, et puis d'affecter la chaine du prepare(), et l'array des bindParam() ?

Il me semble, que la mise en arbre est gourmande en temps d'exécution.

Merci beaucoup de vos réponses.

Respectueusement.
 
WRInaute accro
WRInaute accro
Bonjour spout

Je vais faire une très modeste fonction de parsing, qui sera en fait un très petit interpréteur de la $query.

Je n'ai pas besoin d'adapter ni d'utiliser ma librairie de parsing html.

Ce devrait être rapide. ;)

Respectueusement.
 
WRInaute accro
Rebond

Voilà, merci de tester à l'url :

https://www.pronostics-courses.fr/parse_function.php

Il suffit de donner le paramètre sql="instruction SQL" ( sans quotes simples ni doubles ) , qui est parsé pour en déduire les paramètres du prepare() et des bindParam().

Le bug : Il n'accepte pas d'espaces dans les valeurs ( constantes ou variables ) de WHERE.

Pour l'instant il ne traite que les SELECT.

Il affiche le sql du prepare() et les paramètres des bindParam().

Je vais voir pour les espaces.

Merci de vos suggestions.

Respectueusement.
 
WRInaute accro
Rebonjour

Le bug semble résolu.

Je n'ai plus qu'à adapter pour les INSERT UPDATE et DELETE.

Merci beaucoup de vos suggestions.

Respectueusement.
 
WRInaute accro
Rebonjour

Url : https://www.pronostics-courses.fr/parse_function.php

Voilà, j'ai fait cette petite fonction, qui à partir de l'ordre SQL, génère le paramètre de la fonction prepare(), et les paramètres des fonctions bindParam().

Merci de tester, en spécifiant un paramètre sql= ( sans quotes doubles ni simples autour ).

Valide théoriquement pour ces types d'instructions SQL : SELECT, INSERT, UPDATE, DELETE.

Pour l'instant en version alpha. ;)

Merci de me signaler les erreurs.

Respectueusement.
 
WRInaute accro
Bonjour niap

Par exemple :

Code:
 https://www.pronostics-courses.fr/parse_function.php?sql=update CHEVAUX set NOMCH='aplusbegalix'  where NUMCH=345

Il ne faut pas mette d'apostrophes autour de la chaîne SQL, mais on peut en mettre où on veut à part çà.

Il ne faut pas d'espace avant ou après le signe égal du sql.

Par exemple : sql = SELECT ne marche pas.

Par contre, on peut mettre des espaces où on veut dans le paramètre SQL.

Merci beaucoup pour ton aide.

Respectueusement.
 
WRInaute accro
niap a dit:
ortolojf a dit:

Il faut tester comment ? Y a pas de champ ? 8O

Rebonjour niap

J'y ai mis un petit formulaire facile à remplir.

Merci beaucoup pour vos tests !

Mettez n'importe quel ordre SQL, du type select, ou insert, ou update, ou delete, il affiche le paramètre du prepare(), et puis les trois paramètres de chaque fonctions bindParam().

Ces deux fonctions, sont économiques en mémoire RAM, et plus performantes que les query() ou exec() directs.

Il va sans dire, que ces fonctions utilisent l'interface PDO de MySQL.

L'intérêt de ma fonction, est de simplifier le travail du programmeur SQL, je vais prochainement terminer mon interface d'abstraction MySQL.

Après, je n'aurai même plus besoin de songer aux détails de programmation, il y aura une fonction utilisant prepare() et bindParam() ( si besoin uniquement ), qui rendra l'array à deux indices des données ( en cas de lecture ), ou le code de retour sinon.

Pour le cas des prepare() séparés des bindParam() -> execute(), deux fonctions transparentes ( par exemple pour des boucles internes contenant des lectures/écritures de structures identiques. ).

Cà va me faciliter la vie, et accélérer beaucoup mon temps de développement.

Et puis, la vitesse d'exécution...

Bien à vous.

Respectueusement.
 
WRInaute accro
Je vois vraiment pas l'intérêt de ton truc qui parse la requête SQL pour binder automatiquement les paramètres:
Code:
update CHEVAUX set NOMCH='aplusbegalix'  where NUMCH=345

Cette query tu dois quand même la construire en PHP:
PHP:
<span class="syntaxdefault"><br />$nomch </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">'aplusbegalix'</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault">$numch </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> 345</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault">$sql </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"update CHEVAUX set NOMCH='</span><span class="syntaxdefault">$nomch</span><span class="syntaxstring">'  where NUMCH=</span><span class="syntaxdefault">$numch</span><span class="syntaxstring">"</span><span class="syntaxkeyword">;&nbsp;</span><span class="syntaxdefault"></span>

En quoi c'est plus simple à ce que je t'ai montré:
ORM:
PHP:
<span class="syntaxdefault">$cheval </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> Chevaux</span><span class="syntaxkeyword">::</span><span class="syntaxdefault">find</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">345</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault">$cheval</span><span class="syntaxkeyword">-></span><span class="syntaxdefault">nomch </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">'aplusbegalix'</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault">$cheval</span><span class="syntaxkeyword">-></span><span class="syntaxdefault">save</span><span class="syntaxkeyword">();&nbsp;</span><span class="syntaxdefault"></span>

Query builder:
PHP:
<span class="syntaxdefault">DB</span><span class="syntaxkeyword">::</span><span class="syntaxdefault">table</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'chevaux'</span><span class="syntaxkeyword">)-></span><span class="syntaxdefault">where</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'id'</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> 345</span><span class="syntaxkeyword">)-></span><span class="syntaxdefault">update</span><span class="syntaxkeyword">([</span><span class="syntaxstring">'nomch'</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">=></span><span class="syntaxdefault"> </span><span class="syntaxstring">'aplusbegalix'</span><span class="syntaxkeyword">]);&nbsp;</span><span class="syntaxdefault"></span>

NB:
Code:
SELECT * FROM utilisateurs WHERE prenom LIKE '%ortolo%'
Ne fonctionne pas
 
WRInaute accro
Bonjour spout

J'ai corrigé le problème.

J'avais omis de mettre le LIKE comme opérateur mathématique ( ou de comparaison ).

Je cherche avant tout la vitesse d'exécution.

Merci beaucoup de ton aide.

Respectueusement.
 
WRInaute accro
Rebonjour

Je crois avoir bien avancé la mise au point de cette fonction.

Pour spout : Cette fonction n'est pas l'interface d'abstraction SQL.

Elle en constituera simplement le composant principal, avec la surcouche ( légère ;) ) en plus.

J'aurais besoin que vous testiez.

Normalement, la syntaxe supportée devrait inclure les sous-requêtes( avec IN et NOT IN ) , l'opérateur BETWEEN, les opérateur arithmétiques courants, mais il doit certainement manquer des opérateurs.

Je ne pratique que de la manipulation de données, pas d'administration.

On peut spécifier les mots-clés en majuscules ou minuscules.

Respectueusement.
 
WRInaute discret
$sql_query = SELECT * FROM test WHERE t.catId<>0

Erreur WHERE dans la fonction parse_myql().
2eme operande manquant.
$sql_prepare =


Warning: Invalid argument supplied for foreach() in /var/www/html/parse_function.php on line 1273
;-)
 
WRInaute accro
T'as pas fini alors:
Code:
SELECT COALESCE(nickname, firstname, 'Mr./Mme.') || ' ' || lastname AS fullname FROM utilisateurs
SELECT * FROM utilisateurs WHERE id = '1'; DROP TABLE utilisateurs;--
 
WRInaute accro
Voilà c'est fait.

Merci niap, la valeur 0 était considérée comme vide par empty().

J'ai eu des problèmes de regex multiples instantanées qui portaient sur le même élément plusieurs fois.

Pour spout : Je ne fais jamais en PHP d'instructions différentes qui se suivent ( séparées par des points-virgules ).

Ta première instruction SQL ( simple ) passe.

Maintenant, ma fonction attend les visiteurs. ;)

Merci beaucoup de votre aide.

Respectueusement.
 
WRInaute accro
ortolojf a dit:
Pour spout : Je ne fais jamais en PHP d'instructions différentes qui se suivent ( séparées par des points-virgules ).
Mais ceux qui exploitent les failles d'injection SQL oui, donc il faut faire gaffe.
 
WRInaute discret
Mais s'il génère une requête préparée, il devrait être paré face aux injections sql, non ?

$sql_query = SELECT * FROM test WHERE t.catId<10 UNION select * from test2 t2 where t2.catId='10'

$sql_prepare = SELECT * FROM test WHERE t.catId<:t_catId_1: UNION SELECT * FROM test2 t2 WHERE t2.catId=:t2_catId_2:

$sql_bindParam[0] = :t_catId_1:
$sql_bindParam[1] = 10
$sql_bindParam[2] = 1

$sql_bindParam[0] = :t2_catId_2:
$sql_bindParam[1] = '10'
$sql_bindParam[2] = 2

J'imagine que $sql_bindParam[2] est le type de données ?
Dans ce cas ta fonction semble considérer que 10 est différent de '10'.
 
WRInaute accro
Oui mais il génère une requête préparée automatiquement.
Donc si:
PHP:
<span class="syntaxdefault">$id </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'id'</span><span class="syntaxkeyword">];<br /></span><span class="syntaxdefault">$sql </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"SELECT * FROM utilisateurs WHERE id=</span><span class="syntaxdefault">$id</span><span class="syntaxstring">"</span><span class="syntaxkeyword">;</span><span class="syntaxdefault"></span>
Si on passe ?id=1

Code:
$sql_prepare = SELECT * FROM utilisateurs WHERE id=:id_1:

$sql_bindParam[0] = :id_1:
$sql_bindParam[1] = 1
$sql_bindParam[2] = 1

Si on passe ?id= ; DROP TABLE utilisateurs; --

Code:
$sql_prepare = SELECT * FROM utilisateurs WHERE id=:id_1: ; DROP TABLE utilisateurs; --

$sql_bindParam[0] = :id_1:
$sql_bindParam[1] = ''
$sql_bindParam[2] = 2
BOUM
 
WRInaute accro
Bonjour

Effectivement $sql_bindParam[2] est le type de données.

Effectivement, j'ai dans ma database des champs entiers du type varchar.

Si je veux changer celà, je peux changer ma fonction : $this->PDO_type($str).

Merci me dire si c'est nécessaire. Mais celà m'obligerait à changer les type de ces champs.

Quant à la sécurisation contre les hacks, c'est la couche d'abstraction ( en amont ) qui le fera.

J'ai rajouté à l'instant les mots-clés : SHOW, CREATE, et DROP.

Merci beaucoup de votre aide.

Respectueusement.
 
WRInaute accro
Bonjour

Maintenant les INSERT avec VALUES sont théoriquement supportés.

J'avais des problèmes avec les valeurs avec espaces.

Merci de tester pour tous les type d'instructions SQL.

Quand ce sera au point, j'indexerai les bindParam par les noms de colonnes au lieu de chiffres.

J'espère que ce sera possible dans tous les cas, avec des structures de queues ou des array.

J'ai l'intention pour la couche d'abstraction, de mettre des set${nom_colonne}[$value) dans la classe Database.

Amicalement.
 
WRInaute accro
Bonjour

Je suis en train de concevoir l'interface d'abstraction de mon soft.

Le problème, est de découpler les prepare() des bindParam() et execute().

Pour mémoriser les paramètres, j'envisage une structure de de pile ( stack ).

En effet, les prepare() ( réels ) sont censés être "nestés".

J'envisage les méthodes suivantes de la classe Database :


1) my_prepare($sql)

2) fill_bindParam($array_values)

3) my_bindParam(void)

4) myExecute(void)

5) delete_bindParam(void)

6) my_queryAll($sql)

Le 1) produira le $sql_prepare, lancera le $conn->prepare($sql_prepare) , et produira en interne une $array_masque contenant le masque à remplir pour la fonction fill_bindParam($array_values).

Ceci, sauf si l'array_masque est non affectée, ce qui peut se produire quand le $sql ne comporte pas de variables ou de constantes PHP ( ç'est-à-dire s'il n'y a aucune value dans le $sql ).

Dans ce cas, appel à ma fonction aquery() de mon interface myPDO.

Sinon, cette méthode incrémentera le compteur servant d'indice pour la stack ( pile ) mémorisant les valeurs des $array_masque ( prévu dans le cas des boucles imbriquées ).

Le 2) aura en argument les values à intégrer dans le masque $array_masque courant, et le modifiera.

Le 3) déclenchera les bindParam() réelles adéquates,

Le 4) déclenchera le $conn->execute(),

Est-il opportun de joindre les 2), 3) et 4) ?

Le 5) déclenchera les unset pour l'indice de pile courant ( dépilera de la stack et donnera accès en interne ( dans la classe ) à l'array_masque de degré inférieur.

Le 6) sera une méthode à part, censée faire tout en un :

Suivant critère ( à définir ) , soit lancement d'un $conn->aquery($sql) classique ( de mon interface
myPDO déjà faite), soit lancera l'équivalent des 1) à 6), sans empilage ni dépilage.

Si SELECT, rendra l'array à deux indices lue en mémoire RAM.

Sinon, code de retour ( autre chose que SELECT ).

Merci me donner vos suggestions sur ce type d'intégration.

Celà vous paraît-il correct ou non ?

Respectueusement.
 
WRInaute accro
ortolojf a dit:
1) my_prepare($sql)

2) fill_bindParam($array_values)

3) my_bindParam(void)

4) myExecute(void)

5) delete_bindParam(void)

6) my_queryAll($sql)

PHP n'est pas pas encore assez pourri que t'as pas réussi à te décider entre snake_case et camelCase ? :mrgreen:
Maintenant il y a des coding standards: http://www.php-fig.org/psr/, pour les méthodes c'est au choix mais il faut rester cohérent.

NB: tu ne cesses de réinventer la roue des frameworks qui sont testés unitairement et largement utilisés.
 
WRInaute discret
spout a dit:
PHP n'est pas pas encore assez pourri que t'as pas réussi à te décider entre snake_case et camelCase ? :mrgreen:
:lol:
C'est clair qu'il faut que tu fasse un choix ortolojf.
Perso j'ai fait les choix suivant :
- tout est nommé en camel case
- les fonctions débutent toujours avec une majuscule
- les fonctions sont nommés dans l'ordre inverse de leur fonctionnalité
- les variables toujours avec une minuscule
- les arguments de fonction débutent toujours avec un a minuscule
- les variables globale toujours avec un g minuscule

C'est quoi la finalité de cette interface d'abstraction ?
Ouvrir ta base de données à d'autres sites ?
 
WRInaute accro
Bonjour niap

Je voulais faire en camelCase avec une minuscule au début, mais les fonctions avec "bind" auraient ressemblé à :

fillBindParam($array_values), myBindParam(void) et deleteBindParam(void)

C'est ok ?

Et les variables entièrement en minuscules ?

Super merci pour votre aide. ;)

Respectueusement.
 
WRInaute accro
Rebonjour

Je vais arranger ma fonction parse_mysql($sql), en extrayant les noms de tables et les colonnes.

Ainsi, les masques destinés aux fonctions *BindParam() seront fiables et complets.

Je montrerai le résultat dès que j'aurai terminé.

Respectueusement.
 
WRInaute accro
Hello

Je butte sur un problème conceptuel.

A propos des MyPrepare($sql) :

Après chaque MyPrepare($sql), il faut mémoriser les masques destinés aux MyBindParam($array_values).

Mais... Cette fonction MyPrepare($sql), ( qui se charge de lancer le prepare($sql_prepare) réel ) , peut très bien être lancée à l'extérieur de toute boucle interne.

Et... Les MyBIndParam($array_values) ne peuvent être lancées qu'à l'intérieur de chaque boucles internes.

Le problème, consiste à faire le lien entre les masques ( private ), produits par MyPrepare(), et leurs utilisations dans MyBindParam($array_values).

Comment ( dans la classe Database ), identifier puis relier ces deux données de masques , en amont en aval ?

Pour rappel, un masque serait une array du type :

Code:
  $array_values["TABLE1.COLUMN_1" => $value_1,
                      "TABLE1.COLUMN_2" => $value_2,
                     etc...
                     "TABLE2.COLUMN_x" => $value_x,
                     etc...
        ]

La prise en compte de ce paramètre, ne peut être que transparente.

Si un MyPrepare($sql), incrémente un compteur auquel sont rattachés les masques, comment ( sur le plan programmatique ), savoir quel est le bon masque ( bon compteur ), pour le traitement des masques par MyBindParam($array_values) ?

Normalement, celà ne pourrait être fait ( mémorisé ), que par le programmeur ?

Comment faire ?

Merci beaucoup de votre aide.

Respectueusement.
 
WRInaute accro
Voili, voilou

J'avais une approche trop orientée "programmation séquentielle", alors que c'est de la P.O.O.

Chaque masque rendu par le MyPrepare($sql), est donc un objet instancié, donc cette fonction doit rendre un pointeur $masque_bind vers ce masque, ce pointeur sera utilisé au moment du lancement de
$masque_bind->MyBindParam($array_values), ce qui permettra éventuellement de simplifier $array_values en array simple

Je ne suis pas spécialiste en P.O.O., aussi comment gérer ce pointeur et ces values ?

Au passage, les $array_values seront simplifiées, il n'y aura besoin que des valeurs ( clés numériques) , puisque l'interprétation ( stricte ;) ) des types de valeurs sera faite.

Merci beaucoup de votre aide.

Respectueusement.
 
WRInaute accro
Rebonjour

Voilà l'architecture de classes que j'envisage :

Une classe Database ( dérivée de PDO ) dont le constructeur génère le $sql_prepare, et fixe dans la classe DatabaseBindParam les conditions de type des paramètres des myBindParam(), en instancifiant DatabaseParam puis en rendant le $this de l'objet instancié.

La classe Database, comporte évidemment une fonction __get("my_prepare").

Une classe DatabaseBindParam ( dérivée de PDOStatement ) fournissant les__set($column, $value) , avec vérification forte des types, lançant les bindParam() réels. Mais le $this de cette classe, me permet-il de lancer les $this->bindParam() réels ?

Les $column, fixés par le programmeur-utlisateur, sont censés être rigoureusement les mêmes que dans la requête SQL initiale.

Dans cette classe, une méthode ad hoc $this->my_execute() chargée de déclencher le execute réel. ( Même chose ? )

Le blème serait que la connexion serait assurée par la classe singleton DatabaseConnect indépendante, avec une fonction statique getConnect() rendant $conn l'objet unique PDO de connexion.

Est-il envisageable, de lancer cette fonction statique à l'intérieur de Database, pour éventuellement lancer le $conn->prepare(_get($this->my_prepare)) directement, ?

Merci beaucoup pour vos réponses à mes questions.

Respectueusement.
 
WRInaute accro
Bonjour

Voilà, j'ai résolu semble-t-il les dépendances entre classes :

- DatabaseConnection extends PDO

Cette classe fournit la fonction singleton : getConnection(), donnant l'objet instancié $this sur la classe parente ( le même quel que soit le nombre de fonctions déclenchées ).

Le constructeur ( qui ne fait rien ), est malheureusement public, car cette classe dérive de PDO.

Contient la fonction : prepare($sql_prepare) qui lance parent::prepare($sql_prepare). ( Obligatoire pour Database ).

- Database extends DatabaseConnection.

Lance : $this->conn = parent::getConnection(); A part çà constructeur vide.

Contient la fonction : MyPrepare($sql), qui calcule $this->sql_prepare, puis lance : parent:: prepare($this->sql_prepare);

- DatabaseBindParam extends PDOStatement

Contient des setter pour les colonnes avec vérification forte des types.

Plus d'erreur à la compilation ou l'exécution, sauf des problèmes résiduels qui nécessitent une mise au point fine de l'algorithme.

Je vais déboguer puis mettre au point l'intégration.

La séquence d'utilisation :

Code:
  // Exemple 
 $sql = "select NUMCH, NOMCH  from CHEVAUX where NUMCH=3";

 // Une seule fois dans le script. Donne le handle de connexion.
  $conn = new Database;

  // Lance parent:prepare($sql_prepare).
 $bindParam = $conn->MyPrepare($sql);

  // Affectation des valeurs :
 $bindParam->NUMCH = 200;  etc... La valeur et le type sont vérifiés, puis lancement à chaque fois de la fonction parent::bindParam($column, $value, $type).

  // Appel de la fonction : parent::execute();
 $tmp_array = $bindParam->MyExecute();

  // Affichage.
  foreach($tmp_array as $key => $value)
  {
       echo "\tNUMCH = " . $value[0] . "\t\tNOMCH = " . $value[2] . "\n";
  }

  // Nettoyage.
  unset($tmp_array);
  $BindParam->deleteBindParam();
  $conn->deleteDatabase();


Pour l'instant l'intégration est minimale, et je dois encore mettre au point.

Mais il n'y a plus d'erreurs fatales, sauf pour des détails de l'algorithme.

Merci de me dire ce que vous en pensez. ;)

Respectueusement.
 
WRInaute accro
Ah oui ça à l'air super pratique, c'est clean, c'est cohérent, PSR compliant, DRY et j'en passe.

PHP:
<span class="syntaxdefault">$conn</span><span class="syntaxkeyword">-></span><span class="syntaxdefault">deleteDatabase</span><span class="syntaxkeyword">();&nbsp;</span><span class="syntaxdefault"></span>
ça on devine tout de suite que ça fait un DROP DATABASE, sympa.

Tu devrais mettre ça en open source.
 
WRInaute accro
Bonjour spout

Le nom ( Database ) de la classe, peut être n'importe quel nom.

deleteDatabase signifie : suppression de cette classe et de ses données.

Sinon, cette suppression n'aurait lieu qu'à la fin du script...

Comment résoudre le problème de l'interface ?

Je suis obligé de séparer ( fonctionnellement parlant ), le MyPrepare($sql) des affectations ( $bindParam->column = $value; etc... du reste : MyExecute().

Un instance $bindParam conserve les valeurs, ce qui peut limiter les affectations au maximum, et conserver le MyPrepare().

Pour l'intégration j'ai l'intention de faire une fonction : $conn->fetchAll() ( qui ramasse tout ), et puis une fonction spécifique pour tous les résultats réduits à une seule row : $conn->fetchRow(). ( pas de Myprepare(), seulement $conn->aquery() + $res->fetch(PDO::NUM) + $res->closeCursor() ).

Pour les petits nombres de rows : $conn->Aquery() ( idem que pour fetchRow() ).

Le blème, c'est que l'utilisateur/programmeur devrait décider ( suivant le nombre attendu de rows ), de quelle fonction lancer : Aquery(), fetchRow() ou fetchAll().

Et... Ces fonction détruisant l'objet créé à la fin, il y aurait toujours besoin éventuellement de : MyPrepare() ->affectation colonnes ->MyExecute() -> etc... ( les delete () à la fin ).

Donc : 5 fonctions plus les delete...().

Suffisant comme intégration ?

Merci beaucoup de vos réponses.

Respectueusement.
 
WRInaute accro
Joyeux Noël à tous les Wrinautes

Suite à perte/vol de mon portefeuille, je suis en train de faire refaire mes documents.

Demandes prévues pour : C.I. dans une semaine, Vitale dans 15 jours.

J'ai élargi les mots-clés en tenant compte de la version 1.7 de MySQL.

Mais, je vais revoir en profondeur la logique de la fonction myPrepare($sql) de ma classe Database.

Je vais faire une approche top-down.

Bien amicalement.
 
WRInaute accro
Bonjour niap

J'ai tous les documents pour la C.I. et la carte vitale.

Ma CB Visa me parviendra demain ou après-demain. ;)

J'attends demain pour tirer de l'argent avec mon chéquier.

Théoriquement un mauvais moment à passer.

J'ai adapté à l'instant ma librairie de parsing html pour du sql MySQL.

Cette fois-ci, j'interprète les caractères uns à uns, au lieu de par blocs.

J'ai tous les mots-clés de MySQL 5.7 dans une array, et mon interprétation des instructions MySQL sera exhaustive.

Je vais programmer la suite demain.

Respectueusement.
 
WRInaute accro
Bonjour

J'ai ma CB Visa, j'aurai ma C.I. dans trois ou quatre semaines. ;)

J'attends l'imprimé de la Sécu pour la Carte Vitale.

Tout baigne. Ouf de ouf.

Amicalement.
 
WRInaute accro
Rebonjour

Voilà, j'ai tous mes papiers sauf ma Carte d'Electeur. ;)

A propos de cette "Database Abstraction Layer Interface", j'ai mis sur mon site, un script 'essai_mysql.php' lisant l'instruction MySQL en paramètre sql, et corrigeant ses défauts.

Une très légère analyse lexicale, qui me permettra une fois validée, de traduire les masques à donner aux instructions PHP prepare( ), de manière automatique.

Voici l'url du script :

https://www.pronostics-courses.fr/essai_mysql/essai_mysql.php

L'instruction MySQL en paramètre, peut être donnée en ajoutant: ?sql=instruction_mysql , sans quotes autour du paramètre.

Merci beaucoup si vous pouviez tester de toutes les manières possibles.

Théoriquement, ce script n'accepte que des DML ( Data Manipulation ).

Merci beaucoup de vos réponses.

Respectueusement.
 
WRInaute accro
Bonjour spout

J'ai corrigé l'erreur.

Maintenant, je met un espace avant tout mot-clé, que ce soit un opérateur ou une fonction.

Il est à prévoir, qu'il y aura un espace avant les opérateurs arithmétiques, malheureusement.

Merci de me reporter d'autres erreurs.

Respectueusement.
 
WRInaute accro
Rebonjour

Maintenant çà devrait marcher. ;)

J'ai revu le problème des espaces, en réaffectant les fonctions et les opérateurs.

Les opérateurs ne sont pas précédés par un espace, les fonctions sont précédées par un espace.

Normalement çà passe avec n'importe quelles fonctions et n'importe quels opérateurs.

J'arrangerai la détection des erreurs plus tard.

Rappel d'url :

https://www.pronostics-courses.fr/essai_mysql/essai_mysql.php

Respectueusement.
 
WRInaute accro
Rebonjour

Message d'erreur corrigés.

Maintenant, je suis capable en théorie d'identifier les values ( auxquelles il faudra appliquer des masques ), et les Tables, Colonnes, et Aliases.

Si tout est correct sur le soft actuellement.

Je pourrai ainsi gérer les arrays faisant correspondre ces éléments, à charge ensuite de faire automatiquement : les prepare() réels, les bindParam() réels, et les execute().

Ceci de manière transparente et automatique.

Les bindParam() ultérieurs sur un prepare() en cours, seront fait avec des setter.

Après, la surcouche PHP sera simple et facile.

Merci beaucoup pour les suggestions déjà reçues, et merci pour vos avis et suggestions éventuels.

Respectueusement.
 
WRInaute accro
ortolojf a dit:
Après, la surcouche PHP sera simple et facile.

PHP:
<span class="syntaxdefault"></span><span class="syntaxkeyword"><?</span><span class="syntaxdefault">php<br />Cheval</span><span class="syntaxkeyword">::</span><span class="syntaxdefault">where</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'numch'</span><span class="syntaxkeyword">,&nbsp;</span><span class="syntaxdefault">200</span><span class="syntaxkeyword">)-></span><span class="syntaxdefault">count</span><span class="syntaxkeyword">();<br /></span><span class="syntaxdefault">Cheval</span><span class="syntaxkeyword">::</span><span class="syntaxdefault">whereIn</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'numch'</span><span class="syntaxkeyword">,&nbsp;[</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">,&nbsp;</span><span class="syntaxdefault">2</span><span class="syntaxkeyword">,&nbsp;</span><span class="syntaxdefault">3</span><span class="syntaxkeyword">]);<br /></span><span class="syntaxdefault">Cheval</span><span class="syntaxkeyword">::</span><span class="syntaxdefault">whereHas</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'race'</span><span class="syntaxkeyword">,&nbsp;function&nbsp;(</span><span class="syntaxdefault">$q</span><span class="syntaxkeyword">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="syntaxdefault">$q</span><span class="syntaxkeyword">-></span><span class="syntaxdefault">where</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'nom'</span><span class="syntaxkeyword">,&nbsp;</span><span class="syntaxstring">'Lusitanien'</span><span class="syntaxkeyword">);<br />});&nbsp;</span><span class="syntaxdefault"></span>

On a pas la même notion de simple et facile... :lol:
 
WRInaute accro
Bonjour spout

Moi j'instancifie ma classe Database . ;)

Je n'ai pas de méthodes ( ni de variables ) statiques.

C'est vrai que c'est mieux de diminuer le nombre d'instructions PHP.

Mais mon procédé ( algorithme ), sera de vitesse : O(n) ( linéaire ).

Celà dit, j'essaierai d'adopter ta syntaxe entièrement orientée objet.

Merci beaucoup de ton aide.

Amicalement.
 
WRInaute accro
Rebond

Avant-hier, j'ai obtenu la connexion à ma database ( sur mon ordi ) et pu lancer la classique fonction query().

Deux classes : DatabaseConnexion ( singleton de connexion ), et Database extends PDOStatement.

Les fonctions PDOStatement habituelles, sont simplement déclarées avec :

Code:
        public function __call($method, $args)
        {
                if(is_callable(array($this->pdo_statement, $method)))
                {
                        return call_user_func_array(array($this->pdo_statement, $method), $args);
                }
                else
                {
                        throw new BadMethodCallException('Undefined method Database::' . $method);
                }
        }

$this->pdo_statement est la variable contenant le résultat de :

Code:
   $this->pdo = DatabaseConnexion::getInstance();

  $this->pdo_statement = $this->pdo->query($this->sqlprepare);

Mon singleton ne pouvant pas être dérivé de PDO ( mais rendant quand même un objet PDO ), je ne sais pas comment faire autrement que de cette manière très très barbare.

En tout cas la database est accédée.


Depuis hier, je peaufine le parsing MySQL.

Celà consiste en une "finite state machine" qui lit en suivant l'instruction MySQL préformatée, et qui générera les rnasques et les valeurs.

A la fin, je traiterai le problème des variables SQL ( précédées par des ampersand & ).

Pour l'instant les ampersand sont considérés comme des opérateurs binaires, et donc je devrai ajouter en aval l'interprétation en fonction du contenu.

Cà devrait aller plus vite maintenant, j'ai l'algorithme ( en théorie ).

Il faudra résoudre le problème de l'adaptation prévisionnelle aux versions futures de MySQL.

Je vais voir à la fin, si c'est possible d'identifier les mots-clés même non connus.

Bien à vous.

Amicalement.
 
WRInaute accro
Bonjour spout

Mon ordi est en PHP 7.

J'ai deux problèmes similaires ( rien à voir avec PHP 7 ) :

1) Mon singleton DatabaseConnexion ne peut pas dériver de PDO.

2) Ma classe Database doit dériver de PDOStatement pour que les fonctions genre fetch(), fetchAll() etc natives de PDOStatement, puissent être utilisées dans Database.

Pour le 2), ces fonctions ne peuvent pas appeler leur parent, puisque Database est instanciée sans que son parent ne le soit :

Code:
    /**
         * Constructeur de la classe Database
         */
        public function __construct() {

                $this->fonc_parse = "sql_data_token";

                $this->sql_init();
        }

Je ne vois pas quels paramètres je mettrais à parent::__constuct() pour instancier le parent PDOStatement.


Pour le 1) Je n'ai accès à l'objet PDO proprement dit, qu'avec : $this->PDOInstance, et cet objet, bien que de type PDO, ne peut également pas me permettre d'instancier le parent ( pas de parent PDO puisque le constructeur de DatabaseConnexion est private ).

Voici le constructeur :

Code:
       /**
         * Constructeur de la classe DatabaseConnexion
         */
        private function __construct()
        {
                try {
                        $this->_PDOInstance = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db . ";charset=" . $this->charset . ";collate" . $this->collate, $this->user, $this->passwd);
                        $this->_PDOInstance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                } catch(PDOException $e) {
                        exit("\n\n" . 'Erreur : '. $e->getMessage() . "\n\n");
                }
        }

      /**
         * Méthode qui crée la connexion
         * si elle n'existe pas encore 
         * puis la retourne.
         */
        public static function getInstance()
        {
                if (!(self::$_instance instanceof self))
                {
                        self::$_instance = new self();
                }

                return self::$_instance;
        }


L'injection de dépendance, me permettrait-il de résoudre l'instanciation des classes parentes ?

Super merci pour ton aide.

Respectueusement.
 
WRInaute accro
Rebonjour spout

Voilà, finalement je n'ai plus besoin de dériver quoi que ce soit.

J'ai ces fonctions :

Code:
  /*
   * Classe DatabaseConnexion
   * ( renommée en DB pour faire simple )
   */
  public static function __callStatic($method, $args)
        {
                if (!is_callable(self::getInstance(), $method))
                {
                        throw new BadMethodCallException("No such method $method for DB");
                }

                return call_user_func_array(array(self::getInstance(), $method), $args);
        }

        public function __call($method, $args)
        {
                if(is_callable(array($this->_PDOInstance, $method)))
                {
                        return call_user_func_array(array($this->_PDOInstance, $method), $args);
                }
                else
                {
                        throw new BadMethodCallException('Undefined method DB::' . $method);
                }
        }

       /*
        * Classe Database
        */
        private function setPDOStatement($value)
        {
                $this->pdo_statement = $value;
        }

        private function getPDOStatement()
        {
                return $this->pdo_statement;
        }

La classe DB est correcte, les deux classes ne sont plus dérivées ( pas besoin ), mais cet affreux hack avec setPDOStatement() et getPDOStatement() me reste sur le coeur.

Ce n'est pas de la POO, mais je ne vois pas comment faire autrement.

En tout cas le problème de la classe DB est résolu.

Merci beaucoup de ton aide.

Respectueusement.
 
WRInaute accro
Pardon

Nonobstant mon ignorance manifeste en POO, je vois la fin du tunnel :

Code:
  /**
         * Constructeur de la classe Database
         */
        public function __construct() {

                $this->pdo = DB::getInstance();

                if((is_null($this->pdo))||(!$this->pdo instanceof DB))
                {
                        echo "Erreur de connexion DB.\n\n";

                        print_r($this->pdo)  . "\n";

                        throw new Exception("Erreur de connexion DB.");

                        return(true);
                }

                $this->fonc_parse = "sql_data_token";

                $this->sql_init();
        }

Après, le lancement des fonctions PDOStatement habituelles ou particulières à mon interface, sera géré normalement par le code dans Database.

Il n'y a pas besoin d'instancier plusieurs fois Database pour celà ( pour chaque lancement de fonction ), ce sera à moi de gérer les variables rendues par ces fonctions.

D'ailleurs pour gérer ces variables, j'aurai à garder trace des variables créées par ces fonctions.

J'ai remarqué sur le net, que l'utilisation des classes PDO et PDOStatement, donnait lieu en général à de la POO pure pour la classe PDO, et plutôt procédurale pour la PDOStatement.

Respectueusement.
 
WRInaute accro
Bonjour

Voici ce que j'obtiens pour l'instant :

( Je n'affiche pas les ID ).

Code:
	$sql_initiale = select A.NUMCH, A.NOMCH, B.ID  From CHEVAUX A, COURSES B WHERE B.NUMCH=A.NUMCH ORDER BY B.ID DESC LIMIT 5

		Instuctions MySQL resultantes : 

		$sql_prepare = SELECT A.NUMCH, A.NOMCH, B.ID FROM CHEVAUX A, COURSES B WHERE B.NUMCH= A.NUMCH ORDER BY B.ID DESC LIMIT 5
		$sql_masque_prepare = SELECT A.NUMCH, A.NOMCH, B.ID FROM CHEVAUX A, COURSES B WHERE B.NUMCH= A.NUMCH ORDER BY B.ID DESC LIMIT 5

	$token_select[ 0 ][ 0 ][COLUMN] = NUMCH
	$token_select[ 0 ][ 0 ][TABLE_OU_ALIAS] = A
	$token_select[ 0 ][ 1 ][COLUMN] = NOMCH
	$token_select[ 0 ][ 1 ][TABLE_OU_ALIAS] = A
	$token_select[ 0 ][ 2 ][COLUMN] = ID
	$token_select[ 0 ][ 2 ][TABLE_OU_ALIAS] = B

	$token_from[ 0 ][ 0 ][TABLE] = CHEVAUX
	$token_from[ 0 ][ 0 ][ALIAS_TABLE] = A
	$token_from[ 0 ][ 1 ][TABLE] = COURSES
	$token_from[ 0 ][ 1 ][ALIAS_TABLE] = B


		154285		EL:NINO:(HOL)
		157422		GINA:BROS:(GER)
		139745		URCK:DEL:RIO:(ITY)
		158014		GREEK:F:BOKO:(GER)
		146580		FLORAL:GIRL:(HOL)

Je n'aurai plus qu'à rapprocher les aliases.

Je vais maintenant faire les $token_where et les $token_having

Amicalement.
 
WRInaute accro
Rebonjour

Un peu d'amélioration :

Code:
		$sql_initiale = INSERT INTO FONCTIONS_TARTEAUCITRON(ID, NAME, NUM, PARAM) VALUES('APLUSBEGALIX' . ',' . 'APLUSBEGALIX' . ', ' . 'APLUSBEGALIX' . ', ' . 'APLUSBEGALIX')

		Instuctions MySQL resultantes : 

		$sql_prepare = INSERT INTO FONCTIONS_TARTEAUCITRON(ID, NAME, NUM, PARAM) VALUES('APLUSBEGALIX','APLUSBEGALIX','APLUSBEGALIX','APLUSBEGALIX')
		$sql_masque_prepare = INSERT INTO FONCTIONS_TARTEAUCITRON(ID, NAME, NUM, PARAM) VALUES('APLUSBEGALIX','APLUSBEGALIX','APLUSBEGALIX','APLUSBEGALIX')

		IS_INSERT

	$token_nom[IS_INSERT][TABLE][0] = FONCTIONS_TARTEAUCITRON

		IS_INTO

	$token_nom[IS_INTO][COLUMN][0] = ID
	$token_nom[IS_INTO][COLUMN][1] = NAME
	$token_nom[IS_INTO][COLUMN][2] = NUM
	$token_nom[IS_INTO][COLUMN][3] = PARAM

		IS_VALUES

	$token_value[IS_VALUES][ID][0] = 'APLUSBEGALIX'
	$token_value[IS_VALUES][NAME][1] = 'APLUSBEGALIX'
	$token_value[IS_VALUES][NUM][2] = 'APLUSBEGALIX'
	$token_value[IS_VALUES][PARAM][3] = 'APLUSBEGALIX'

Voilà, en théorie pour générer les masques des valeurs, il suffit de prendre les keys des values, qui sont les noms des colonnes.

Je vais d 'abord tester avec des opérateurs ( relationnels et conditionnels ) et des fonctions, qui peuvent éventuellement donner lieu à des variables indicées de manière peu adaptée, et puis après, passer à la programmation de l'interface.

Respectueusement.
 
WRInaute accro
Rebonjour

Cette fois-ci, j'ai mis en place dans le prepare(), les masques des bindParam().

Merci de me dire ce qui ne va pas :

https://www.pronostics-courses.fr/essai_mysql/essai_mysql.php

Résultat par défaut :

Code:
		Usage : https://www.pronostics-courses.fr/essai_mysql/essai_mysql.php?sql=instruction_mysql

		$sql_initiale = select A.NUMCH, A.NOMCH, B.ID  From CHEVAUX A, COURSES B WHERE B.NUMCH=A.NUMCH GROUP BY A.NUMCH DESC HAVING B.ID<2

		Instuctions MySQL resultantes : 

		$sql_prepare = SELECT A.NUMCH, A.NOMCH, B.ID FROM CHEVAUX A, COURSES B WHERE B.NUMCH= A.NUMCH GROUP BY A.NUMCH DESC HAVING B.ID<2

		prepare(SELECT A.NUMCH, A.NOMCH, B.ID FROM CHEVAUX A, COURSES B WHERE B.NUMCH= A.NUMCH GROUP BY A.NUMCH DESC HAVING B.ID<:B_ID_1);

		bindParam(:B_ID_1, 2, 1);

Vous pouvez mettre le paramètre "sql=" que vous voulez ( sans quote ).

Amicalement.
 
WRInaute accro
Bonjour

J'espère que Monsieur spout va me répondre.

Voilà ce que celà donne maintenant, avec le $sql par défaut :

Code:
Usage : https://www.pronostics-courses.fr/essai_mysql/essai_mysql.php?sql=instruction_mysql 

$sql_initiale = insert into TABLE(NUMCH, NOMCH) select NUMCH, NOMCH from CHEVAUX where NUMCH=5 

Instuctions MySQL resultantes : 

$sql_prepare = INSERT INTO TABLE(NUMCH, NOMCH) SELECT NUMCH, NOMCH FROM CHEVAUX WHERE NUMCH=5 
prepare(INSERT INTO TABLE(NUMCH, NOMCH) SELECT NUMCH, NOMCH FROM CHEVAUX WHERE NUMCH=:NUMCH_1); 

Setter/Getter : CHEVAUX->SELECT->NUMCH
Setter/Getter : CHEVAUX->SELECT->NOMCH
Setter/Getter : CHEVAUX->WHERE->NUMCH
bindParam(:NUMCH_1, 5, 1);


Théoriquement, on peut mettre n'importe quelle instruction MYSQL ( de type DML quand même ), et il te sort le prepare(), les setter/getter et les bindParam().

Les setter/getter sont très théoriques, il n'y a aucune vérification par rapport à l'utilisation pratique.

Ce sont simplement des mises en évidence des dépendances entre les tables, les éléments du langage et les colonnes.

Merci de bien vouloir tester.

J'ai testé jusqu'à présent sur cinq sql.

Il est éventuellement possible qu'il y ait des problèmes avec les subqueries.

Amicalement.
 
WRInaute accro
Rebonjour

Maintenant, il donne théoriquement tous les getters, et seulement les setter quand il y a des values.

Il faut que je peaufine l'interprétation des setter, en fonction des opérateurs arithmétiques ( à ne pas confondre avec un bindParam() ) .

Egalement, je devrais évaluer ce qu'est réellement un getter.


Code:
Usage : https://www.pronostics-courses.fr/essai_mysql/essai_mysql.php?sql=instruction_mysql 

$sql_initiale = insert into TABLE(NUMCH, NOMCH) select NUMCH, NOMCH from CHEVAUX where NUMCH=5 

Instuctions MySQL resultantes : 

$sql_prepare = INSERT INTO TABLE(NUMCH, NOMCH) SELECT NUMCH, NOMCH FROM CHEVAUX WHERE NUMCH=5 

prepare(INSERT INTO TABLE(NUMCH, NOMCH) SELECT NUMCH, NOMCH FROM CHEVAUX WHERE NUMCH=:NUMCH_1); 

Getter : $value = CHEVAUX->SELECT->NUMCH 

Getter : $value = CHEVAUX->SELECT->NOMCH 

Setter : CHEVAUX->WHERE->NUMCH op 5 

bindParam(:NUMCH_1, 5, 1);


Merci de tester :

https://www.pronostics-courses.fr/essai_mysql/essai_mysql.php

Respectueusement.
 
WRInaute accro
Voili, voilou

Maintenant, le soft supporte en théorie les update, insert, replace, select, delete ( j'ai pas essayé ), et donne ces 4 éléments :

1) Requête MySQL formatée,

2) Requête prepare formatée,

3) Les Setters et Getters. Ce ne sont que des libellés non programmés.

4) Les bindParam(). Ceux-là sont programmés.

Il y a des Getters en trop.

Pour les Setters, je ne met que ceux dont l'opérateur est le signe égal ( = ). ( avec un filtrage ).

J'ai besoin que vous testiez pour m'indiquer les erreurs éventuelles.

J'ai mis sur un fichier l'ensemble de la syntaxe MySQL 5.7

Je vais écrire tous les ordres MySQL possibles, et les passer à la moulinette.

Respectueusement.
 
WRInaute accro
Bonjour

Je ne cherche pas, à générer des ordres MySQL dynamiquement en POO, comme me l'a suggéré Spout.

J'ai l'intention d'utiliser des Setters et Getters, et je peux facilement filtrer/limiter leur production sur mon interface, en fonction des opérateurs entre autre, mais j'aurais besoin de savoir sur le plan théorique-utilisation d'une interface POO MySQL, comment doivent être utilisés ces getters/setters.

Pour l'instant je produis un setter quand la value est présente, et que c'est un SET suivant un INSERT ou un UPDATE, ou un DUPLICATE suivant un UPDATE, et que l'opérateur est le signe d'affectation ( = ).

Sinon c'est un getter.

Mais, il y a le cas des opérateurs de comparaison ( par exemple des WHERE ), ayant donc une value, quelle serait l'utilisation d'un getter ou setter dans ce cas ?

Plus généralement, quelle est l'utilisation des setter/getters en général ?

Merci beaucoup de vos réponses.

Respectueusement.
 
WRInaute accro
Bonjour

Suite à ma lecture de la documentation de Laravel ( uniquement le topic "Database" relatif à l'ORM Eloquent ), je pense adopter pratiquement les mêmes fonctions d'accès MySQL que celles d'Eloquent, en mode réduit. ( quelques fonctions en moins ).

Ceci, avec un code PHP ( de mon interface ) plus réduit, du moins deux ou trois scripts PHP.

Mais, je ne sais pas encore sous quelle forme ( interface sinon codage PHP ), j'assurerai la mémorisation des ordres MySQL de départ, à compléter/modifier après :

Code:
// Initial 
$conn = new Database("SELECT num1, num2 FROM Table1 WHERE Id LIKE 'value'");

$tmp_array = $conn->get();

// $tmp_array est une array double ( ou équivalent objet ), à traiter. 
     
// Modification :
$tmp_array = $conn->WHERE('Id', 'LIKE', 'value2')->get();

// Idem

En résumé, je ne tiens pas à faire du tout intégré POO comme Spout me l'a indiqué, et oui, je te suis très reconnaissant de m'avoir indiqué l'existence de Laravel.

Cependant, je pense ( je me trompe probablement ), que le codage MySQL en pur POO avec les fonctions de Eloquent, nécessite une réécriture de chaque ordre MySQL quand celui-ci est légèrement modifié.

Et aussi, d'après la doc de Laravel, les instructions complexes SQL, doivent être écrites en mode raw, ce qui ôte un peu de son intérêt à la syntaxe simplifiée de Eloquent.

J'ai la réponse ( grâce à Eloquent ), à ma question de l'utilisation des settrers/getters.

Spout merci pour ton aide. ;)

Très respectueusement.
 
WRInaute accro
Bonjour

Je met au point-prépare la librairie MySQL orientée objet, que je guigne depuis longtemps.

J'ai l'intention, de présenter l'interface aux programmeurs ( moi ) de cette manière :

Code:
    /*
     * ( équivalent Setter )
     */
     Table->INSERT->Column=value;

     Idem pour les REPLACE ou UPDATE, ou DUPLICATE KEY


Voici le problème, pour les SELECT :

Code:
                $sql_initiale = select sb1,sb2,sb3 FROM (select s1 AS sb1, s2 AS sb2, s3*2 AS sb3 FROM t1) AS sb where sb1 > 1

                Instuctions MySQL resultantes :
                $sql_prepare = SELECT sb1, sb2, sb3 FROM (SELECT s1 AS sb1, s2 AS sb2, s3 * 2 AS sb3 FROM t1) AS sb WHERE sb1>1
                prepare(SELECT sb1, sb2, sb3 FROM (SELECT s1 AS sb1, s2 AS sb2, s3 * :s3_1 AS sb3 FROM t1) AS sb WHERE sb1>:sb1_2);

                bindParam(:s3_1, 2, 1);
                bindParam(:sb1_2, 1, 1);

La valeur 2 dans le SELECT ( s3 * 2 AS sb3 ) est une value, donc je devrais prendre en compte la possibilité que cette value soit variable.

Donc, je dois pouvoir l'affecter dans mon interface.

Mais, de quelle manière en orienté objet ?

Comment va réagir l'utilisateur, pour affecter cette value ?

Pour ce qui est des WHERE, ceci devrait suffire :

Table->WHERE->Column('operator', value);

Merci de me dire vos suggestions.

Respectueusement.
 
WRInaute accro
Rebonjour

Problème pur de programmation orientée objet :

Supposons que j'ai ces 4 valeurs dans une array de ce type :

array('Table', 'INSERT', 'Column', value)

Evidemment je peux pour l'instant faire une affectation avec ces éléments, qui ne soit pas orientée objet.

Comment programmer ( dans une classe Database ) le setter suivant ?

Table->INSERT->Column(value);

de telle manière, que le fait de lancer ( n'importe quel $sql ) :

$conn = Database($sql)

$conn->Table->INSERT->Column($value);

permette de faire l'équivalent de l'affectation, mais en mode orienté objet ?

Merci beaucoup de vos réponses.

Respectueusement.
 
WRInaute accro
Bonjour

Changement de montre, changement de Kelton.

Je vais probablement ( mais ce n'est pas sûr ) adopter ( à peu près ) la syntaxe de Laravel, mais en majuscules.

De la forme :

TABLE('Table')->WHERE('champ', 'operator', value)->GET();

ou :

TABLE('Table')->INSERT(array('champ1' => value1, 'champ2' => value2, etc...));

A charge pour moi de faire des classes ou des fonctions pour ces tokens :

TABLE, WHERE, GET, INSERT, etc...

Je vais voir pour les dépendances entre classes.

Il faudrait que ce soit compatible, avec des Prepare() réutilisables, des bindParam() et des Execute().

Bien amicalement.
 
WRInaute accro
Franchement je comprend tjs pas pourquoi ne pas utiliser directement Eloquent ?
Largement utilisé, unit testé, supporte MySQL, Postgres, SQLite, SQL Server..., PSR/autoloading, gestion des relations, accessors/mutators, casting automatique, events, ... bref pleins de trucs que t'as même pas imaginé et pourtant super pratique.
 
WRInaute accro
Bonjour Spout ;)

Merci beaucoup de ton aide.

Sur le plan de la syntaxe, comment ( en Laravel ) , faire le sql suivant ? :

Code:
  SELECT * FROM MyTable  WHERE ( 'Champ1'='value1' AND 'Champ2'='value2' ) OR 'Champ3'='value3';

Peut-on faire :

Code:
  DB::table('MyTable')->where([['Champ1', '=', 'value1']
                               ['Champ2', '=', 'value2']
                               ])->orWhere(Champ3', '=', 'value3')->get();

Si c'est possible, je suppose que l'on peut spécifier n'importe quel logique booléenne par rapport aux conditions des where ?

C'est simplement pour le modèle syntaxique.

Merci beaucoup de ta réponse.

Respectueusement.
 
WRInaute accro
Bonjour Spout

Pour résoudre le problème des conditions where complexes, je vais tâcher de faire une conversion automatique, de la notation infixe vers la notation postfixe ( polonaise inversée ), avec l'algorithme Shunting-yard.

Après, je tâcherai de rendre indépendants les clauses where, et de mettre des save().

J'aurais des problèmes si je mettais bout à bout plusieurs clauses where.

Amicalement.
 
WRInaute accro
Problème :

Pour traduire de l'infixe en notation polonaise inversée :

- Comment présenter quelque chose de traduisible automatiquement ( du texte), et qui doit aussi être un composé d'instructions PHP ?


Ou bien, en gardant la notation infixe :

- Comment rajouter des parenthèses à la notation Laravel des where ?

Ceci, sans mettre d'instructions "function($query)(where( ) etc... )" dans les instructions Laravel ?

Correction : Seule solution : deux instructions PHP dédiées simulant les parenthèses.

La Palice ...


Merci beaucoup de vos réponses.

Amicalement.
 
WRInaute accro
Rebonjour Spout

Je tiens à faire cette interface, car Laravel nécessite quand même de faire beaucoup de choses en mode raw.


Voilà, je vais adapter pour mes besoins la syntaxe de Laravel :

Je vais adapter toutes les conditions logiques where à l'intérieur d'un seul WHERE, avec des arrays :

Exemple de where :

( ( where_unique1 AND where_unique2 ) OR ( where_unique3 AND where_unique4 ) ) AND ( where_unique5 )

Ma traduction :

Code:
  DB::TABLE('MyTable')->WHERE( array(
                              array('where_unique1', 'AND', 'where_unique2'),
                               'OR', array('where_unique3', 'AND', 'where_unique4')
                              ), 'AND', 'where_unique5')->get();


De toute façon, je fais un parsing, celà ne me gêne pas de parser une seule clause WHERE.

Chaque clause where_i , est de la forme :

where_i = array('Champ_i', 'op_i', value_i)

Cette interface me permet de présenter du contenu parsable, à traiter dans ma classe WHERE.

Elle me permettra aussi, de spécifier n'importe quel ensemble de conditions, ce que ne permet pas Laravel.

A part OR et AND, il y a aussi XOR ( je crois ) non traité par Laravel.

Et puis, plus besoin de rabouter à perte de vue des conditions difficilement interprétables avec des where complexes.

J'obligerai seulement le pauvre programmeur que je suis, à mettre toutes les conditions, même implicites.

Merci de vos réponses.

Très respectueusement.
 
WRInaute accro
Bonjour Spout

Laravel, pour moi, c'est peut-être relativement simple d'utilisation, mais je suis rétif à installer ce théorique framework ( je sais c'est une orm pas un framework ), car j'ai peur en général des framework.

A cause des ressources en RAM ( je n'ai que 8 Go sur mon VPS SSD 2016 OVH ).


Je n'ai plus ( sur le plan conceptuel ), qu'à savoir comment traiter les fonctions agrégats, et autres babioles à laisser telles quelles.

Celà, malgré le fait que je ne veux qu'un seul WHERE par instruction MySQL.

Il y a les agrégats dans les where et les select.

Dans les select, je vais laisser les fonctions d'origine MySQL.

Et dans les where aussi, puisque je parserai ces clauses where.

D'autre part, ( excusez du peu ), je me suis aperçu que je n'ai jamais utilisé dans mon site, de clause JOIN pour les instructions MySQL multi-tables. Je les convertirai après la mise au point de l'interface.

Pour le développement de cette interface, je vais y aller progressivement, lentement, en essayant de résoudre les difficultés au fur à mesure.

Je vous tiendrai au courant.

Respectueusement.
 
WRInaute accro
Laravel c'est le framework dit "full stack"
Eloquent c'est l'ORM de Laravel, utilisable en standalone.
 
WRInaute accro
Bonjour

Merci beaucoup Spout . ;)

Ton aide m'a permis d'évaluer ce qui reste à faire.

Très amicalement et respectueusement.
 
Discussions similaires
Haut