Pagination de checkbox complétée depuis la BDD sous codeigniter

Pagination de checkbox complétée depuis la BDD sous codeigniter 1

Je fais une petite fixette sur codeigniter dans le cadre de l’écriture d’une application en ce moment. Ce billet est un aide mémoire (ou un tuto de bout en bout pour ceux que ça intéresse).

Contexte :

L’application a comme groupes de rôles l’administrateur puis quelques milliers d’autres groupes destinés à restreindre en visibilité les utilisateurs (environ 10000) dès la connexion sur leurs groupes utilisateurs respectifs. Pour schématiser, c’est un peu le fonctionnement de consultation de N comptes bancaires qui seraient consultés chacun par X utilisateurs.

Problème :

Pour initier le projet, j’ai pris le parti de ne pas me casser la tête à créer la librairie d’authentification de toute pièce. Mon choix s’est donc porté sur Codeigniter ion_auth library qui est complète et simple d’utilisation MAIS qui présente un très gros inconvénient : il n’y a pas de système de pagination des groupes utilisateurs.
Dans son fonctionnement, la librairie ion auth propose d’assigner les groupes aux utilisateurs (ce qui m’arrangeait). C’est pratique quand on a 4 ou 5 rôles à distribuer mais avec plus de 3000 rôles (correspondant à des groupes de consultation), ça devient totalement ingérable. Il est impensable de devoir chercher, depuis une fiche utilisateur, quelles sont les cases à cocher parmi 3000 groupes portant approximativement le même nom.

Solution :

Pour résoudre ce désagrément sans tout exploser, j’ai sorti le combo JSON + jQuery + datatables pour résoudre ce problème de pagination de checkboxes en utilisant ce que j’ai trouvé sur internet. Le moteur de recherche m’a guidé vers :

C’est parti !

Etape 1 : Création du fichier JSON

Ce fichier est utilisé par le script de génération des groupes. Il est utilisé pour afficher l’intégralité des groupes utilisateurs de ion auth sur lesquels ont va jouer avec les cases à cocher.

Il est formé de cette manière :
{« data »:[[« 1″, »admin »],[« 2″, »members »],[« 6″, »GROUPE1 »],[« 7″, »GROUPE2 »],[« 8″, »GROUPE3 »]]}

Comme ce n’est pas la construction par défaut de ce que ion auth propose pour la création de notre json sous codeigniter, on va exploiter une méthode du modèle Ion_auth_model.php

$this->data['groups'] = $this->ion_auth->groups()->result();

// CREATION DU JSON
$longueur = count($this->data['groups']);
$contenu_json = '{"data":[';
for ($i=0; $i < $longueur; $i++)
{
  $contenu_json .= '["'.$this->data['groups'][$i]->id.'","'.$this->data['groups'][$i]->name.'"]';
  if ($i != $longueur-1)
  {
    $contenu_json .= ',';
  }
}
$contenu_json .= ']}';
// FIN CREATION JSON

    //echo '{"data":[["'.$this->data['groups'][0]->id.'","'.$this->data['groups'][0]->name.'"]]}';
//$contenu_json = json_encode($this->data['groups']);
//var_dump($contenu_json);

// Nom du fichier à créer
$nom_du_fichier = 'fichier.json';

// Ouverture du fichier
$fichier = fopen($nom_du_fichier, 'w+');

// Ecriture dans le fichier
fwrite($fichier, $contenu_json);

// Fermeture du fichier
fclose($fichier);

C’est fait. Notre fichier fichier.json est dorénavant à la racine -> http://localhost/fichier.json (qu’il faudra placer ailleurs en situation réelle, bien sûr).

On peut du coup passer à la suite.

Etape n°2 : Préparation du tableau de cases à cocher datatables

Dans la vue users/edit_user_view.php virer

  <div class="form-group">
    <?php
    if(isset($groups))
    {
      echo form_label('Groupes','groups[]');
      foreach($groups as $group)
      {
        echo '<div class="checkbox">';
        echo '<label>';
        echo form_checkbox('groups[]', $group->id, set_checkbox('groups[]', $group->id, in_array($group->id,$usergroups)));
        echo ' '.$group->name;
        echo '</label>';
        echo '</div>';
      }
    }
    ?>
  </div>
  <?php echo form_hidden('user_id',$user->id);?>
  <?php echo form_submit('submit', 'Modifier l\'utilisateur', 'class="btn btn-primary btn-lg btn-block"');?>
