Merci pour la fonction ; je vais essayer de l'utiliser et je te dis.
Version imprimable
Merci pour la fonction ; je vais essayer de l'utiliser et je te dis.
Bonsoir,
je ne pense pas utiliser la fonction comme il faut car le BOM n'est pas retiré. Bien sûr, cette fonction est disponible dans PhpStorm (File -> Remove BOM) mais pour les utilisateurs autres que moi, ça va pas.
Voici comment je l'utilise :
J'ai mis cette fonction dans la classe CSVFile_model (j'ai laissé les extensions que tu n'aimes pas !) puis dans le fichier controller_controller.php (qui n'est pas une classe et d'ailleurs, y en a pas dans le répertoire controller) :
Il est où le bug ?Code:
1
2
3
4
5
6
7
8
9
10
11
12 $upload_dir = 'csv'; $name = $_SESSION['name']??NULL; $filepath = DIR_ROOT .$upload_dir.DIRECTORY_SEPARATOR.$name; $CSV_file_check1=new CSVFile_model(); if (isset($filepath)) { $ret = $CSV_file_check1->checkFile($filepath); // to check if it is well a CSV file $CSV_file_check1->removeUTF8BomFile($filepath);// remove the BOM }
salut,
ce n'est pas que j'aime pas, chacun fait comme bon lui semble. Je donne des conseils d'après une certaine logique et expérience, tu restes libre de faire comme tu veux.
Mais te retrouver avec des trucs pareils : model\CSVFile_model ou encore controller_controller.php, à mes yeux c'est du grand n'importe quoi, ça pique !
Le code informatique est un langage et comme tout langage, plus on est précis et mieux c'est. Dans la vie courante, tu n'utilises pas des pléonasmes à tout bout de champ, ben dans le code informatique, c'est pareil. C'est du bon sens. Au quotidien, tu t'efforces d'employer le mot juste pour exprimer un point de vue, une sensation... afin que la compréhension soit la plus précise possible. Ben dans la programmation c'est exactement pareil. Il faut apporter le plus grand soin au choix des noms des variables, fonctions, classes et autres méthodes. Il en va de la facilité de compréhension du code en un simple coup d’œil.
Récemment, dans un autre fil, j'avais déjà abordé ce point avec un étudiant. Je disais que quand on manipule PDO, la variable ne doit pas s'appeler autrement que $pdo, quand on manipule un PDOStatement, la variable doit s'appeler $stmt, etc. Comme ça en un coup d’œil, tu sais parfaitement ce qui se passe, tu saisis le déroulé du code, sa logique, etc... Et c'est valable aussi pour ceux qui aident et doivent relire le code...
Et comme toi tu as déjà une tendance naturelle à te perdre dans la logique de tes codes, le fait d'essayer d'avoir des marqueurs précis (un nommage léger mais précis) est d'après un moi un gros plus. Ça facilite la lecture et la reprise du code après une période plus ou moins longue.
On va clore cet aparté et passer à ton code :
regarde ce que tu as fait :
À ton avis c'est logique le isset() ?Code:
1
2
3
4
5
6 $upload_dir = 'csv'; $filepath = DIR_ROOT .$upload_dir.DIRECTORY_SEPARATOR.$name; if (isset($filepath)) { // etc }
ensuite :
Je suppose que quand tu écris "to check if it is well a CSV file" ça veut dire que tu fais une tentative de parsage du csv.Code:
1
2
3 $ret = $CSV_file_check1->checkFile($filepath); // to check if it is well a CSV file $CSV_file_check1->removeUTF8BomFile($filepath);// remove the BOM
Or tu sais que ta fonction de parsage de CSV n'est pas compatible avec un CSV contenant un BOM (Byte Order Mask) et toi tu fais dans l'ordre :
1. Tu tentes de parser le CSV (avec ou sans le BOM)
2. Tu supprimes du fichier l'éventuel BOM
Je te laisse conclure mais bon, faut que tu sois plus rigoureux. Je ne sais pas comment tu peux l'être plus, fais toi un schéma global, dessine, parles en avec ta femme mais trouve un truc :lol:
Non, pour le test du type du fichier, je ne tente pas un parsage.
checkfile() :
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 public function checkFile($name) { // contrôle du fichier transmis if (isset($name)) { $info = pathinfo($name, PATHINFO_EXTENSION); if ($info !== 'csv') { $body = new PhpEcho(); $body->setCode('<p>This file is not a .csv file</p>'); echo new PhpEcho([DIR_ROOT, 'view Layout.php'], ['body' => $body]); return(FALSE); } else { $body = new PhpEcho('', ['msg_end' => $name." is uploaded, start processing."]); $body->setCode('<p>'.$body('msg_end').'<br></p>'); echo new PhpEcho([DIR_ROOT, 'view Layout.php'], ['body' => $body]); return(TRUE); } }
ok par rapport à mon message précédent, on touche du doigt le fond du problème :
regarde ton code :
et vois maintenant mon interprétation (enfin devinette) tellement c'est pas clair. J'ai carrément tapé à côté !!!Code:$ret = $CSV_file_check1->checkFile($filepath); // to check if it is well a CSV file
Ta fonction, tu l'aurais nommée : checkFileExtension($filepath) que même le commentaire bateau à côté n'était plus nécessaire.
J'espère que tu comprends où je veux t'amener. La précision, toujours la précision.
Passe moi un tes fichier csv parce que chez moi j'ai essayé sur un fichier texte d'exemple qui ne contenait que "abcdef" encodé avec le BOM
Voici le fichier original en hexa :
Pièce jointe 556897
et voici le fichier en sortie de ma fonction removeBOM() :
Pièce jointe 556901
Donc à priori, ma fonction fait le taf.
Bonjour rawsrc,
voici un extrait de fichier csv avec bom
J'ai simplifié le contenu et renommé les noms de famille.Citation:
SESAID;ALTERNATEIDS;PLATFORM;PLATFORMOWNER;PUBLISHER;APPLICATIONNAME;HFMCODE;CURRENTLYACTIVE;ACTIVATEDATE;DEACTIVATEDATE;GROUPNAME;OFFICECOUNTRY;FIRSTNAME;LASTNAME;COMPANYNAME;JOBCODE;REPORTINGENTITY;BUSINESSUNITNAME;LOCATION;INTERNETADDRESS;MANAGERFIRSTNAME;MANAGERLASTNAME;MANAGEREMAIL SESA100008;(none);Software Engineering;SESA69723;CollabNet;TeamForge_Full;;TRUE;Jul 26, 2018;;;France;Bon;Jean;Schneider Electric France;TYS5;;Industry Business;CARROS HORIZON;bon.jean@schneider-electric.com;Marc;Bidule;marc.bidule@schneider-electric.com
Sinon, pour te joindre le fichier lui-même, le forum n'accepte te pas : "fichier non valide" ; je ne peux que par mail. Peux-tu me donner ton adresse mail par MP ? T'attends pas à un temps de réponse rapide car je vais manger..
Pour revenir au nommage précis des fonctions (et des variables), j'ai bien noté et renommé ma fonction : checkFileExtension.
Et pour le nommage des fichiers, du style CSVFile_model, l'utilité que j'y vois, c'est que dès que je vois le nom du fichier (ouvert dans un IDE), je sais dans quel répertoire, il est stocké. En ne rajoutant pas l'extension _model, ça serait impossible...
Salut rawsrc,
tu viens de me dire par mail que pour toi, ça marche. Comme je n'arrive pas à le faire marcher, voici mon code :
La méthode debug est un var_dump amélioré de $filepath et ça afficheCode:
1
2
3 $CSV_file_check1->removeUTF8BomFile($filepath);// remove the BOM UtilFct_controller::debug("controller 32 filepath",$filepath);
Le fichier csv n'est pas modifié, donc j'utilise mal ta méthode : comment faudrait faire ?Citation:
controller 32 filepath
string(51) "C:\xampp\htdocs\www\ticket2\csv\csv_pour_rawsrc.csv"
Tous comptes faits, comme tu l'as tout de suite vu quand tu es venu sur mon PC, le problème ne venait que de l'upload, mal fait. J'en ai ch... pour le mettre au point, mais une fois fait, ton script a immédiatement marché. :D:D
Je réfléchis à la classe CSVAnalyse que tu me proposais dans le post #4 (y a 10 jours !). On reçoit un tableau avec les colonnes nommées et il faut construire le SQL. Qu'on fasse un update ou un create, la mise en forme des données est la même. Par contre, celle-ci diffère pour chaque donnée. Ce que je pense faire, c'est écrire un code de ce genre :
Le tableau reçu a 2 dimensions, chaque ligne contenant un enregistrement complet venant du CSV, d'où les 2 foreach. Est-ce une bonne méthode ?Code:
1
2
3
4
5
6
7
8
9
10
11
12
13 foreach($tab_CSV as $data_tab_CSV) { foreach ($data_tab_CSV as $key => $value_enreg_CSV) switch ($key) { case "data1" : //code PHP to update this data break; case "data2" : //code PHP to update this data break; etc } }
c'est moyen...
il faut te poser la question qu'est ce qui va faire que tu vas opter pour un UPDATE ou un INSERT ?
Est ce que les données du CSV doivent être typées, si oui lesquelles...
Le typage dépend de la source ou plusieurs sources injectent leur données dans la même table... etc.
que ça soit moyen ne me surprend pas : je partage cet avis, mais pas capable de produire du non-moyen...
En fait, les CSV peuvent avoir 2 origines mais vont alimenter 5 tables (en fait 2 tables dont certaines colonnes sont des clés vers une des 3 autres tables).
Faire un INSERT ou un UPDATE : en fait, les 2 tables principales comportent une colonne qui est un identifiant. Donc si l'identifiant lu dans le CSV est déjà présent en BDD, UPDATE et sinon, INSERT .
Oui, certaines données du CSV seront typées : il y a des entiers (en fait des chaînes de caractères dont je vais extraire que le nombre), des booléens et des dates.
Mais ayant dit tout ça, je suis toujours pas capable de faire du non-moyen...
en fonction des relations d'intégrité qui pourraient exister ou pas entre tes tables tu peux joindre les INSERT et UPDATE en une seule requête REPLACE.
Si le fonctionnement de REPLACE est problématique du fait des relations (REPLACE réalise deux actions quand une clé primaire existe déjà : DELETE suivi de INSERT), tu peux faire un SQL dans le genre INSERT INTO ... VALUES (...) ON DUPLICATE KEY UPDATE ...
OK, je regarderai ça et avant de coder quoi que ce soit, je testerai dans phpMyadmin.
Mais quel que soient les requêtes SQL que je fais, il y a d'abord une mise en forme des données à faire. As-tu une autre méthode à me proposer que celle (moyenne) que j'ai exposée dans le post #49 ?
Laurent, je ne vois pas trop quoi te proposer, vu que je ne connais pas du tout les données.
Essaie de pondre quelque chose de ton côté et on verra après si c'est améliorable.
Une devise qui me va bien : qui va piano, va sano :mouarf:
Je viens de finir la mise au point de la partie la plus simple ( !) de la classe CSVAnalyse. Cette classe a pour rôle, recevant un tableau à 2 dimensions avec toutes les données d’un fichier CSV et les colonnes étant nommées, de générer les requêtes SQL pour la mise en BDD des données.
L’élaboration des requêtes SQL m’a pris du temps, mais j’ai pu vérifier dans phpMyAdmin qu’elles fonctionnent.
En résumé :
- Une classe d’upload : CSVUpload_view (oui j’ai gardé les extensions dans les noms des fichiers (et donc des classes))
- On teste si c’est bien un fichier CSV
- On appelle ta fonction rawsrc pour enlever le BOM
- Si pas d’erreur, on parse le fichier : génération d’un fichier à 2 dimensions (une ligne par enregistrement du fichier CSV et une colonne par donnée (cette colonne est nommée)
- La classe CSVAnalyse.
Pour éviter un long discours, voici le code commenté du contrôleur qui appelle la classe, de la classe elle-même puis la DDL des tables SQL.
Le contrôleur :
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 $CSV_file1=new CSVUpload_view(); // upload of the file $CSV_file1->csv_upload(); $upload_dir = 'csv_upload'; $name = $_SESSION['name']??NULL; $filepath = DIR_ROOT .$upload_dir.DIRECTORY_SEPARATOR.$name; $CSV_file_check1=new CSVFile_model(); $csv_parser = new CSVParser_model(); $ret = $CSV_file_check1->checkFileExtension($filepath); // to check if it is well a CSV file $CSV_file_check1->removeUTF8BomFile($filepath);// remove the BOM if ((isset($ret)) && $ret) { $data_CSV=$csv_parser->parse($filepath,4096); $errors=$csv_parser->errors(); if (!empty($errors)) echo implode($errors)."<br/>"; else { $csv_analyser = new CSVAnalyser_model(); $tab_SQL=$csv_analyser->build_SQL($csv_parser, $data_CSV);//generation of SQL requests } }
La classe CSVAnalyse :
Code:
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 <?php namespace model; use model\CSVFile_model; use model\Validator_model; use controller\UtilFct_controller; /** * Class CSVAnalyser_model * @package model */ class CSVAnalyser_model { /** * @param CSVParser $ref_CSV_Parser * @param $tab_CSV * * @return array */ public function build_SQL(CSVParser_model $ref_CSV_Parser,$tab_CSV):array { $sql_to_execute=[]; $ref_CSVFile=new CSVFile_model(); $CSV_type=$ref_CSV_Parser->CSVType(); $CSV_type_authorized=['tickets','licenses']; if (in_array($CSV_type,$CSV_type_authorized)) { $req_for_ticket_or_license = self::buildSqlForTicket_or_license($tab_CSV, $CSV_type); } return($req_for_ticket_or_license); } // end of fct build_SQL private function buildSqlForTicket_or_license($tab_CSV,$CSV_type) { $req_sql_to_execute=[]; $nb_line=0; $arg_for_sql=[]; /* The SQL requests are independant of the data, so they are created only once and after, there is a loop (a foreach) in which one takes into account each line of the CSV and affect the data of the SQL requests. One creates the array $arg_for_sql with these data, which is returned. */ switch($CSV_type) { case 'licenses' : /*INSERT IGNORE means that one insert only if the primary key or any unique key are not present in the bdd (if it's the case, one does nothing) PK = application_key unique key=application_name */ $req_sql_to_execute[] = "INSERT IGNORE INTO `application` (application_name,platform,platform_owner,publisher) VALUES(id_appli_name,:platform,:platformowner,:publisher)"; /* with this request, INSERT if id_SESA (PK) isn't present, elsewhere UPDATE * application_key=FK on table application (on PK of this table) * for application_key column, the value is the result of a SELECT request. */ $req_sql_to_execute[]="INSERT INTO `license` (id_SESA,application_key,currently_active,activate_date,deactivate_date) VALUES(:id_SESA, (SELECT application_key from `application` where (application_name=:appli_name)), :currently_active,:activate_date,:deactivate_date) ON DUPLICATE KEY UPDATE application_key=(SELECT application_key from `application` where (application_name=:appli_name)), currently_active=:currently_active,activate_date=:activate_date,deactivate_date=:deactivate_date"; break; case 'tickets':echo "this a ticket<br/>"; break; } foreach($tab_CSV as $data_tab_CSV) { switch($CSV_type) { case 'licenses' : $arg_for_sql[$nb_line]=[['id_appli_name'=>$data_tab_CSV['APPLICATIONNAME']] , [':platform'=>$data_tab_CSV['PLATFORM']] , [':platformowner'=>$data_tab_CSV['PLATFORMOWNER']] , [':publisher'=>$data_tab_CSV['PUBLISHER']]]; $arg_for_sql[$nb_line++]=[[':id_SESA'=>$data_tab_CSV['SESAID']] , [':appli_name'=>$data_tab_CSV['APPLICATIONNAME']] , [':currently_active'=>$data_tab_CSV['CURRENTLYACTIVE']] , [':activate_date'=> Validator_model::SQLFormatDate($data_tab_CSV['ACTIVATEDATE'])] , [':deactivate_date'=> Validator_model::SQLFormatDate($data_tab_CSV['DEACTIVATEDATE'])] ]; break; case 'tickets':echo "this a ticket<br/>"; break; } } return ([$req_sql_to_execute,$arg_for_sql]); } } // end of class
DDL de la table license :
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 DROP TABLE IF EXISTS `license`; CREATE TABLE `license` ( `id_SESA` int(6) NOT NULL, `application_key` smallint(2) DEFAULT NULL, `currently_active` tinyint(1) DEFAULT NULL, `activate_date` date DEFAULT NULL, `deactivate_date` date DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; ALTER TABLE `license` ADD PRIMARY KEY (`id_SESA`), ADD KEY `fk_application` (`application_key`); ALTER TABLE `license` MODIFY `id_SESA` int(6) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=12347; ALTER TABLE `license` ADD CONSTRAINT `fk_application` FOREIGN KEY (`application_key`) REFERENCES `application` (`application_key`) ON DELETE SET NULL ON UPDATE SET NULL; COMMIT;
DDL de la table application :
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 CREATE TABLE `application` ( `application_key` smallint(2) NOT NULL, `application_name` varchar(30) COLLATE utf8_bin NOT NULL DEFAULT '', `platform` varchar(30) COLLATE utf8_bin DEFAULT NULL, `platform_owner` varchar(30) COLLATE utf8_bin DEFAULT NULL, `publisher` varchar(30) COLLATE utf8_bin DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; ALTER TABLE `application` ADD PRIMARY KEY (`application_key`), ADD UNIQUE KEY `UK_appli_name` (`application_name`); ALTER TABLE `application` MODIFY `application_key` smallint(2) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3; COMMIT;
Je viens de renommer une méthode et une propriété dans mon post précédent car c'était pas clair (les noms dataient de mes premiers essais et je ne les avais pas actualisés)
salut Laurent,
j'ai commencé à regarder ton code et c'est un peu incompréhensible.
Je ne comprends pas du tout le code de ton contrôleur.
Il appelle une vue pour télécharger un fichier, puis s'occupe de gérer le fichier téléchargé puis retire le BOM (si nécessaire) puis lance un parser puis lance l'analyseur et après avoir écrit en base de données, il va se coucher parce qu'il est claqué !
Non mais, tu te rends comptes de tout ce qu'il fait ? Autant coder toute ton application avec un seul et unique script. Comme ça tu gagneras du temps (quoique) et surtout tu n'utiliseras pas trop d'espace disque.
OK, le "diviser pour mieux régner", c'est raté, on dirait. Néanmoins, il n'y a pas 36 actions utilisateur : un upload et derrière, la mise en BDD ; comme cette dernière est systématique après l'upload, ça ne fait qu'une seule action. Par contre, ensuite, le contrôleur a plusieurs actions à lancer :
- vérifier le type du fichier téléchargé
- retirer éventuellement le BOM
- parser
- écrire en BDD
Si je découpe ça en autant de méthodes, ça serait bien ?
Et as-tu jeté un oeil à la classe CSVAnalyse ?
Hello, j'ai tenté le découpage que je pensais faire. Je pense que c'est pas top, mais je ne sais pas faire mieux. Voilà où j'en suis :
Tout d’abord, on lance startController.php :
Code:
1
2
3
4
5
6
7
8
9 <?php use controller\controller_controller; use view\DispMenu_view; $menu = new DispMenu_view(); $menu->dispMenu(); $controller=new controller_controller; $controller->controllerController();
Celui-ci lance une méthode du contrôleur :
controller_controller.phpLe reste est inchangé.Code:
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 <?php declare(strict_types=1); namespace controller; use view\CSVUpload_view; use model\CSVFile_model; use model\CSVParser_model; use model\CSVAnalyser_model; class controller_controller { private $csv_parser; public function controllerController () { self::CVSUpload(); $upload_dir = 'csv_upload'; $name = $_SESSION['name'] ?? NULL; $filepath = DIR_ROOT . $upload_dir . DIRECTORY_SEPARATOR . $name; $ret=self::manageFile($filepath); if (!$ret) exit; $data_CSV=self::parse($filepath); $errors = $this->csv_parser->errors(); if (!empty($errors)) { echo implode($errors) . "<br/>"; } else { self::analyse($data_CSV); } } private function CVSUpload() { $CSV_file1 = new CSVUpload_view(); $CSV_file1->csv_upload(); } private function manageFile($filepath):bool { $CSV_file_check1 = new CSVFile_model(); $ret = $CSV_file_check1->checkFileExtension($filepath); // to check if it is well a CSV file if ($ret) $CSV_file_check1->removeUTF8BomFile($filepath);// remove the BOM return ($ret); } private function parse($filepath):array { $this->csv_parser = new CSVParser_model(); $data_CSV = $this->csv_parser->parse($filepath, 4096); return($data_CSV??[]); } private function analyse($data_CSV):array { $csv_analyser = new CSVAnalyser_model(); $tab_SQL = $csv_analyser->build_SQL($this->csv_parser, $data_CSV); return($tab_SQL); } }
bon Laurent, il s'agirait que tu suives un peu mieux les conseils qu'on te donne.
Arrête-toi et regarde :
Euh, t'es sûr qu'il ne te manque pas un p'tit controller quelque part ?Code:
1
2
3
4 use controller\controller_controller; $controller=new controller_controller; $controller->controllerController();
Allez parce que c'est toi :
Je me pose quand même la question si t'es certain, absolument certain de manipuler un controller, parce qu'on pourrait avoir un léger doute...Code:
1
2
3
4 use controller\controller\controller_controller; $controller_controller = new controller_controller; $controller_controller->controllerController();
Combien de fois je t'ai rabâché de ne pas utiliser ce genre de nommage, t'es testard dans ton genre ! Tu vois le ridicule du code, ou pas encore ?
Est-ce que dans mes codes sur mon blog, j'ai procédé une fois ainsi ? Jamais !
La nature de l'objet est déduite de son nom pleinement qualifié et de rien d'autre.
Quand tu astu sais que dans ce cas FormUpload est un objet View car son nom pleinement qualifié est Project\View\FormUpload.Code:
1
2
3 namespace Project\View; class FormUpload { }
Maintenant quand tu astu sais que dans ce cas FormUpload est un objet Controller car son nom pleinement qualifié est Project\Controller\FormUpload.Code:
1
2
3 namespace Project\Controller; class FormUpload { }
Et là tu observes et déduis immédiatement, tiens est-ce que le contrôleur FormUpload ne piloterait-il pas l'affichage du formulaire FormUpload ?
Même après un bon mois de confinement, la compréhension reste naturelle, pas besoin de se faire des nœuds au cerveau, bien que toi tu sembles adorer ça.
Donc comme tu code en MVC, le MVC doit être respecté.
Un contrôleur qui ne s'occupe que d'afficher le formulaire d'upload du fichier
Un contrôleur qui ne s'occupe que de traiter le fichier envoyé : vérification du type, retrait du BOM
Un contrôleur qui ne s'occupe que de l'analyse du fichier basé sur un fichier du modèle en charge de l'analyse du fichier.
Tu peux très bien dans ton interface, renvoyer à l'utilisateur une information concernant la validité du fichier qu'il vient d'envoyer. Et n'afficher un bouton Analyser que quand cela est nécessaire.
OK pour le nommage. J'avais pas vu qu'avec le nom pleinement qualifié de l'objet, on avait l'info...OK aussi pour faire plusieurs contrôleurs, plutôt qu'une classe unique avec plusieurs méthodes.
Je ne pourrai m'en occuper qu'en fin d'après-midi, et du renommage, il vaut mieux le faire d'une traite, sinon, on oublie des trucs et on passe un temps fou à trouver. Quand ça sera fait, je reviendrai.
Je reviens !! Oui, le renommage est fait (depuis 2 jours). Comme je l'ai dit dans une autre discussion, j'avais un souci de découpage en plusieurs classes. Avant d'aller plus loin, je souhaite valider ce point-là.
Donc voici le code de mon contrôleur :
L'idée est qu'il appelle 2 contrôleurs (upload puis vérification de l'extension du fichier) et si tout va bien, on a le lien html pour lancer l'analyse. (l'affichage de ce lien se fait via PhpEcho). Je ne donne volontairement aucun code supplémentaire car je voudrais déjà valider ce principe. Que pensez-vous de ça ?Code:
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 <?php declare(strict_types=1); namespace controller; use vendor\PhpEcho\PhpEcho; use controller\dispCSVUpload; use controller\manageFile; class controller { public function controller() { $ret=false; $_SESSION=""; $disp_form_upload = new dispCSVUpload(); //class in controller $disp_form_upload->dispFormUpload(); $filepath = $_SESSION['filepath'] ?? NULL; $manage_file = new manageFile(); //class in controller $ret=$manage_file->manageFile($filepath); if (!$ret) { $body = new PhpEcho(); $body->setCode('<p>This file is not a .csv file</p>'); echo new PhpEcho([DIR_ROOT, 'view Layout.php'], ['body' => $body]); } else { echo new PhpEcho([DIR_ROOT, 'view linkAnalyze.php']); } } }
salut Laurent,
je n'arrive pas à comprendre pourquoi tu n'arrives pas à diviser ton projet en blocs simples, vraiment je ne vois pas de raison.
Voici comment ton projet devrait s'articuler :
Et si tu décides que dès qu'un fichier valide téléversé est envoyé à l'analyseur tu peux même raccourcir un peu ce schéma en :www |--controller | |--Home | |--FormUpload | |--FormUploadSubmit | |--Analyze |--model | |--CSVParser | |--CSVAnalyze |--view | |--Home | |--FormUpload | |--AnalyzeResult |--index.php
Et par pitié arrête ton nommage foireux : namesapce controller; class controller; function controller() ce qui donne : \controller\controller->controller(). Heureusement que tu dis avoir repris le nommage, heureusement !!! :aie: :aie:www |--controller | |--Home | |--FormUpload | |--FormUploadSubmitAndAnalyze |--model | |--CSVParser | |--CSVAnalyze |--view | |--Home | |--FormUpload | |--AnalyzeResult |--index.php
Voici un prototype de nommage adapté à ta manière de coder :
ou encoreCode:
1
2
3
4
5
6
7
8
9
10
11 <?php declare(strict_types=1); namespace controller; class FormUpload { public function invoke() { // ... } }
Code:
1
2
3
4
5
6
7
8
9
10
11 <?php declare(strict_types=1); namespace controller; class FormUploadSubmitAndAnalyze { public function invoke() { // ... } }
Bonsoir,
En fait, je crois que je n’étais pas loin de ton approche. J’ai renommé les classes et méthodes et légèrement modifié mon code pour être le proche possible de ton approche.
Du coup, voici mon contrôleur (home.php) :
Validons déjà cela avant de détailler les classes.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 <?php declare(strict_types=1); namespace controller; use vendor\PhpEcho\PhpEcho; use controller\FormUpload; use controller\FormUploadSubmit; use controller\Analyze; class home { public function invoke() { $form_upload = new FormUpload(); //class in controller $filepath=$form_upload->dispFormUpload(); // $filepath is the complete path of the file $manage_file = new FormUploadSubmit(); //class in controller $ret=$manage_file->invoke($filepath); // check if extension's file is csv $analyze=new Analyze(); $analyze->invoke($ret); // analyze only if $ret is true } }
je ne comprends toujours pas ce que tu fais.
Relis-toi : un contrôleur en charge de gérer le traitement de la page d'accueil qui appelle et exécute tous les contrôleurs de ton application. Non mais, WTF ???
Pourtant, c'est pas faute d'avoir déjà abordé plusieurs fois avec toi les points relatifs au découpage de ton code en blocs simples.
C'est la dernière fois, après je lâche l'affaire, tu te débrouilleras sans moi.
Je pars sur ce découpage :
Tu décides de rediriger absolument toutes les url vers ton script index.php (soit en dur soit en utilisant le mécanisme de redirection de ton serveur web). Donc toutes les url vont pointer vers index.php et tu décides que chaque URL devra avoir un paramètre action pour être compatible avec ton système de routage : donc toutes les url devront être de la forme https://www.tonsite.com/index.php?action=XXXX et si l'action ne correspond à rien dans ton application, tu renvoies sur la page d’accueil.www |--controller | |--Home | |--FormUpload | |--FormUploadSubmit |--model | |--CSVParser | |--CSVAnalyze |--view | |--Layout | |--Home | |--FormUpload |--index.php
1. tu codes le layout général de ta page : View\Layout,
2. tu codes le bloc vue Accueil (variable 'body' dans le layout) : View\Home,
3. tu codes le bloc vue Envoi de fichier (variable 'body' dans le layout) : View\FormUpload,
4. le routage : tu décides les actions pour lesquelles il existe un traitement spécifique :
- action=form => affichage du formulaire d'upload (contrôleur controller\FormUpload)
- action=formsubmit => soumission et traitement de l'upload (contrôleur controller\FormUploadSubmit)
- pour toutes les autres actions, tu renvoies la page d'accueil
5. tu codes le contrôleur controller\Home qui ne fait qu'afficher la page d'accueil :
6. tu codes le contrôleur controller\FormUpload qui ne fait qu'afficher le formulaire d'upload :Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 <?php declare(strict_types=1); namespace controller; use vendor\PhpEcho\PhpEcho; class Home { public function invoke() { $page = new PhpEcho([DIR_ROOT, 'view Layout.php']); $page->addChild('body', [DIR_ROOT, 'view Home.php']); echo $page; } }
Attention : l'url de soumission du fichier dans le code de la vue doit être https://www.tonsite.com/index.php?action=formsubmit, elle doit absolument correspondre à ce qui a été décidé au niveau du routage.
7. tu codes le contrôleur controller\FormUploadSubmit qui se charge de la soumission et du traitement du fichier uploadéCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 <?php declare(strict_types=1); namespace controller; use vendor\PhpEcho\PhpEcho; class FormUpload { public function invoke() { $page = new PhpEcho([DIR_ROOT, 'view Layout.php']); $page->addChild('body', [DIR_ROOT, 'view FormUpload.php']); echo $page; } }
J'ai fait au plus simple, si tout se déroule sans accroc, je renvoie la page du formulaire d'upload avec juste un message 'Fichier importé avec succès'Je pense que tu peux regrouper avantageusement tous les traitements au sein d'un seul fichier modèle CSVImport.phpCode:
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 <?php declare(strict_types=1); namespace controller; use vendor\PhpEcho\PhpEcho; use model\CSVParser; use model\CSVAnalyze; class FormUploadSubmit { public function invoke() { // ici tu gères le fichier uploadé avec $_FILES // .... $page = function(string $msg): PhpEcho { // on renvoie le formulaire d'upload en indiquant un message $page = new PhpEcho([DIR_ROOT, 'view Layout.php']); $page->addChild('body', [DIR_ROOT, 'view FormUpload.php'], ['msg' => $msg]); return $page; }; // tu vérifies la validité, tu retires l'éventuel BOM $parser = new CSVParser(); if ($parser->checkFile('path/to/the/uploaded/file') === false) { echo $page('Fichier non conforme'); exit; } // fichier conforme et sans BOM : on lance l'analyse $analyzer = new CSVAnalyze; if ($analyzer->parse('path/to/the/uploaded/file') === false) { echo $page('Contenu du fichier non conforme'); exit; } // fichier conforme, contenu conforme, on sauvegarde en base de données // je le ferais directement avec l'analyseur if ($analyzer->saveToDb()) { echo $page('Fichier importé avec succès'); } else { echo $page('Importation impossible'); } } }
Si avec ça, tu n'arrives toujours pas à comprendre, c'est que, soit je suis vraiment une brêle dans ma manière d'expliquer, soit c'est toi.
Pour PhpEcho, prends la dernière version sur mon dépôt Github : version 2.3.2
ainsi cette syntaxe te sera disponible :
Code:$page->addChild('body', [DIR_ROOT, 'view FormUpload.php']);
:merci:
J'ai des difficultés à utiliser la nouvelle version de PhpEcho. Pourrais-tu STP répondre au commentaire que j'ai fait dans ton blog ? https://www.developpez.net/forums/bl.../#comment13876
salut Laurent
tu as bien pris les deux fichiers : PhpEcho.php et stdHelpers.php ?
L'un ne va pas sans l'autre.
Pour le blog, je ne reçois aucune notification quand un commentaire est posté. J'ai essayé plein de trucs, mais rien à faire ça ne veut pas. Donc il est préférable de laisser tomber les commentaires dans le blog au profit du forum.
OK pour privilégier le forum.
Oui, j'ai bien pris les 2 fichiers. Voici ce que j'ai écrit en fin de matinée :
Citation:
Bonjour,
je tente d'utiliser cette nouvelle version. Tu m'as dit qu'il y a une nouvelle syntaxe (avec addChild). J'ai essayé d'adapter l'exemple d'utilisation de la version 1 avec cette syntaxe, mais c'est pas bon. De plus, dans ton billet, cette syntaxe n'est pas évoquée. Et mon essai n'est pas bon. Pourrais-tu le corriger ? (et compléter ton billet) :
login.php :
Code:
1
2
3
4
5
6 $page = new PhpEcho([DIR_ROOT, 'view Layout.php']); $page->addChild('body', [DIR_ROOT, 'view LoginForm.php'], [ 'url_submit' => '/index.php?page=loginsubmit', 'login' => 'rawsrc' ]); echo $page;
j'ai aussi modifié loginform.php :
plus de notation appel de fonction.Code:
1
2
3
4
5
6
7
8
9
10 <p>Veuillez vous identifier</p> <form method=post action="<?= URL_HOME.$this['url_submit'] ?>"> <label>Identifiant</label> <input type="text" name="login" value="<?= $this['login'] ?>"><br> <label>Mot de passe</label> <input type="password" name="pwd" value=""><br> <input type="submit" name="submit" value="SE CONNECTER"> </form> <br> <p style="display:<?= $this['show_error'] ?? 'none' ?>"><strong><?= $this['err_msg'] ?></strong></p>
Visiblement, le tableau passé ligne 3 et 4 de login.php n'est pas attendu. Comment faire ? (J'ai bien pris la version sur GitHub)
Ok essaie avec comme ça :
addChild() utilise comme point de départ, le répertoire courant, c'est pour cette raison que la notation tableau n'est plus permise. C'est une injection relative.Code:
1
2
3
4 $page->addChild('body', 'LoginForm.php', [ 'url_submit' => '/index.php?page=loginsubmit', 'login' => 'rawsrc' ]);
J'ai bien mis à jour la doc sur Github mais je devrais refaire une grosse mise à jour sur DVP pour coller aux nouveautés de la version 2.3.2
Apres dans PhpStorm, tu n'as qu'à faire CTRL+q pour avoir accès au PHPDoc de la fonction
C'est magique, ça marche :D
En fait, je n'ai pas regardé la doc dans GitHub ; je me suis contenté d'y télécharger les 2 fichiers.
Je vais passer à ton post d'hier...
CTRL+q sur le nom du fichier ; effectivement, y a bien une doc qui s'affiche, mais c'est très limité (nom du fichier, sa taille, son type, date de dernière modif et date de création)
Salut,
je m'y suis mis hier soir, et j'avais du mal à démarrer à tel point que j'avais commencé à écrire un post et il ne restait plus qu'à cliquer sur le bouton "envoyer" quand la lumière est venue !
Néanmoins, ce matin, nouvelle difficulté. La page d'accueil est très simple pour le moment ; elle n'affiche que this is the homepage. Par contre, mon idée est de factoriser l'affichage du menu sur chaque page et là, ça se complique. J'ai fait plusieurs essais infructueux.
- mettre le menu dans layout.php.
- appeler une méthode dans chaque page
en mettant ces 3 lignes en début de chaque page :
Code:
1
2
3
4 use view\DispMenu; $menu=new DispMenu(); $menu->dispMenu();
DispMenu.php :
et Menu.php (pas à jour) :Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 <?php namespace view; use vendor\PhpEcho\PhpEcho; class DispMenu { public function dispMenu() { $page = new PhpEcho([DIR_ROOT, 'view Layout.php']); $page->addChild('body', 'Menu.php', [ 'view' => 'View', 'controller' => 'Controller', 'select' => 'Select', 'array' => 'Array' ]); // on renvoie au navigateur la page assemblée echo $page; } } // end of class DispMenu ?>
Code:
1
2
3
4
5
6
7
8
9
10 <ul> <li><a href="#"><?= $this['view'] ?></a> <ul> <li><a href="?action=<?= $this['view'] ?>" target="_blank"><?= $this['select'] ?></a></li> <li><a href="?action=<?= $this['array'] ?>" target="_blank"><?= $this['array'] ?></a></li> </ul> </li> <li><a href="?action=<?= $this['controller'] ?>" target="_blank"><?= $this['controller'] ?></a></li> </ul>
Mais au lieu d'afficher le menu, ça afficheComment je peux faire ?Citation:
Fatal error: Cannot declare class rawsrc\PhpEcho\PhpEcho, because the name is already in use in C:\xampp\htdocs\www\ticket3\vendor\PhpEcho\PhpEcho.php on line 58
J'ai mis de côté le souci du menu et suis passé à la vue pour afficher le formulaire d'upload. Et nouvelle difficulté :
view/formupload.php :
comme l'application restera en local, je peux mettre l'action comme ça.Code:
1
2
3
4
5
6
7
8 <!-- form to upload a csv file from the hard disk --> <form action="<?php echo URL_LOCALHOST. DIRECTORY_SEPARATOR .'www'. DIRECTORY_SEPARATOR .'PROJECT_NAME'. DIRECTORY_SEPARATOR . 'index .php?action=formsubmit'; ?>" method="post" enctype="multipart/form-data"> <label for="file2"><b>File CSV</b></label> <input type="file" name="file2" id="file2"><br/> <input type="submit" name="submitfile2" value="CSV file download"><br/> </form>
Dans le routeur, j'ai : 'formsubmit'=>'view/home.php' (pour l'instant, le traitement du fichier n'est pas codé). Par contre, quand je clique sur le bouton de submit :et dans la barre d'adresse, il y a http://localhost/www/ticket3/view/%3Cbr%20/%3E%3Cb%3EWarning%3C/b%3E:%20%20Use%20of%20undefined%20constant%20URL_LOCALHOST%20-%20assumed%20'URL_LOCALHOST'%20(this%20will%20throw%20an%20Error%20in%20a%20future%20version%20of%20PHP)%20in%20%3Cb%3EC:/xampp/htdocs/www/ticket3/view/formUpload.php%3C/b%3E%20on%20line%20%3Cb%3E5%3C/b%3E%3Cbr%20/%3EURL_LOCALHOST/www/PROJECT_NAME/index.php?action=formsubmit et le code source de la page qui affiche le formulaire :Citation:
Accès interdit!
Vous n'avez pas le droit d'accéder à l'objet demandé. Soit celui-ci est protégé, soit il ne peut être lu par le serveur.
Si vous pensez qu'il s'agit d'une erreur du serveur, veuillez contacter le webmestre.
Donc il ne comprend pas la constante définie dans Config.php...Code:
1
2
3
4
5
6
7
8
9 <!-- form to upload a csv file from the hard disk --> <form action="<br /> <b>Warning</b>: Use of undefined constant URL_LOCALHOST - assumed 'URL_LOCALHOST' (this will throw an Error in a future version of PHP) in <b>C:\xampp\htdocs\www\ticket3\view\formUpload.php</b> on line <b>5</b><br /> URL_LOCALHOST\www\PROJECT_NAME\index .php?action=formsubmit" method="post" enctype="multipart/form-data"> <label for="file2"><b>File CSV</b></label> <input type="file" name="file2" id="file2"><br/> <input type="submit" name="submitfile2" value="CSV file download"><br/> </form>
salut Laurent,
on va reprendre le découpage des vues.
Tu dois raisonner en blocs vue, pages et layout et surtout penser simple.
l'arborescence des dossiers :
Le bloc Nav.phpwww |--view |--Block | |--Nav.php | |--FormUpload.php |--Home.php |--Layout.php
Le bloc FormUpload.phpCode:
1
2
3
4
5
6
7
8
9
10
11 <nav> <ul> <li><a href="<?= $this['view'] ?>"></a> <ul> <li><a href="?action=<?= $this['view'] ?>" target="_blank"><?= $this['select'] ?></a></li> <li><a href="?action=<?= $this['array'] ?>" target="_blank"><?= $this['array'] ?></a></li> </ul> </li> <li><a href="?action=<?= $this['controller'] ?>" target="_blank"><?= $this['controller'] ?></a></li> </ul> </nav>
Le Layout.phpCode:
1
2
3
4
5 <form action="?action=formsubmit" method="post" enctype="multipart/form-data"> <label for="file2"><b>File CSV</b></label> <input type="file" name="file2" id="file2"><br/> <input type="submit" name="submitfile2" value="CSV file upload"><br/> </form>
Et la page Home.phpCode:
1
2
3
4
5
6
7
8
9
10 <!DOCTYPE html> <html> <head> <base href="<?= $this->raw('base_href') ?>"> </head> <body> <?= $this['nav'] ?> <?= $this['body'] ?> </body> </html>
Reste simple dans le déroulé de la logique.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 <?php use vendor\PhpEcho\PhpEcho; $layout = new PhpEcho([DIR_ROOT, 'view Layout.php'], ['base_href' => URL_LOCALHOST]); $layout->addChild('nav', 'Block Nav.php', [ 'view' => 'View', 'controller' => 'Controller', 'select' => 'Select', 'array' => 'Array' ]); $layout->addChild('body', 'Block FormUpload.php'); return $layout;
Le but de PhpEcho c'est de te permettre de découper toutes tes vues en blocs simples (autonomes) que tu peux agencer ensuite selon tes besoins.
Penser simple qu'il a dit le monsieur...D'ailleurs, ce mot revient 3 fois dans ton dernier post. Cela dit, ton code ne l'est pas assez pour moi. Alors, je me suis dit : on va le mettre dans PhpStorm et il sera plus facile de l'étudier. Je l'ai fait, puis ai exécuté le code pour voir et...Ce que j'ai le plus de mal à comprendre :Citation:
Fatal error: Cannot declare class rawsrc\PhpEcho\PhpEcho, because the name is already in use in C:\xampp\htdocs\www\ticket3\vendor\PhpEcho\PhpEcho.php on line 58
- l'appel de la méthode raw ligne 4 de layout.php
- le 2e argument passé à PhpEcho ligne 5 de Home.php
Mon prochain objectif : avoir un truc à montrer jeudi après-midi, avec au moins la lecture des CSV et l'idéal, leur mise en BDD.
salut,
comme je ne sais pas ce que t'utilises dans ton environnement, tu ne dois pas redéclarer deux fois la classe PhpEcho. En gros, tu ne dois faire qu'une seule et unique fois dans index.php include 'vendor/PhpEcho/PhpEcho'; et ensuite partout où t'as besoin de PhpEcho, juste faire use PhpEcho;.
Bref tu dois adapter mon code à ton environnement.
Dans Layout.php, tu as <base href="<?= $this->raw('base_href') ?>">, si tu regardes le fichier des helpers stdHelpers.php, tu verras la définition de $raw = function()... qui ne fait que retourner la valeur sans l'échapper. Comme tout est échappé par défaut dans PhpEcho, il faut utiliser cet helper pour dire au moteur que ce n'est pas la peine de l'échapper.
Si tu regarde le layout, il attend 3 paramètres pour fonctionner : 'base_href', 'nav' et 'body'. Donc quand on instancie le layout, on lui passe la valeur du premier paramètre, ensuite $layout->addChild('nav', ...) s'occupe de fournir un bloc PhpEcho en guise de valeur au paramètre 'nav' et $layout->addChild('body', ...) fait de même pour 'body'. C'est totalement modulaire.
Merci pour ta réponse.
Inspection faite, j'ai un seul include de PhpEcho. J'ai mis à la place include_once mais l'erreur reste. Je reprendrai la question demain matin...
Hello,
essayant de voir d'où vient ce message d'erreur, j'ai simplifié à outrance view/home.php echo "this is the homepage<br/>"; et l'erreur s'en va, mais si je rajoute une ligne :et ai aussi simplifié Layout.php :Code:
1
2
3
4
5
6
7 <?php use vendor\PhpEcho\PhpEcho; echo "this is the homepage<br/>"; //$layout = new PhpEcho([DIR_ROOT, 'view Layout.php'], ['base_href' => URL_LOCALHOST]); $layout = new PhpEcho([DIR_ROOT, 'view Layout.php']);
l'erreur revient. Pourquoi ?Code:
1
2
3
4
5
6
7
8
9
10 <!DOCTYPE html> <html> <head> <!-- <base href="<?= //$this->raw('base_href') ?>">--> </head> <body> <!--<?= //$this['nav'] ?>--> <?= $this['body'] ?> </body> </html>
t'as essayé avec juste use PhpEcho; ?