Cela fait maintenant presque deux ans que je suis entré dans la vie active en tant que développeur et designer Web. Et en tant que tel, je suis souvent (comprenez : tout le temps) amené à faire quasiment les mêmes choses, bien que chaque projet soit différent.

Personnellement j’aime passer du temps à peaufiner un design. je déteste réécrire les mêmes fonctions pour chaque projet. Alors oui, il des frameworks PHP, mais pourquoi utiliser un bazooka lorsqu’on doit abattre une mouche ?

MAJ 11/01/2016 : Si le PHP Objet vous parle, vous pouvez lire Créer son framework PHP Objet qui est un peu plus récent 

Attention, l’article est un chouilla trop long (beaucoup de lignes de code).

Oui utiliser une CMS déjà tout fait c’est bien, oui utiliser un framework en PHP Objet c’est bien. Seulement parfois, utiliser une arme de destruction massive n’est pas la bonne méthode à appliquer pour régler les choses les plus simples.

Ces dernier mois, au fil des projets qui le permettaient, j’ai pu me construire ma propre bibliothèque de fonctions, en essayant de l’améliorer à chaque itérations, tout en restant le plus générique possible pour permettre une très bonne portabilité de projet en projet. Bien sûr je n’ai pas hésité à piquer des idées dans les frameworks PHP, mais plutôt que de me trimballer toute la clique, j’ai maintenant un petit « framework » qui me permet de faire juste ce dont j’ai besoin. Cela m’a aussi permis de monter facilement notre propre CMS, lui aussi très basique.

Aujourd’hui j’ai donc envie de partager avec vous quelques unes de mes fonctions, car ça fait un petit moment que je n’ai pas publié un peut de code PHP dans mes pages.

Mes fonctions de CRUD

Etape obligatoire lorsqu’on souhaite agir sur des informations stockées en base de données, les célèbres fonctions INSERT, SELECT, UPDATE et DELETE. Après plusieurs itérations je suis arrivé à des fonctions quasiment utilisable par n’importe quel projet, même si je pense qu’elles ne sont pas encore parfaites, pour le moment elles me suffisent. Cependant, si vous avez de quoi les améliorer n’hésitez pas à me donner votre avis !

Ajouter un enregistrement dans une table

Après pas mal de projet dans lesquels j’avais une fonction d’ajout pour chaque type de contenu (page, article, etc.) enregistrés dans des tables différentes, j’ai essayé de trouver un moyen simple d’ajouter n’importe quelle données dans n’importe quelle table. Je suis donc passé de plusieurs fonction plus ou moins lourde à une bête fonction d’une quinzaine de ligne avec laquelle je peux faire ce que je veux, simplement en lui passant un tableau des données à ajouter, et le nom de la table dans laquelle faire cet INSERT.

/***
 *  insert des données dans la table en paramètre
 *  @datas  tableau des données à insérer dont la clé et le nom du champs dans la table
 *  @table  table dans laquelle insérer les données
 */
function add($datas, $table){
  $bdd = db(); //on ouvre la connexion à la base de données

  foreach($datas as $key => $value){
    $keys[] = $key;
    $values[] = $value;
  }

  $strSQL = "INSERT INTO ".$table." (";
  foreach($keys as $ky => $k){ $strSQL .= $k . ","; }

  $strSQL = substr($strSQL,0,-1) . ") VALUES(";
  foreach($values as $vl => $v){ $strSQL .= "?,"; }

  $strSQL = substr($strSQL,0,-1) . ")";

  $query = $bdd->prepare($strSQL);
  if($query->execute($values)) return $bdd->lastInsertId();
  else return false;
}

Comment utiliser la fonction ?

Après avoir vérifiées, échappées, etc., les informations soumisent à mon formulaire, il me suffit de créer un tableau dont les clés seront les noms des champs que je veux renseigner pendant mon INSERT.

Exemple : je veux renseigner le nom, l’adresse email et le commentaire dans la table « comments ».

$datas = array(
  'nom' => $nom,
  'email' => $email,
  'commentaire' => $commentaire
);

if(add($datas,'comments')) {
  return "cool";
} else return "pas cool";

Si tout c’est bien passé, la fonction add() me renvoie l’ID de l’enregistrement créé.

Mettre à jour les informations d’un enregistrement

Là aussi, c’est une fonction vraiment très simple. comme ma fonction add(), ma fonction update() requiert un tableau des données et le nom de la table à modifier. Seul petit changement, on doit aussi fournir l’ID de l’enregistrement à modifier.

/***
 *  met à jour les données de l'ID dans la table en paramètre
 *  @id identifiant de la ligne à modifier
 *  @datas  tableau des données à insérer dont la clé et le nom du champs dans la table
 *  @table  table dans laquelle insérer les données
 */