<?php echo form_close();?>

et remplacer par  :

<div class="form-group">
<table id="example" class="display select" cellspacing="0" width="100%">
   <thead>
      <tr>
         <th><input name="select_all" value="1" type="checkbox"></th>
         <th>Noms Groupes</th>
      </tr>
   </thead>
</table>
<?php echo form_hidden('user_id',$user->id);?>
<p><button class="btn btn-primary btn-lg btn-block">Modifier les données</button></p>
</form>

L’absence de balise <td></td> est normale. Elle vont apparaître à la lecture du JSON.

Etape 3 : JQuery de génération des cases à cocher datatables, coche des cases de la BDD et soumission du formulaire

a/ Traiter le contrôleur pour que la vue repeuple le formulaire des cases déjà cochées (en base)

Dans Users.php qui envoie les données à la vue, il faut charger $this->data['votrevariable'] avec les groupes déjà assignés à l’utilisateur pour recocher les cases :

$this->data['groupes_utilisateur'] = $usergroups = $this->ion_auth->get_users_groups($user->id)->result();

b/ Positionner le script Jquery de génération / lecture des cases à cocher

Et $groupes_utilisateur est exploité par le javascript (cf. ci-dessous « boucle de récupération des groupes ») :

<script type="text/javascript">
// DEBUT SCRIPT JSON
function updateDataTableSelectAllCtrl(table){
   var $table             = table.table().node();
   var $chkbox_all        = $('tbody input[type="checkbox"]', $table);
   var $chkbox_checked    = $('tbody input[type="checkbox"]:checked', $table);
   var chkbox_select_all  = $('thead input[name="select_all"]', $table).get(0);

   // Si aucune des checkbox n'est cochée
   if($chkbox_checked.length === 0){
      chkbox_select_all.checked = false;
      if('indeterminate' in chkbox_select_all){
         chkbox_select_all.indeterminate = false;
      }

   // Si toutes les checkbox sont cochées
   } else if ($chkbox_checked.length === $chkbox_all.length){
      chkbox_select_all.checked = true;
      if('indeterminate' in chkbox_select_all){
         chkbox_select_all.indeterminate = false;
      }

   // Si quelques checkbox sont cochées
   } else {
      chkbox_select_all.checked = true;
      if('indeterminate' in chkbox_select_all){
         chkbox_select_all.indeterminate = true;
      }
   }
}

$(document).ready(function (){
   
   <?php 
   // boucle de récupération des groupes de l'utilisateur pour 
   // repeupler le tableau
   $lignes_a_cocher = "";
   foreach ($groupes_utilisateur as $guser) {
      $lignes_a_cocher .= '"'.$guser->id.'",';
   } 
   $lignes_a_cocher = rtrim($lignes_a_cocher,',');


   ?>
   // Tableau contenant les ID sélectionnés
   var rows_selected = [<?php echo $lignes_a_cocher?>];
   var table = $('#example').DataTable({
      'ajax': 'http://localhost/fichier.json',
      'columnDefs': [{
         'targets': 0,
         'searchable':false,
         'orderable':false,
         'width':'1%',
         'className': 'dt-body-center',
         'render': function (data, type, full, meta){
             return '<input type="checkbox">';
         }
      }],
      'order': [1, 'asc'],
      'rowCallback': function(row, data, dataIndex){
         // Récupération du row ID
         var rowId = data[0];

         // SI le row ID est dans la liste des row ID sélectionnés
         if($.inArray(rowId, rows_selected) !== -1){
            $(row).find('input[type="checkbox"]').prop('checked', true);
            $(row).addClass('selected');
         }
      }
   });

   // Tient le clic sur la checkbox
   $('#example tbody').on('click', 'input[type="checkbox"]', function(e){
      var $row = $(this).closest('tr');

      // Récupère le row data
      var data = table.row($row).data();

      // Récupère le row ID
      var rowId = data[0];

      // Determine whether row ID is in the list of selected row IDs 
      var index = $.inArray(rowId, rows_selected);

      // Si la checkbox est cochée et que le row ID n'est pas
      // dans la liste des row ID
      if(this.checked && index === -1){
         rows_selected.push(rowId);

      // Sinon, si la checkbox n'est pas cochée et 
      // que le rowID est dans la liste des row ID sélectionnés
      } else if (!this.checked && index !== -1){
         rows_selected.splice(index, 1);
      }

      if(this.checked){
         $row.addClass('selected');
      } else {
         $row.removeClass('selected');
      }

      // Met à jour le contrôle SELECT ALL
      updateDataTableSelectAllCtrl(table);

      // Evite que le clic se propage au parent
      e.stopPropagation();
   });

   // Gère le clic sur les cellules en lien avec les checkboxes
   $('#example').on('click', 'tbody td, thead th:first-child', function(e){
      $(this).parent().find('input[type="checkbox"]').trigger('click');
   });

   // Gère le clic select all
   $('thead input[name="select_all"]', table.table().container()).on('click', function(e){
      if(this.checked){
         $('#example tbody input[type="checkbox"]:not(:checked)').trigger('click');
      } else {
         $('#example tbody input[type="checkbox"]:checked').trigger('click');
      }

      // Evite que le clic se propage au parent
      e.stopPropagation();
   });

   // Récupère les évènements du draw de la table
   table.on('draw', function(){
      // Mets à jour en cas de "select all"
      updateDataTableSelectAllCtrl(table);
   });
    
   // Récupère l'évènement de soumission de formulaire 
    $('#frm-example').submit(function(e) {
      var form = this;

      // Itère en fonction des checkboxes cochées
      $.each(rows_selected, function(index, rowId){
         // Constitution du formulaire
         $(form).append(
             $('<input>')
                .attr('type', 'hidden')
                .attr('name', 'groups[]')
                .val(rowId)
         );
      });

   });
});
// FIN SCRIPT JSON

