8796 sujets

Développement web côté serveur, CMS

Pages :
Bonjour à tous,

Étant plutot graphiste à la base je me suis pris de passion également pour ce qui est développement, notamment en orienté Objet.

J'ai accumulé pas mal de connaissances en ActionScript, Js et PHP, mais je suis face à un problème certainement classique.
N'ayant jamais suivi de formation dev (la vraie, la dure) j'aimerais savoir si des gens calés pourraient m'aiguiller sur une bonne marche à suivre en terme d'architecture des dossiers, et de structure pour avoir un MVC efficace et flexible au max.

En gros j'aimerais développer mon mini framework de la meilleure maniere qui soit.

Je sais que chacun a plus ou moins sa vision du MVC, mais il doit y avoir des règles de base que je ne connais pas...

Des conseils pour un noob ?? Smiley biggrin
Salut,

Je te conseil de regarder comment est fait Zend Framework, cela peut te donner une bonne idée d'une bonne structure MVC.

Tu peux d’ailleur utiliser des éléments du Framework, ils sont vraiment très bien.

Pour la logique d'organisation des fichiers, l'idée c'est de suivre la logique de l'autoload

Par exemple si tu organises tes fichiers dans le style de Zend:

function __autoload($class)
{
    $path = str_replace("_", "/", $class);
    require $path.".php";
}


L'idée c'est de faciliter l'appel des includes en organisant les fichiers en fonction de leur nom

Ensuite c'est mieux d' utiliser des noms "standard" comme controllers, views, lib, models, modules, plugins, etc pour les dossiers.

Un autre point c'est une bonne utilisation de l'include_path, les lignes suivantes par exemples recherche automatiquement les includes dans tous les plugins:


$pluginsDirs = glob('./plugins/*', GLOB_ONLYDIR);
foreach ($pluginsDirs as $dir){
     $pluginName = basename($dir);
     $incPaths[] = WEBSITE_DIR . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR.$pluginName;
}
set_include_path( get_include_path() . PATH_SEPARATOR . implode(PATH_SEPARATOR, $incPaths) );

Ça c'est super flexible Smiley biggrin ... et beaucoup plus rapide que plein de file_exists() pour rechercher tes fichiers.

Tu peux appliquer le même système pour rechercher les includes automatiquement dans le dossier modèle.

Voilà c'est quelque truc il y en a pleins d'autres...
Modifié par matmat (21 May 2010 - 04:17)
Merci beaucoup pour la réponse msieur,

J'ai bien assimilé la premiere, je vais devoir revoir mes nommages de fichiers !!
Je suivais les conseils certainement débiles d un prof, c'est a dire "maclasse-inc.php",
j'aurais du les nommer comme pour l'actionscript, qui lui oblige à nommer son fichier comme sa classe (il me semble).
Donc si je comprend bien, par l autoload, le serveur scanne les dossiers en recherche du nom de ma classe instanciée ? Il scanne donc tous les dossiers depuis la racine de mon site c'est bien ca?

Pour la seconde technique, je vais tester ca des que j'ai un moment ça à l'air super puissant bien que je ne comprenne pas toute la syntaxe. Ca a l air de meriter de se ronger un peu le crane dessus !

Puis je pensais Zend payant, je vais télécharger ca de ce pas !

Merci a toi matmat Smiley biggrin
Bonjour,
surement une tout autre approche qu'est la mienne, Un vrais MVC se fait
"a l'envers" ...

Deuc cas , mais "le même regard"

1) Tu as un projet a développer , tu en connais tout les contours ,
quoi de plus simple que de l'écrire de façon traditionnelle ? c'est toujours ce que je fais pour ma part.
Lorsque le projet est bien "ficelé" alors tu mesures de suite s'il mérites d'étre "encombré" par du OO voir même du MVC.
Si oui alors tu sépares les trois niveaux fondamentaux
SIC :
a écrit :
L'organisation globale d'une interface graphique est souvent délicate. L'architecture MVC ne résout pas tous les problèmes. Elle fournit souvent une première approche qui peut ensuite être adaptée. Elle offre aussi un cadre pour structurer une application.