function update($id, $datas, $table){
  $bdd = db();
  foreach($datas as $key => $value){
    $keys[] = $key;
    $values[] = $value;
  }

  $strSQL = "UPDATE ".$table." SET ";
  foreach($datas as $key => $value){
    $strSQL .= $key . " = ?,";
  } $strSQL = substr($strSQL,0,-1) . " WHERE id = ?";
  $values[] = $id;
  $query = $bdd->prepare($strSQL);
  if($query->execute($values)) return true;
  else return false;
}

Supprimer un enregistrement

Rien de bien compliqué là non plus, on supprime l’enregistrement dont l’ID est passé en paramètre dans la table elle aussi en paramètre.

/***
 *  supprime les données correspondant à l'ID dans la table en paramètre
 *  @id   identifiant de la ligne à supprimer
 *  @table  table sur laquelle on applique la suppression
 */
function delete($id, $table){
  $bdd = db();
  $strSQL = "DELETE FROM ".$table." WHERE id = ?";
  $query = $bdd->prepare($strSQL);
  //print_r(array($id));

  if($query->execute(array($id))) return true;
  else return false;
}

Retourner une liste d’enregistrements

Cette dernière est l’une des fonctions que j’ai eu le plus de mal à rendre générique tout en me permettant de faire beaucoup de choses avec, comme par exemple préparer le terrain pour l’utiliser dans le cadre des données scindées sur plusieurs pages.

Ici ma fonction me permet de récupérer à la fois les enregistrements de ma requête, le total d’enregistrements que retournerai ma requête si aucun paramètre LIMIT n’était renseigné, la requête exécutée (pour les tests en phase de développement) ou les erreurs possibles quant à l’exécution de cette requête.

/***
 *  retourne le resultat d'un select
 *  @columns  colonnes à selectionner pour la requête (ex: array('champ1','champ2') ou '*')
 *  @table    nom de la table sur laquelle faire la requête
 *  @where    champs sur lequels appliquer des conditions ( ex: array( 'champ1 =' => 'valeur', 'champ2 LIKE' => 'valeur%') )
 *  @concats  [ AND | OR ]
 *  @order    champs sur lequels appliquer le tri, et l'ordre pour chaque champs (ex: array('champ1' => 'ASC','champ2' => 'DESC') )
 *  @limit    limit[0] => debut de la liste, limit[1] => nombre d'éléments dans la liste retournée (ex: array('0','20') )
 *
 *  return @retour  : tableau contenant la requête executée, les éventuelles erreurs et le resultat de la requête
 */
function get($columns = null, $table = null, $where = null, $concats = "AND", $order = null, $limit = null){
  $bdd = db();
  $retour = array(); //variable de type tableau, retournée par la fonction
  $rows = "";
  $clause = "";
  $sort = "";
  $limitStr = "";

  if(!is_null($columns) && !is_null($table)){

    // si $rows est un tableau ou égale à * tout va bien.
    if(is_array($columns)){
      foreach($columns as $column) { $rows .= $column .', '; }
      $rows = substr($rows,0,-2);
    } elseif($columns == '*'){
      $rows = '*';
    } else {
      $retour['erreur'] = "Les champs selectionné doivent être appelé depuis une variable Tableau";
    }

    if(!in_array(strtolower($concats),array('and','or'))){
      $retour['erreur'] = "<strong>".$concats."</strong> n'est pas une valeur autorisée pour concaténer des conditions. Utilisez 'OR' ou 'AND'.";
    }

    /*
    si @where est renseigné, on filtre les résultats grâce au tableau @where construit comme suit :
      array ('colname operateur' => 'valeur');
      ex: array('page_id =' => 5);
    sinon, on ne filtre pas les résultats
    */
    if(!is_null($where) && is_array($where)){
      foreach($where as $k => $v){
        $clause .= $k." ? ".$concats." ";
        $values[] = $v;
      }
      $clause = " WHERE ".substr($clause,0,(-(strlen($concats)+2)));
    } elseif(!is_null($where) && !is_array($where)){
      $retour['erreur'] = "La clause WHERE doit être construite via une variable Tableau";
    } else {
      $clause = "";
    }

    //si $order est un tableau et n'est pas null
    if(!is_null($order) && is_array($order)){
      foreach($order as $k => $v){ $sort .= $k." ".$v.", "; }
      $sort = " ORDER BY ".substr($sort,0,-2);
    } elseif(!is_null($order) && !is_array($order)) {
      $retour['erreur'] = "ORDER BY doit être construit via une variable Tableau";
    } else {
      $sort = "";
    }

    if(!is_null($limit) && is_array($limit) && is_numeric($limit[0]) && is_numeric($limit[1])){
      $debut = $limit[0];
      $nbRows = $limit[1];
      $limitStr = " LIMIT " . $debut . "," . $nbRows;
    } elseif(!is_null($limit) && !is_array($limit)){
      $retour['erreur'] = "LIMIT doit être construit via un tableau de deux entiers";
    } else {
      $limitStr = "";
    }

    // on construit la requête
    $strSQL = "SELECT ".$rows." FROM ".$table.$clause.$sort.$limitStr;
    if(empty($retour['erreur'])){
      $query = $bdd->prepare($strSQL);
      $query->execute(@$values);
      $retour['requete'] = $strSQL;
      $retour['reponse'] = $query->fetchAll(PDO::FETCH_ASSOC);

      $sqlTotal = "SELECT COUNT(*) as total FROM ".$table.$clause.$sort;
      $q = $bdd->prepare($sqlTotal);
      $q->execute(@$values);
      $tot = $q->fetchAll(PDO::FETCH_ASSOC);
      $retour['total'] = $tot[0]['total'];
    }

  } else {
    $retour['erreur'] = "Impossible de créer la requete, les champs à selectionner et la table sont vide";
  }

  return $retour;
}