Youhou ! Ca marche !

 

PS : A ajouter dans le header (en plus de jquery)

<script src="https://cdn.datatables.net/select/1.2.7/js/dataTables.select.min.js"></script>
<link type="text/css" href="https://cdn.datatables.net/select/1.2.7/css/select.dataTables.min.css" rel="stylesheet" >

 

 

Codeigniter : faire un where sur un champ vide ou NULL

Codeigniter : faire un where sur un champ vide ou NULL 2

Encore un pense-bête qui peut servir à d’autres.

Dans une table dont les valeurs vides ne sont pas nulles mais vides, c’est à dire de cette nature ->  » (rien entre deux quotes), je souhaite poser comme condition de ne pas prendre en compte ces valeurs.

La manoeuvre est simple, il faut juste échapper les quotes :

->where('champ != \'\'')

pour que ça fonctionne. Si vous tentez les guillemets, vous vous collerez une erreur.

Egalement, et c’est mon cas, la valeur champ est passée en paramètre pour utiliser plusieurs fois la même méthode d’appel à la requête. Même procédé, en positionnant le champ comme variable :

->where($champ.' != \'\'')

Enfin, si votre champ n’est pas vide mais NULL, il faut ruser tout doucement l’active record qui ne prend pas ISNULL en faisant

->where($champ.' !=', null, false)

Créer une sous-requête avec condition sous Codeigniter

Créer une sous-requête avec condition sous Codeigniter 3

Je mets ça en aide-mémoire vu que j’ai un peu ramé pour créer une sous-requête avec condition sous Codeigniter 3.1.6, l’active record ne prenant pas en charge cette fonctionnalité.

Le but de la manoeuvre est de renvoyer 1 pour vrai si un utilisateur est présent dans la table users_groups et si le nom du groupe passé également en paramètre de la fonction est un groupe valide dans la table groups.

Pour réaliser cela, il faut donc sélectionner 1 quand l’id de l’utilisateur est égal à l’id de session utilisateur passé en paramètre et quand l’id du groupe est présent dans la table des groupes d’après son nom complet.

Active record ne permettant pas cela directement, la requête doit être lancée puis son résultat stocké dans une chaîne renvoyée par la méthode get_compiled_insert([$table =  »[$reset = TRUE]]) chargée dans la variable $query que l’on jouera après, ce qui donne (faites super gaffe aux quotes et guillemets) :

$query = $this->db->select("(SELECT 1 as ok FROM users_groups WHERE user_id = '".$user_id."' and group_id = (select id from groups where name = '".$nom_complet_du_groupe."'))", FALSE)->get_compiled_select();