Ce modèle d'architecture impose la séparation entre les données, la présentation et les traitements, ce qui donne trois parties fondamentales dans l'application finale : le modèle, la vue et le contrôleur


2) Une ancienne application, qui a étée mille fois "rafistolée"
Alors voila bien le cas ou le passage en MVC va faire mieux que la remise a plat par la réécriture.

Voila mon approche si cela te donnes une autre vision des choses Smiley eek
Modifié par Christele (21 May 2010 - 08:53)
JohnNada a écrit :
Donc si je comprend bien, par l autoload, le serveur scanne les dossiers en recherche du nom de ma classe instanciée ? Il scanne donc tous les dossiers depuis la racine de mon site c'est bien ca?


L'autoload scanne les dossiers défini par set_include_path(), (par défault include_path contient le dossier courant, à toi de rajouter les dossiers nécessaires, comme le dossier des librairies ou bien le dossier PEAR), en les recherchant selon leur noms, par exemple si tu as une classe Image, il va inclure Image.php.

Dans mon exemple j'utilise la logique d'organisation des fichiers de Zend qui remplace les "_" par les dossiers, para exemple si tu as une classe Image avec des extensions Gd et Imagick, tu auras a la racine Image.php puis dans un dossier Image les fichiers Gd.php et Imagick.php ensuite tu les appelle en faisant new Image_Gd() et tu n'as plus besoin d'utiliser require ou inclure quand tu as besoin d'une de tes classes.

JohnNada a écrit :
Puis je pensais Zend payant, je vais télécharger ca de ce pas !


Tu vas voir que si tu utilises toute l'application via la classe Zend_Application, c'est un peu lourd, c'est normal la classe charge un grande partie du framework, pour ma part j'utilise les composant de manière indépendante, para exemple Zend_Db est vraiment super, ou bien selon les projets, par exemple la semaine dernière j'ai utilisé Zend_Json, rien à dire c'est impeccable!

Christele a écrit :

Lorsque le projet est bien "ficelé" alors tu mesures de suite s'il mérites d'étre "encombré" par du OO voir même du MVC.


Je ne trouve pas que la programmation Objet ou bien le MVC encombre un projet bien au contraire!

Par exemple une requête avec Zend_Db:

$select = $db->select();
$select->form('pages',array('title','description','text'));
$select->order('date DESC');
$select->joinLeft('images','images.id_page=pages.id_page',array('src','title'=>'alt'));
$select->where('id_parent=?',$idParent);
$pages = $db->fetchAll($select);


Tu utilises PDO, donc c'est rapide, Zend_Db construit un requête sûre donc pas de prise de tète a les magick_quote ou autre mysql_real_escape_string, et en plus c'est quand même beaucoup plus lisible!, pour les update ou insert c'est toute aussi simple.

Par exemple quand je disais que j'ai utilisé Zend_Json, en fait la seul chose que j'ai fait c'est echo Zend_Json::encode($lines); Smiley lol , je te dit pas le gain de temps entre ça et faire en Json de façon "traditionnel", grâce a la technique de l'autoload, j'ai même pas a inclure quoi que se soit!

La ou je suis d'accord avec toi, c'est que le modèle MVC peut parfois alourdir la conception surtout dans la gestion des controlleurs, c'est un peu laborieux d'appeler Controller_Index() et de devoir faire des méthodes juste pour lancer un index Smiley biggol . Mais l'un n’empêche pas l'autre, tu peux toujours avoir des fichiers de base en programmation type "procédurale" qui appelle tes classes et tes modèles.
Modifié par matmat (21 May 2010 - 16:42)
Modérateur
matmat a écrit :


$select = $db->select();
$select->form('pages',array('title','description','text'));
$select->order('date DESC');
$select->joinLeft('images','images.id_page=pages.id_page',array('src','title'=>'alt'));
$select->where('id_parent=?',$idParent);
$pages = $db->fetchAll($select);



<troll date="vendredi">Pfff, en Coldfusion, sans framework particulier, les requêtes SQL sont 10 fois plus sexy que ça!</troll>

