Visualizzazione dei risultati da 1 a 8 su 8
  1. #1

    dubbio implementativo (OOP + transazioni)

    Salve a tutti avrei un dubbio implementativo, stò aggiornando un mio script in OOP e mi sorge un problema con le transazioni. Io vorrei fare in modo che quando creo un'istanza della classe (passando l'id dell'utente) questa nel costruttore mi blocchi l'id dell'utente da eventuali richieste al DB e che nel distruttore me la sblocchi. Ecco un esempio di codice così rendo meglio l'idea:

    Codice PHP:
    <?php
    class user
    {
        private 
    $_id;
        private 
    $_soldi;
        public  
    $DB;
        
        function 
    __construct($id$DB)
        {
            
    $this->_id    = (int) $id;
            
    //Uso una classe esterna per manipolare il DB 
            
    $this->DB     $DB;
            
    //Blocco l'utente qui'
            
    $data         $DB->row("SELECT soldi FROM user WHERE id = '".$id."' LIMIT 1");
            
    //Se l'utente non esiste blocco tutto ovviamente
            
    $this->_soldi $data['soldi'];
        }
        
        function 
    getSoldi()
        {
            return 
    $this->_soldi;
        }
        
        function 
    setSoldi($amount)
        {
            if(
    $this->getSoldi >= $amount)
            {
                
    //Faccio l'update
                
    $update $this->DB->update("user",array('soldi' => 'soldi -'.$amount),"id = '".$this->_id."'");
                
    //Se va tutto bene aggiorno
                
    $this->_soldi -= $amount
            }
        }
        
        function 
    __destructor()
        {
            
    //Sblocco l'utente qui
        
    }
    }

    $objUser = new User(1,$db);

    echo 
    "L'utente ha ora ".$objUser->getSoldi();

    $tolgo_soldi $objUser->setSoldi(100);

    echo 
    " ed ora dopo avergli tolto 100 sacchi ".$objUser->getSoldi();
    ?>
    Qualche idea ?

  2. #2
    una domanda ed una osservazione:
    1) a che ti serve bloccare quell'id da altre letture? (domanda)
    2) per me la tua classe "user" fa più cose di quelle che deve fare: cosa centra il db con l'utente?! (osservazione)

    secondo me avresti dovuto avere qualcosa del tipo:
    Codice PHP:
    class db {
     ...
    }

    class 
    user {
     ...
    }

    class 
    loadUserDb {
      public function 
    loadUserDb($db) {
         ...
      }

      public function 
    load(id) {
         ...
         return 
    "una istanza della classe user";
      }

    o cmq qualcosa di simile, xkè vorrendo rimanere un pelino più astratti la classe "loadUserDb" la si potrebbe suddividere utilizzando tipo una interfaccia + una classe con qualcosa di questo genere:
    Codice PHP:
    interface loadUser {
      public function 
    load($id);
    }

    class 
    loadUserDB implements loadUser {...}
    class 
    loadUserFile implements loadUser {...}
    class 
    loadUserSession implements loadUser {...} 
    la sostanza è che alla classe "user" non interessa niente sapere se esiste o meno un database...lei deve essere una astrazione dell'oggetto utente che ha un id, nome, cognome, email, etc...
    Administrator of NAMDesign.Net

  3. #3
    1) Mi serve perchè aime mi è capitato che vi siano doppie scritture e valori negativi, nel senso che mi sono ritrovato con due decrementi di "soldi" anche se il secondo non poteva avvenire, in sostanza il motivo per cui servono le transizioni in mysql:
    1 - "vedi" la risorsa (la select del mio esempio)
    2 - La blocchi così nel frattempo nessuno la può usare (e quindi ad esempio decrementare)
    3 - La manipoli (e quindi la decrementi se il suo valore te lo permette, come nell'esempio da me scritto)
    4 - La sblocchi e permetti ad eventuali altre istanze della classe di nascere e manipolare nuovamente l'utente
    Se questo non avviene cosa può succedere (e cosa mi è successo, facendomi letteralmente impazzire negli ultimi 6 mesi) che viene eseguita una query che fa andare il valore a meno zero e siccome il campo è unsigned mi ritrovavo il valore massimo rappresentabile nel campo. Se ti interessa approfondire:
    http://bugs.mysql.com/bug.php?id=14543

    2) Ho iniziato a scrivere in OOP solo di recente quindi non conosco bene tutte le meccaniche, comunque per rispondere alla tua domanda, faccio così perchè all'apperenza mi sembra rindondante usare il metodo da te citato, se quel codice tanto serve solo alla classe utente perchè lo devo dividere da essa ? Oltre tutto le query sull'utente le uso in tutta la classe e quindi l'oggetto DB mi serve non solo nel costruttore. Dico questo con tutta l'umiltà possibile eprchè non ne sò un granchè di regole di OOP, quindi se magari tu potessi spiegare meglio l'utilità di quel codice, o linkarmi una guida che spieghi queste "regole di stile" o "buona programmazione in OOP" te ne sarei comunque grato.

    In ogni caso è possibile fare quello che ho cheisto nel primo messaggio ? cioè bloccare eventuali modifiche (l'ideale sarebbe anche le letture) a un record del DB nel costruttore di una classe e sbloccarla nel distruttore ?
    O se non è possibile c'è qualche altro metodo ? ad esempio non permettere che vengano create altre istanze di una classe istanziata con un id (stò sparando)?

  4. #4
    Allora,

    in merito al locking del record ti suggerisco questo link in mysql reference: http://dev.mysql.com/doc/refman/5.0/...ing-reads.html dove spiega come bloccare dei record per un uso esclusivo nella sessione che si sta eseguendo sul db._

    Detto questo, il pattern... la classe Db deve avere tutti i metodi necessari per poter eseguire operazioni sul database, comprensive anche per fare il locking. La classe user deve sapere poter usare la classe db per aggiornare lo stato dell'entità nel database. Detto questo, mi pare che piu o meno ci siamo... l'unica cosa è che magari userei una classe Singleton che restituisca l'istanza di DB da usare dentro a user, piuttosto che passare db al costruttore di user.. magari una bella classe Model da cui eredita User con metodo protetto getDB che richiama la classe singleton e ritorna l'istanza di DB precaricata...
    IP-PBX management: http://www.easypbx.it

    Old account: 2126 messages
    Oldest account: 3559 messages

  5. #5
    allora ho modifcato setSoldi()
    Codice PHP:
    function setSoldi($amount)
        {
            
    //E' rindondate ma a quanto pare non c'è altro modo...
            
    $data  $DB->row("BEGIN;
                               SELECT soldi FROM user WHERE id = '"
    .$id."' LIMIT 1 FOR UPDATE;");
                
            
    $update false;
            if(
    $data['soldi'] >= $amount)
            {
                
    $update $this->DB->update("user",array('soldi' => 'soldi -'.$amount),"id = '".$this->_id."'");
            }
            if(
    $update)
            {
                
    //Se va tutto bene aggiorno
                
    $commit        $this->DB->query("COMMIT;");
                
    $this->_soldi -= $amount;
            }
            else
            {
                
    //Se no rollback, ma poi a che serve mica stò facendo un insert..
                //nel select non dovrei aver bloccato il record ? come potrebbe fallire ?
                //e se non mi serve a una mazza il rollback a stò punto potrei lasciare
                //il tipo Myisam della tabella visto che l'unica differenza con InnoDB
                //stà nel fatto che non supporta il rollback ?
                
    $rollback =  $this->DB->query("ROLLBACK;");
            }
        } 
    Andrebbe bene questo codice ? Come ho detto è rindondate visto che mi costrige a fare 2 query, quando potevo farne 1 in tutta la classe. Inoltre potreste risondere alle osservazioni che ho scritto nei commenti del codice ?


    l'unica cosa è che magari userei una classe Singleton che restituisca l'istanza di DB da usare dentro a user, piuttosto che passare db al costruttore di user.. magari una bella classe Model da cui eredita User con metodo protetto getDB che richiama la classe singleton e ritorna l'istanza di DB precaricata...
    Non ho capito niente, potresti spiegarti meglio ? Singleton ? modello ? Più che altrò vorrei capire il vantaggio implementativo. Se un metodo come quello che proponete dà maggiore astrazione capirei ma se devo dividere un pezzo della classe in un'altro e poi alla fine comuqnue User necessita di un qualcosa che gli istanzia un oggetto per manipolare il DB questo a che serve ?

  6. #6
    Originariamente inviato da Shinji 89
    Non ho capito niente, potresti spiegarti meglio ? Singleton ? modello ? Più che altrò vorrei capire il vantaggio implementativo. Se un metodo come quello che proponete dà maggiore astrazione capirei ma se devo dividere un pezzo della classe in un'altro e poi alla fine comuqnue User necessita di un qualcosa che gli istanzia un oggetto per manipolare il DB questo a che serve ?
    Serve perchè User deve essere il piu staccato possibile dal database... serve? non serve? vantaggi? svantaggi? dipende da quanto ti interessa a te rispettare una determinata filosofia e avere un modello di procedimento che da molti vantaggi soprattutto in termini di quantità di codice scritto, riusabilità e mantenimento del codice scritto...

    per quanto riguarda l'osservazione nel codice:

    la transazione può comunque fallire, mica perchè il record è locked questa debba cmq andare a buon fine.
    IP-PBX management: http://www.easypbx.it

    Old account: 2126 messages
    Oldest account: 3559 messages

  7. #7
    Originariamente inviato da Shinji 89
    ...
    quindi se magari tu potessi spiegare meglio l'utilità di quel codice, o linkarmi una guida che spieghi queste "regole di stile" o "buona programmazione in OOP" te ne sarei comunque grato.
    ...
    quello che intendevo dire è che tendenzialmente nella programmazione ad oggetti si cerca di rendere le varie classi:
    1) "indipendenti" dalle altre (per quanto possibile ma senza complicare troppo le cose)
    2) complete

    creare, secondo me, una classe user fortemente legata al database implica che tu per usare quella classe dovrai sempre istanziare anche la classe db; questo potrebbe anche andar bene nel caso del web (dipende molto da come è strutturato il progetto) ma in linea generale è una dipendenza inutile.

    un utente prescinde dal come vengono pescati i dati per crearlo, come ho detto prima magari in questo progetto ti serve prendere i dati dal db ma nel prossimo potresti volerli pescare da file, da socket, da sessione...e dovresti riscrivere la tua classe user perchè al momento troppo vincolata al db.

    anche se dietro ad un "user" ci sono più domande da farsi rispetto a queste perchè in genere in un sito internet con autenticazione non è sempre facile individuare cosa deve poter fare la clase "user" e cosa no, difatti:
    1) Zend Framework (se non mi sbaglio) non fornisce una vera classe "user" ma un insieme di classi per il controllo dei permessi e dell'autenticazione
    2) Joomla Framework invece possiede esplicitamente una classe "user" ma per crearla prima si prendono i dati e poi li si passa alla classe (che a quel punto non sa da dove provengono) e le varie operazioni sull'utente vengono fatte da altre classi.
    Codice PHP:
    // "generate" a new JUser Object
    $user JFactory::getUser(0); // it's important to set the "0" otherwise your admin user information will be loaded
     
    $data = array(); // array for all user settings
     
    ...
     
    // set up the "main" user information
     
    $data['name'] = $firstname.' '.$lastname// add first- and lastname
    $data['username'] = $username// add username
    $data['email'] = $email// add email
    $data['gid'] = $acl->get_group_id''$usertype'ARO' );  // generate the gid from the usertype
     
    ...
     
    if (!
    $user->bind($data)) { // now bind the data to the JUser Object, if it not works....
        
    JError::raiseWarning(''JText::_$user->getError())); // ...raise an Warning
        
    return false// if you're in a method/function return false
    }
     
    if (!
    $user->save()) { // if the user is NOT saved...
        
    JError::raiseWarning(''JText::_$user->getError())); // ...raise an Warning
        
    return false// if you're in a method/function return false
    }
     
    return 
    $user// else return the new JUser object 
    Se tu ad esempio la necessità di gestire il credito dell'utente ci sarà qualcosa che altererà questo credito che quindi può essere visto come un oggetto già più consistente...ad esempio nei libri si usa portare esempi del tipo "class Conto { ... }" che modifica le informazioni (bancarie) di un utente.

    La sostanza è che quando si programma OOP bisogna strutturare bene le classi e capire perchè si è scelta quella strada.

    Spero di esserti stato di aiuto.
    Administrator of NAMDesign.Net

  8. #8
    Grazie siete stati entrambi molto chiari, ho capito cosa volevate dire. Appena posso provo la funzione SetSoldi() e vedo se funge, intanto se qualcuno ha altri commenti da fare a riguardo, sono più che graditi!

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.