La chaine $query est chargée du contenu de la requête. On peut alors la lancer avec :

$query = $this->db->query($query);

A ce niveau, on serait tenté de lancer un simple return $query->result(); récupéré par le contrôleur. Malheureusement, il sera compliqué de récupérer simplement la valeur unitaire dans une variable à cause de l’erreur Array to string conversion.

Puisque l’on sait qu’on ne renvoie qu’une valeur, on utilise la méthode row() qui ne renvoie… qu’une valeur :

$dac = $query->row();

$dac est maintenant chargé. Comment le renvoyer le nom du champ ? Facilement en fait grâce au select 1 as ok…

Un simple :

return $dac->ok;

vous en convaincra.

C’en est fini. Nous avons créé notre requête avec un sous-select sous condition puis renvoyé un champ unique réglant ainsi le problème des erreurs Array to string conversion ou stdClass object.

Tout rassemblé, l’obtention du résultat se fait avec :

$query = $this->db->select("(SELECT 1 as ok FROM users_groups WHERE user_id = '".$user_id."' and group_id = (select id from groups where name = '".$nom_complet_du_groupe."'))", FALSE)->get_compiled_select();
$query = $this->db->query($query);
$dac = $query->row();
return $dac->ok;

Répliquer une base de données Postgresql en local

Répliquer une base de données Postgresql en local 4

Quand on a peur de se louper avant de manipuler violemment une base de données, il est utile de la sauvegarder. Avec postgresql, les commandes les plus couramment utilisées sont pg_dump et pg_restore.

L’inconvénient est que cette manipulation est chiante et très longue avec une base de taille conséquente.

Il est alors préférable de préférer utiliser la réplication qui s’opère en deux lignes avec l’avantage énorme d’être ultra rapide.

Pour répliquer une base de données postgresql, il faut d’abord tuer les connexions existantes sur la base que l’on souhaite dupliquer. Pour cela, il est indispensable de connaître le mot de passe du user postgres. Pas de panique, si vous l’avez oublié, il vous est possible de récupérer ce mot de passe super-utilisateur postgres très facilement en suivant ce tuto.

C’est fait ? Alors on continue.

Tuer les connexions Postgresql en cours

Première étape : Se connecter à postgresql en ligne de commande avec psql

user@pc:~$ psql -h localhost -U postgres
psql (9.5.10, server 9.3.15)
Type "help" for help.

postgres=#

Deuxième étape : tuer les connexions en cours sur la base que l’on veut répliquer

La base à traiter s’appelle restits

postgres=# select pg_terminate_backend(pid) from pg_stat_activity where pid <> pg_backend_pid() and datname = 'restits';
 pg_terminate_backend
----------------------
 t
(1 row)

C’est fait ! On a tué notre utilisateur (1 row). Passons à la suite.

Répliquer une base postgresql

A partir d’ici, c’est du velours vu que ça se joue en une ligne (assurez vous préalablement d’avoir l’espace disque suffisant sur votre machine).

postgres=# create database shhh template restits;
CREATE DATABASE
postgres=#

Et voilà. Pour vous donner une idée de la chose, il a fallu 3mn10 secondes pour répliquer 24Go. Cette commande a créé la base shhh à l’identique de restits en utilisant comme template la base que l’on souhaitait dupliquer.

Cette opération est rendue possible parce que postgresql crée toujours une base à partir d’une template ; la template standard utilisée par CREATE DATABASE est template1 (voir la doc).

 

 

Comment réinitialiser le mot de passe super-utilisateur PostgreSQL

Comment réinitialiser le mot de passe super-utilisateur PostgreSQL 5

Avec Postgresql, il y a deux options pour retrouver le mot de passe super-utilisateur postgres qu’on a lamentablement paumé.

L’option 1 et l’option 2 🙂

La première option consiste, sous windows, à aller fouiller le fichier pgpass.conf généré lorsqu’on utilise pgAdmin. Il faut « afficher les dossiers et fichiers cachés« , puis se rendre dans Utilisateur > NOMUTILISATEUR > AppData > Roaming > postgresql.

A cet endroit se trouvent 2 fichiers, dont pgpass.conf qui contient les mots de passe de connexion postgreSQL enregistrés en clair ! (sous Linux, tout est stocké dans le fichier pgpass. Un find vous aidera à le trouver)