Smiley dehors
Tony Monast a écrit :

<troll date="vendredi">Pfff, en Coldfusion, sans framework particulier, les requêtes SQL sont 10 fois plus sexy que ça!</troll>


En coldfusion tu écris directement la requête non? ou bien il y a truc magique?
Parce qu’une requête avec join, order, et sélection des colonnes, c'est pas toujours très sexy.
Modérateur
En Coldfusion, ça ressemble à ça :


<cfquery name="qry_resultats" datasource="madatasource">
SELECT Champ1, Champ2, Champ3
FROM tblMaTable
WHERE ChampID = <cfqueryparam cfsqltype="cf_sql_integer" value="100">
ORDER BY Champ1, Champ2
</cfquery>


Ensuite, les enregistrements se retrouvent dans la variable qry_resultats qu'on peut très facilement parcourir.

Le cfqueryparam sert à accélérer les requêtes SQL et à se protéger contre l'injection SQL.

Perso je trouve ça plus sexy qu'une liste de fonctions auxquelles on passe des arrays et des paramètres.

Mais don't feed the troll! Smiley smile
C'est clair Coldfusion, c'est bien cool... mais c pas libre Smiley biggol non je rigole... Php c libre mais tu passe tellement de temps a bidouiller que tu pourrais t'acheter 10 licences coldfusion!

Plus sérieusement c'est vrai que c'est le problème de php, c'est pour ça que Zend qui essaye de normaliser un peu le truc et de garantir une qualité c'est plutôt bien.

Dans ta requête il manque le join... Smiley langue
Modifié par matmat (21 May 2010 - 17:30)
Modérateur
Coldfusion n'est pas libre en effet, mais il existe bluedragon qui utilise la même syntaxe que Coldfusion, le CFML. Pour le reste, je n'ai jamais utilisé BlueDragon, alors je ne peux pas affirmer si Coldfusion est supérieur ou pas.
Un design pattern ne résout pas tous les problèmes, c'est seulement un cadre pour être moins brouillon et ça ne dédouane pas d'une phase de conception béton.

Le standard actuel, c'est UML.
Il y a plein de tuto sur le web et de bons bouquins axés développement web.
Un autre volet indissociable, ce sont les tests.

Après ça dépend de ton niveau de connaissance.

php est suffisamment accessible pour que beaucoup d'entre nous se soient passés de l'apprentissage théorique des bases de l'algo, d'http, etc.

A un moment, il faut s'y coller si l'on veut pouvoir progresser tranquillement.

l'utilisation d'un bon framework (zend, symfony, etc) force les bonnes pratiques et met un peu d'ordre dans un langage tel que php.
a écrit :

$select = $db->select(); 
$select->form('pages',array('title','description','text')); 
$select->order('date DESC'); 
$select->joinLeft('images','images.id_page=pages.id_page',array('src','title'=>'alt')); 
$select->where('id_parent=?',$idParent); 
$pages = $db->fetchAll($select); 

Bof, je trouve pas cette forme forcément super. C'est compréhensible et clair mais .... j'aime mieux celle de mon mini-framework à moi :