Comment utiliser la fonction ?

Exemple : Je veux récupérer les cinq articles les plus récents écrits par nighcrawl dont le titre contient le mot « framework » publié depuis la date à laquelle la requête est exécutée.

$champs = array('id','titre','nom','contenu');
$conditions = array(
  'nom =' => 'nighcrawl',
  'date < =' => date('Y-m-d H:i:s'),
  'titre LIKE' => '%framework%'
);
$trier = array('date' => 'DESC');
$limite = array(0, 5);

$resultat = get($champs,'articles',$conditions,"AND",$trier,$limite);
if(isset($resultat['reponse'])){
  foreach($resultat['reponse'] as $row){
    echo "<article>
    <header>
      <h1>".$row['titre']."</h1>
    </header>
    <div>".$row['contenu']."</div>
    <footer>Auteur : ".$row['nom']."</footer>
    </article>";
  }
}
else echo $resultat['erreur'];

Mettre en place un système de pagination

Je m’arrêterai ici parce que l’article est déjà pas mal long, et si vous êtes arrivé jusqu’ici je vous dis chapeau. Personnellement je me serai déjà arrêté :). On fini donc avec une fonction très utile pour générer rapidement des liens de pagination afin de répartir les données d’une requête sur plusieurs pages. Voici donc la belle fonction :

/***
 *  génère des liens de pagination : numeros de pages, 'suivants', 'précédents'
 *  @total  nombre total d'enregistremnts à paginer
 *  @nbpp nombre d'enregistrements à afficher par page
 *  @link chaine qui servira à construire les liens vers les différentes pages
 */
function pagination($total, $nbpp, $link){
  echo"<div class='pagination'>";
    /** Pagination **/
    //calcul du nombre de pages
    $nbLiens = ceil($total/$nbpp);

    if($nbLiens > 1){
      /** précédents **/
      if(isset($_GET['d']) && $_GET['d'] > 0){
        echo "<a href='".$link.($_GET['d']-$nbpp)."'>« Précédents</a>";
      } else {
        echo "<span>« Précédents</span>";
      }
      /** pages ***/
      for($i = 0; $i < $nbLiens; $i++){
        if($_GET['d'] == ($i*$nbpp)){
          echo "<span class='active_pagi'>".($i+1)."";
        } else {
          echo "<a href='".$link.($i*$nbpp)."'>".($i+1)."</a>";
        }
      }

      /** suivants **/
      if(isset($_GET['d']) && $_GET['d'] >= 0 && $_GET['d'] < ($total-$nbpp)){
        echo "<a href='".$link.($_GET['d']+$nbpp)."'>Suivants »</a>";
      } else {
        echo "<span>Suivants »</span>";
      }
    }
  echo "</div>";
}

La pagination va s’effectuée en deux temps, d’abord l’appel de la fonction get(), puis l’appel de la fonction pagination(). Si on réutilise l’exemple précédent :

$nbpp = 5; //5 articles par page
$limite = array(intval($_GET['d']),$nbpp);
$resultat = get($champs,'articles',$conditions,"AND",$trier,$limite);

if(isset($resultat['reponse'])){
  foreach ($resultat as $row) {
    //affichage des articles
    ...
  }
  pagination($resultat['total'],$nbpp,'index.php?parametre=valeur&d=');
} else echo $resultat['erreur'];

Fin !

Merci de m’avoir lu jusqu’au bout. Je vous libère ici ! En espérant que ces quelques fonctions puissent vous être utiles. Elles ne sont pas parfaites et certaines pourraient être encore améliorées, alors si vous avez des idées, n’hésitez pas à les partager dans les commentaires.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.