Comment réinitialiser le mot de passe super-utilisateur PostgreSQL 6

Il n’y a alors plus qu’à se servir et se connecter.

La seconde option nécessite un accès au serveur. Il faut que vous éditiez le fichier pg_hba.conf qui se trouve en principe dans /etc/postgresql/9.3/main.

Dans ce fichier, vous devez changer

local all postgres peer

en

local all postgres trust

Sauvegardez, et relancez postgreSQL.

Vous pouvez dès lors vous connecter avec le compte postgres, puis modifier le mot de passe :

psql postgres postgres

postgres=# alter user postgres with password 'postgres';

Pour finir, vous pouvez retourner sur votre fichier pg_hba.conf et le remettre dans son état initial avant de redémarrer le serveur.

 

Qu’est ce que le Hemline Index ?

Qu'est ce que le Hemline Index ? 8

L’index Hemline – ou index ourlet – date de 1926.

On doit sa création à l’économiste américain George W. Taylor. Il avait remarqué que la longueur des jupes était proportionnelle à l’état économique des pays.

Qu'est ce que le Hemline Index ? 9

Visionnaire, la qualité de son index ne s’est jamais démentie.

Courtes en 1920, longues en 1930, …, minis dans les années 60 et près de la cheville dans les années 2010, les jupes marquent l’époque.

Vu la longueur cette année, la confiance revient…

Combien de temps a duré la guerre la plus courte de l’histoire

Combien de temps a duré la guerre la plus courte de l'histoire 10

Combien de temps a duré la guerre la plus courte de l'histoire 11La guerre la plus courte de l’histoire s’est déroulée entre le Zanzibar – protectorat anglais – et l’Angleterre deux jours après le décès du Sultan Sayyid Hamid ben Thuwain ben Said, le 27 août 1896, et après que Khaled ben Said, cousin du Sultan tente d’usurper le trône.

C’est l’Amiral Rawson, garant des intérêts britanniques, qui mettra fin à cette situation en bombardant le palais du sultan.

Les combats ont duré… 38 minutes.

Qui a inventé le chocolat au lait

L’invention du chocolat remonte à la fin du XVIIème siècle. On la doit à un médecin irlandais, Sir Hans Sloane.

Né à Killyleag en 1660 dans une famille modeste, Hans Sloane se passionne rapidement pour l’histoire naturelle. Cela le conduit à partir étudier la médecine en Angleterre puis en France.

Au terme de ses études, il s’établit à Londres à Bloomsberry, un quartier résidentiel à la mode.

Le choix de l’emplacement lui apporte une clientèle aristocratique et fortunée, dont la reine Anne et les rois George I et II.

Qui a inventé le chocolat au lait 12

Il faut dire que le docteur Sloane est très innovant pour l’époque. Il pousse à l’usage de la quinine pour traiter la malaria, il est partie prenante dans les nouveaux traitements contre la variole telle que la variolisation (inoculation d’une souche ancienne de variole – résultats très contestables).

Mais il cherche également des moyens d’apporter un mieux être à ses patients.

En 1687, Sir Hans Sloane accompagne le Duc d’Albermarle – nouveau gouverneur – en Jamaïque. Là, il découvre le lait chocolaté – une recette jamaïquaine composée de cacao fraichement broyé, accompagné de cannelle, et bouilli dans du lait – et surtout ses propriétés apaisantes.

Quelques années plus tard, il reprendra la formule à son compte devenant ainsi « l’inventeur » du chocolat au lait.

Postgresql – Sélectionner les champs d’une colonne commençant par un chiffre

Postgresql - Sélectionner les champs d'une colonne commençant par un chiffre 13

Aide mémoire

La table adresse présentée ci-dessous est à remettre en forme en remettant les valeurs dans les bons champs. Un bon nombre de valeurs de libellevoie sont mal formées et nécessitent préalablement d’identifier les champs commençant par un nombre afin de reventiler les données.

Postgresql - Sélectionner les champs d'une colonne commençant par un chiffre 14

Pour les sélectionner, la requête est :

select * from origine 
where left(libellevoie, 1) ~ '^[0-9]*$'

Cette requête est utilisable ensuite dans un script d’update de la table où les valeurs sont réaffectées dans numvoie, puis dans typevoie en une passe.