$pages = Record::findAllBySQL('Page', '
select *
from Pages
where idParent = %d
order by date desc
', $idParent);

OU en encore plus simple :

$pages = Record::findAll('Page', array('idParent'=>$idParent, 'order'=>'date desc'));


Tout ça pour dire aussi qu'il n'y a pas qu'une seule façon de voir les choses ni qu'une seule façon de faire les choses correctement. Il existe autant de façon de programmer qu'il n'y a de programmeurs... C'est pour ça qu'il existe 36 frameworks différents qui font tous la même chose.
QuentinC a écrit :

Bof, je trouve pas cette forme forcément super. C'est compréhensible et clair mais .... j'aime mieux celle de mon mini-framework à moi :

$pages = Record::findAllBySQL('Page', '
select *
from Pages
where idParent = %d
order by date desc
', $idParent);

OU en encore plus simple :

$pages = Record::findAll('Page', array('idParent'=>$idParent, 'order'=>'date desc'));



Si j'ai pris un exemple compliqué c'est pour montrer les possibilités. Effectivement si c'est une requête simple tu peux faire directement:


$select = $db->select()
             ->form('pages')
             ->order('date DESC')
             ->where('id_parent=?',$idParent);
$pages = $db->fetchAll($select);


C'est marrant parce que dans vos exemples vous enlever le joinLeft qui est tout l’intérêt de l'exemple...
Modifié par matmat (26 May 2010 - 02:07)
Modérateur
matmat a écrit :
C'est marrant parce que dans vos exemples vous enlever le joinLeft qui est tout l’intérêt de l'exemple...


Un exemple avec un INNER JOIN. La beauté est que le code est esthétiquement bien, performant et il respecte parfaitement la syntaxe SQL. Le code peut être copier-coller plus facilement pour être utilisé dans la base de données ou dans une autre application, même si cette dernière n'est pas en Coldfusion. Il y a seulement le cfqueryparam à remplacer par une autre fonction anti-injection SQL. En utilisant un framework pour faire ses requêtes SQL, ça risque d'être lourd pour réutiliser son code dans un autre environnement.


<cfquery name="qry_resultats" datasource="madatasource">
SELECT tblTableA.Champ1, tblTableA.Champ2, tblTableB.ChampX
FROM tblTableA INNER JOIN tblTableB ON tblTableA.ChampID = tblTableB.LiaisonID
WHERE tblTableA.ChampID = <cfqueryparam cfsqltype="cf_sql_integer" value="100">
ORDER BY tblTableA.Champ1, tblTableA.Champ2
</cfquery>

Modifié par Tony Monast (26 May 2010 - 02:52)
a écrit :
C'est marrant parce que dans vos exemples vous enlever le joinLeft qui est tout l’intérêt de l'exemple...

Pour ma part c'est parce que je n'ai pas réellement compris ce qu'il faisait. J'ai l'impression qu'il manque soit une fonction de groupement genre group_conca et on explode le résultat après, ou alors soit si c'est pour récupérer un tableau d'objets séparé, je ne fais pas comme ça (de toute façon il y a obligatoirement deux requêtes en réalité dans ce cas, non ?)
QuentinC a écrit :

Pour ma part c'est parce que je n'ai pas réellement compris ce qu'il faisait. J'ai l'impression qu'il manque soit une fonction de groupement genre group_conca et on explode le résultat après, ou alors soit si c'est pour récupérer un tableau d'objets séparé, je ne fais pas comme ça (de toute façon il y a obligatoirement deux requêtes en réalité dans ce cas, non ?)

Je me sert souvent de left join ou inner join, précisément pour joindre Smiley biggol des informations de plusieurs tables, par exemple un table de page ou d'évenement avec un table de slugs, ou bien un table de commandes avec un table produit, mais c'est sûr qu'on peut très bien s'en passer et faire en deux requêtes sauf que c'est plus long, plus lourd et plus lent...

C'est un peu comme le faite de sélectionner des colonnes ou de tout sélectionner avec *. Et c'est là que le framework aide il permet de faire des choses complexes simplement.

Je veux pas non plus partir sur un gros débat du meilleur framework, juste dire que de mon point de vue après en avoir essayé plusieurs je trouve Zend très clair et très flexible.

Tony Monast a écrit :


Un exemple avec un INNER JOIN. La beauté est que le code est esthétiquement bien, performant et il respecte parfaitement la syntaxe SQL. Le code peut être copier-coller plus facilement pour être utilisé dans la base de données ou dans une autre application, même si cette dernière n'est pas en Coldfusion. Il y a seulement le cfqueryparam à remplacer par une autre fonction anti-injection SQL. En utilisant un framework pour faire ses requêtes SQL, ça risque d'être lourd pour réutiliser son code dans un autre environnement.


<cfquery name="qry_resultats" datasource="madatasource">
SELECT tblTableA.Champ1, tblTableA.Champ2, tblTableB.ChampX
FROM tblTableA INNER JOIN tblTableB ON tblTableA.ChampID = tblTableB.LiaisonID
WHERE tblTableA.ChampID = <cfqueryparam cfsqltype="cf_sql_integer" value="100">
ORDER BY tblTableA.Champ1, tblTableA.Champ2
</cfquery>


Je comprend le point de vue de laisser les requêtes "entières", et c'est vrai que "style" Coldfusion est clair, ceci dit en php, sans framework tu peux faire des choses dans le même style:

$select = $pdo->prepare("
SELECT tblTableA.Champ1, tblTableA.Champ2, tblTableB.ChampX
FROM tblTableA INNER JOIN tblTableB ON tblTableA.ChampID = tblTableB.LiaisonID
WHERE tblTableA.ChampID = ? ORDER BY tblTableA.Champ1, tblTableA.Champ2
");
$select->execute(array(100));
$results = $select->fetchAll();


C'est un style un peu diffèrent mais ce n'est pas non plus complètement barbare...

Après, faire ou non les requêtes avec un framework, encore un fois je trouve le code plus léger à la longue et plus facile a lire, on identifie plus facilement les colones, l'ordre les groupes, les "having" etc... un autre exemple c'est pour les insertions:

$pdo->insert('pages',array(
      'id_parent'=> Util::post('id_parent'),
      'title'=> Util::post('title'),
      'description'=> Util::post('description'),
      'text'=> Util::post('text'),
));


Plus de problème de sécurité, ni de quote...
Modifié par matmat (27 May 2010 - 04:16)
a écrit :
Je me sert souvent de left join ou inner join, précisément pour joindre des informations de plusieurs tables, par exemple un table de page ou d'évenement avec un table de slugs, ou bien un table de commandes avec un table produit,
mais c'est sûr qu'on peut très bien s'en passer et faire en deux requêtes sauf que c'est plus long, plus lourd et plus lent...

Oui, tout à fait, moi aussi. Sauf que là, je ne vois pas bien ce que fait ton exemple. Tu récupères une série de pages avec les images associées.... mais comme tu présentes les choses, j'ai l'impression que si une page a 100 images, alors tu reçois 100 lignes avec les mêmes attributs title, description et text. A mon sens, il manque donc un group by + un appel à une fonction de groupement.
A supposer que le texte d'un article fasse 5 Ko, alors il y aurait 500 Ko de transfert dont 495 inutiles, et cela multiplié par le nombre réel d'articles sélectionnés en tout ? Soit je suis un noob et je n'ai rien compris aux jointures, soit ZF le fait tout seul et rien dans cet exemple ne pouvait ne me le laisser supposer (car je ne connais pas ZF) et dans ce cas au temps pour moi, soit il y a vraiment quelque chose qui cloche.

a écrit :

$pdo->insert('pages',array( 
      'id_parent'=> Util::post('id_parent'), 
      'title'=> Util::post('title'), 
      'description'=> Util::post('description'), 
      'text'=> Util::post('text'), 
));

Ah, par contre ça c'est assez sympa comme notation. Juste une chose : pour lever le doute pour ceux qui lisent et qui ne sont pas forcément censé savoir, ce n'est pas une feature proposée nativement par PDO.

La correspondance chez moi serait :

$page = new Page(array(
'idParent' => post('idParent'),
'title' => post('title'),
'description' => post('description'),
'text' => post('text')
));
$page->save();

Modifié par QuentinC (27 May 2010 - 08:47)
QuentinC a écrit :

Oui, tout à fait, moi aussi. Sauf que là, je ne vois pas bien ce que fait ton exemple. Tu récupères une série de pages avec les images associées.... mais comme tu présentes les choses, j'ai l'impression que si une page a 100 images, alors tu reçois 100 lignes avec les mêmes attributs title, description et text. A mon sens, il manque donc un group by + un appel à une fonction de groupement.
A supposer que le texte d'un article fasse 5 Ko, alors il y aurait 500 Ko de transfert dont 495 inutiles, et cela multiplié par le nombre réel d'articles sélectionnés en tout ? Soit je suis un noob et je n'ai rien compris aux jointures, soit ZF le fait tout seul et rien dans cet exemple ne pouvait ne me le laisser supposer (car je ne connais pas ZF) et dans ce cas au temps pour moi, soit il y a vraiment quelque chose qui cloche.


Tu as tout à fait raison, c'était juste un exemple qui suppose que tu as une seule image sur une table, ce qui est effectivement inutile, autant rajouter un colonne "src" a la table page...

Des exemples plus concret que j'utilise:

Par exemple quand j'ai plusieurs type de contenu, pages, évènements, annonces, commerces, j'utilise une seul table d'urls, cela facilite les problèmes avec les doublons, donc je fais comme ça:

$select = $db->select(); 
$select->form('pages',array('id_page','title','description','text')); 
$select->order('date DESC'); 
$select->joinLeft(
    'urls',
    "urls.url_table_name='pages' AND urls.url_id=pages.id_page",
    array('url'=>'url','url_id')
)
$select->where('id_parent=?',$idParent); 
$pages = $db->fetchAll($select); 


En fait j'ai même des modèles tout fait pour que ce soit encore plus simple, au final je fais juste un truc comme ça:

$Events = new Select('events','id_event',array('title','date'));
$Events -> joinUrl();
$Events -> logo(100,100);
$Events -> fetchAll('id_page=?',$idPage);
$events = $Events ->addFirstImage('events_documents');


Ou effectivement addFirstImage n'utilise pas JOIN, mais fait une autre requête avec IN.

Un autre exemple ou j'utilise directement zend pour récupérer les produits choisi par un clients dans un panier:

$select = $db->select(); 
$select->form('orders_products',array('id_product','quantity','color','size',)); 
$select->order('date DESC'); 
$select->joinLeft(
    'products',
    "orders_products.id_product=products.id_product",
    array('price'=>'product_price','title','product_key','weight')
)
$select->where('id_order=?',$idOrder); 
$products = $db->fetchAll($select); 


Si je veux rajouter une colonne au produit, je sais tout de suite quelle ligne de la requête et concerné, si je veux voir la requête complète pour débugger je fais echo $select .
Modifié par matmat (27 May 2010 - 21:52)
Hello,

[HS Coldfusion]
Personnellement, la syntaxe Coldfusion qui mélange du SQL avec des balises <cfqueryparam> je ne trouve pas ça très clair, venant d'un background PHP.
J'ai du faire du Coldfusion il y a peu (et je vais être amené à en refaire) et j'ai préféré passer par la syntaxe des cfc, query = new Query(); query.addParam(name, value, cfsqlytpe)
[/HS Coldfusion]

Sinon pour information, au sein du framework cakePHP, les associations entre les différentes tables sont définies au niveau du Model (soit dans la classe même du modèle, soit on-the-fly quand vous en avez besoin.

Par exemple, vos codes précédents pourraient se traduire par un code semblable au suivant (executé dans le controlleur des pages)


$this->Page->bindModel(array(
	'hasMany' => array(
		'Url' => array(
			'foreignKey' => 'url_id',
			'conditions' => array('Url.url_table_name' => 'pages')
		)
	)
));
	$this->Page->find('all', array(
	 'fields' => array('id_page', 'title', 'description', 'text'),
	 'order' => array('date' => 'DESC'),
	 'conditions' => array('Page.id_parent' => $idParent)
	));


Mais vu que l'un des principes principaux de cakePHP est "convention over configuration", le code précédent peut facilement être réduit de moitié pour peu que l'on nomme les champs de la base de données selon la convention de cake : id pour la primary key et *nom_du_modele*_id pour les champs de liaison (ici page_id dans la table urls)

Après je pense surtout que la syntaxe de ce genre de requete dépends des habitudes de chacun. Personnellement j'apprécie que l'ensemble des options de mes requetes soient formées sous forme de tableau, c'est plus facile pour les modifier.
Mais je concois aussi que pour des requetes très customisées, il soit parfois préférable d'écrire du bon vieux SQL directement
Modifié par Tymlis (28 May 2010 - 07:19)
Pages :