Pagina 1 di 3 1 2 3 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 27
  1. #1

    [Pillola] Refresh pagina e duplicazione dati nel db

    [PILLOLA] Refresh pagina con form e duplicazione dati nel db

    Come evitare il problema della duplicazione dati

    Puntualmente si ripresenta nei thread il quesito su come evitare che uno user
    possa semplicemente con un refresh della pagina (F5) inserire piu' e piu' volte
    lo stesso dato, creando innumerevoli record duplicati.

    Poiche' non e' semplice spiegare il perche' ed il percome ad un novizio del PHP,
    ho pensato di mettere a disposizione un esempio su come evitare il problema.

    La soluzione piu' semplice ed immediata sarebbe quella di rinviare ad altra pagina
    che provveda con un redirect a rimandarci sulla pagina dove e' situato il form
    di immissione dati. In questo modo la cache del browser non sarebbe piu' valida e
    $_POST o $_GET svuotati del loro contenuto.

    Proviamo invece a considerare di rimanere sempre e solo con una pagina di immissione
    dati come richiesto da alcuni. Il modo che viene ora proposto puo' anche essere
    suddiviso in due pagine, ma in questo caso occorrera' provvedere dalla seconda pagina
    il rimando alla prima in caso di mancanza di dati o di refresh dei dati del form.

    Per evitare il reinserimento dei dati ci sono piu' possibilita':

    1) Fare un SELECT sull'ultimo id inserito e verificare se i dati da immettere sono
    identici a quelli presenti in $_POST o $_GET.

    2) Tramite la sessione valorizzare una variabile stringa da passare con $_SESSION
    dopo l'esito positivo della INSERT da verificare poi all'ingresso della pagina.
    Se sara' presente la stringa significhera' che il dato e' gia' stato inserito.

    Il primo metodo non e' flessibile, sarebbe dipendente dal tipo di campo, univocita',
    numerosita' e potrebbe essere dispendioso controllare anche decine di campi.
    Inoltre richiede sempre una query preventiva.

    Il secondo metodo impedirebbe addirittura di inserire ulteriori dati se non chiudendo
    e riaprendo il browser. Altrimenti necessita un redirect ad una pagina appoggio che
    unsetti la variabile di sessione e rimandi alla pagina del form.

    Necessita quindi un sistema sicuro, semplice e facilmente utilizzabile, in altre
    parole "standard". Cioe' dove lo metto lo metto e lui funziona senza altro chiedere.

    Proviamo quindi il seguente script che verra' commentato man mano. Intanto se volete
    provare senza fare cambiamenti allo script, potete configurare la seguente tabella
    da mettere nel db "TEST" o dove vi pare aggiornando pero' lo script se messa altrove.
    codice:
    CREATE TABLE `tabella1` (
    `id` int(4) unsigned NOT NULL auto_increment,
    `titolo` varchar(255) NOT NULL default '',
    `autore` varchar(50) NOT NULL default '',
    `note` varchar(255) NOT NULL default '',
    PRIMARY KEY  (`id`)
    )
    Ora lo script con nome file: quello_che_vuoi.php.
    codice:
    <?php
    session_start();
    
    // verifichiamo se $_SESSION e' valorizzato. Se lo e', trasferiamo il contenuto
    // in $check altrimenti settiamo $check = vuoto. Se $_SESSION non esiste significa
    // che e' la prima volta che viene eseguito lo script.
    
    isset($_SESSION['check'])  ?  $check =  $_SESSION['check']  :  $check = '';
    
    // valorizziamo ora una variabile "$caso" con un dato casuale non ripetibile
    // Questa variabile trasmessa in modo hidden dal form ci permettera' di riconoscere
    // quando il dato in POST viene trasmesso da un refresh (sara' identico) oppure
    // da un form (sara' diverso) e permettera' allo user di reinserire gli stessi dati
    // ma da form e non per refresh della pagina. Se questa duplicazione non fosse ammessa
    // sara' sufficiente rimuovere dal form la riga con:
    // <input type = "hidden" name = "check" value = "$caso">
    
    $caso = microtime();
    
    // Prepariamo il form con la sintassi heredoc e lo mettiamo in $str .
    // Assicuriamoci di non avere spazi dopo la fine della prima e dell'ultima riga.
    // Un return secco.
    
    $str = <<<FORM
    <html><body>
    <form method = "POST" action = "$_SERVER[PHP_SELF]">
     Prego inserire i dati. I campi con * sono obbligatori.
    
    
    
    * Titolo <input type= "text" name = "titolo"> 
    
    * Autore <input type= "text" name = "autore"> 
    
    Note <input type= "text" name = "note"> 
    
    <input type = "hidden" name = "check" value ="$caso">
    <input type = "submit" name = "invia" value = "- Invia -">
    </body></html>
    FORM;
    
    // verifichiamo ora se $_POST esiste e che non sia vuoto. Sara' da fare per tutti i campi
    // che devono essere compilati in modo obbligatorio. Se i campi non sono compilati o
    // $_POST non esistente verremo inviati al FORM di immissione presente nell' ELSE.
    
    if(isset($_POST['titolo'], $_POST['autore']) AND $_POST['titolo']!='' AND $_POST['autore']!='')
      {
    
      // Se siamo arrivati qui significa che $_POST c'e' e i dati pure. Verifichiamo se l'hash
      // prodotto da MD5($_POST) e' identico a quello passato con $_SESSION e trasferito in $chek.
      // utilizziamo serialize() perche' essendo $_POST un array, riceveremmo un NOTICE .
    
              if($check === MD5(serialize($_POST)) ) {
                      echo "
    Dati gia immessi - ciao ciao";
                      echo $str;
                      exit;
                      } else {
    
                      // Poiche' dobbiamo mantenere integro il contenuto di $_POST
                      // per poterlo confrontare, eventuali controlli sulle stringhe li faremo
                      // generando nuove variabili. Usiamo come esempio TRIM() ma potrebbe
                      // essere qualunque la funzione di controllo utilizzata.
    
                      $titolo = trim($_POST['titolo']);
                      $autore = trim($_POST['autore']);
                      $note = htmlentities(trim($_POST['note']));
    
                      // Ora inseriamo i dati nel db. Inserite la vostra connessione
    
                      require "./config.inc.php";
                      mysql_select_db('test');
    
                      mysql_query("INSERT INTO tabella (titolo, autore, note)
                                   VALUES ('$titolo','$autore', '$note')");
    
                      // verifichiamo la riga inserita
    
                      $num = mysql_affected_rows();
    
                           // Se la riga e' inserita mandiamo un avviso e settiamo l'hash
                           // nell'array di sessione. Oppure avvisiamo del fallimento.
                           // in questo ultimo caso notate che l'hash non viene prodotto e
                           // che ripresentiamo comunque il form in entrambi i casi.
    
                           if($num == 1)  {
                           echo "
    Dati inseriti
    ";
                           $_SESSION['check'] = MD5(serialize($_POST));
                           echo $str;
                           exit;
                           }
                                else  {
                                      echo "
    Dati NON inseriti - Riprova";
                                      echo $str;
                                      exit;
                                       }
                     }
    
    // nel caso non fosse settato $_POST perche' e' la prima volta che viene eseguita la pagina
    // oppure $POST settato ma con dei campi vuoti. presentiamo o ripresentiamo il form.
    
       }  else {
               echo $str;
               }
    ?>
    Molto semplice ma efficace. Ovviamente uno user potra' inserire tutte le stringhe identiche
    che vorra' se questo e' lecito o necessario.

    In pratica serve ad impedire che il refresh reinserisca lo stesso dato o che lo user per
    errore reinserisca lo stesso dato per distrazione, opzione questa ottenibile
    togliendo la riga con INPUT TYPE = HIDDEN come detto nel commento iniziale.

    Se si vuole invece l'univocita' del dato o di piu' dati inseriti, il controllo dovra'
    tener conto di tutta la tabella e non solo dell'ultimo inserimento fatto.
    Non e' questo il nostro scopo.

    I coockies devono essere abilitati (per uso della sessione), e le sessioni funzionare.

    Come avete potuto riscontrare si tratta di utilizzare 4-5 righe di script assolutamente
    standardizzate, cioe' uguali per qualunque form di inserimento che utilizza una o piu' pagine.

    Mi sembra sia tutto facile e self-explanatory ... ma nel caso servano chiarimenti
    non esitate a chiedere oppure a suggerire soluzioni alternative.

    Ciao.

    Il silenzio è spesso la cosa migliore. Pensa ... è gratis.

  2. #2

  3. #3
    Originariamente inviato da bubu77
    aggiunta
    Ok...!

    Pensi possa servire un completamento alla pillola date/time per la versione 4.1 di mysql... ??? oppure e' prematura? Ci sono un frego di novita'.


    Il silenzio è spesso la cosa migliore. Pensa ... è gratis.

  4. #4
    bella l'idea.. ma mi sembra eccessivo l'uso di $caso... in che situazioni può essere utile reinserire gli stessi dati?

    cmq l'idea di fare un md5 di $_POST è ottimo


    ma tenere in sessione il timstamp dell'ultimo inserimento ed impedire inserimeti troppo ravvicinati non va bene uguale?


  5. #5
    Originariamente inviato da piero.mac
    Ok...!

    Pensi possa servire un completamento alla pillola date/time per la versione 4.1 di mysql... ??? oppure e' prematura? Ci sono un frego di novita'.

    falla falla.. servono sempre le pillole

  6. #6
    Originariamente inviato da }gu|do[z]{®©
    bella l'idea.. ma mi sembra eccessivo l'uso di $caso... in che situazioni può essere utile reinserire gli stessi dati?

    cmq l'idea di fare un md5 di $_POST è ottimo


    ma tenere in sessione il timstamp dell'ultimo inserimento ed impedire inserimeti troppo ravvicinati non va bene uguale?

    Il $caso e' una trovata dell'ultima ora. Ho fatto provare lo script ad un amico. Questi mi ha osservato:

    Ma se io volessi o dovessi inserire dei doppioni per qualunque ragione, per esempio per acquisire degli id, lo script me lo impedirebbe.

    Allora ho pensato di associare un valore random in campo hidden. Se lo script arriva dal form il valore random cambia, se arriva dal refresh rimane identico.

    MD5() modifica il valore e permette l'inserimento. Ovvio che e' un opzional. Basta togliere il campo hidden e ciao peppina... blocca tutti gli inserimenti identici.

    Il timestamp potrebbe andare bene, ma devi fare query, calcolare, e non avrebbe uno standard di funzionamento. Serve una colonna in piu'. Ci avevo pensato, ma poi e' nata l'idea che ho realizzato.

    Il silenzio è spesso la cosa migliore. Pensa ... è gratis.

  7. #7
    Originariamente inviato da piero.mac
    Il $caso e' una trovata dell'ultima ora. Ho fatto provare lo script ad un amico. Questi mi ha osservato:

    Ma se io volessi o dovessi inserire dei doppioni per qualunque ragione, per esempio per acquisire degli id, lo script me lo impedirebbe.

    Allora ho pensato di associare un valore random in campo hidden. Se lo script arriva dal form il valore random cambia, se arriva dal refresh rimane identico.

    MD5() modifica il valore e permette l'inserimento. Ovvio che e' un opzional. Basta togliere il campo hidden e ciao peppina... blocca tutti gli inserimenti identici.
    sì sì.. per carità.. mi chiedevo solo che utilità avesse consentire inserimenti identici... se poi uno ne ha esplicita esigenza.. è perfetto quel che dici


    Il timestamp potrebbe andare bene, ma devi fare query, calcolare, e non avrebbe uno standard di funzionamento. Serve una colonna in piu'. Ci avevo pensato, ma poi e' nata l'idea che ho realizzato.
    no no.. intendo.. tieni in sessione il timestamp dell'ultimo inserimento... e confronti con il timestamp corrente prima di inserire.. se non sono passati tot secondi o minuti [de gustibus] non fa inserire.. che funziona sia da antiflood che da antorefresh.. no?

    e chiaramente permette inserimenti identici.. ma non con il refresh..

  8. #8
    Originariamente inviato da }gu|do[z]{®©
    no no.. intendo.. tieni in sessione il timestamp dell'ultimo inserimento... e confronti con il timestamp corrente prima di inserire.. se non sono passati tot secondi o minuti [de gustibus] non fa inserire.. che funziona sia da antiflood che da antirefresh.. no?

    e chiaramente permette inserimenti identici.. ma non con il refresh..
    Ok.. Capito. Quindi qualcosa tipo quel che fa il forum che ti impedisce di reiterare post prima dei 30 secondi....

    Solo che impedisce non solo la reiterazione, ma anche la continuita' del lavoro. Dovrei sempre aspettare il tempo del time out per poterne inserire un successivo. Ho una mentalita' piu' intranettiana che internettista (inter.. sic! ).

    E' comunque una soluzione da tenere in considerazione quantomeno come antiflood...

    Il silenzio è spesso la cosa migliore. Pensa ... è gratis.

  9. #9
    Originariamente inviato da piero.mac
    Ok.. Capito. Quindi qualcosa tipo quel che fa il forum che ti impedisce di reiterare post prima dei 30 secondi....

    Solo che impedisce non solo la reiterazione, ma anche la continuita' del lavoro. Dovrei sempre aspettare il tempo del time out per poterne inserire un successivo. Ho una mentalita' piu' intranettiana che internettista (inter.. sic! ).

    E' comunque una soluzione da tenere in considerazione quantomeno come antiflood...
    beh chiaramente dipende dalla situazione.. ma se metti 5 secondi penso che vada bene per una web-application.. insomma.. dove il refresh non si fa per dolo ma per distrazione.... chiaramente uno che vuole farlo per dolo se capisce che dopo un tot può refreshare.. aspetta...

    mi sembra una soluzione semplice per i re-inserimenti accidentali..

  10. #10
    Complimenti... ottima pillola

    La soluzione adottata per verificare i dati di $_POST attraverso MD5() e serialize() mi piace!
    Il codice è performante ed essenziale.

    Bravo!

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.