2012-08-27 14 views
5

Lasciatemi iniziare con una piccola introduzione. Sono in procinto di imparare OOP in PHP. Ho anche studiato modelli di design, ma non ho ancora compreso appieno i diversi tipi di concetti. Sono al livello in cui ogni pochi mesi mi rendo conto che non sto facendo le cose nel modo giusto e devo cambiare il mio stile. Questo è così frustrante. Perciò vorrei scoprire il modo corretto di fare le cose una volta per tutte. Ho cercato di leggere integralmente su StackOverflow sui seguenti argomenti:Qual è il modo corretto di impostare le mie classi nella programmazione OOP in PHP?

ORM
Mapper dati
Singleton
Globali sono il male
Tutto ciò che riguarda

Tuttavia io non sono ancora chiari su un paio di cose. Sto postando il mio codice qui in modo chiaro e conciso e spero che le persone possano indicare entrambe le le buone pratiche e quelle cattive. Elencherò tutte le mie domande alla fine.

Si prega di non chiudere come un duplicato, ho cercato onestamente attraverso quasi tutte le domande sull'argomento, ma voglio ancora sapere alcune cose che non sono stato in grado di chiarire. Scusate, è così lungo ma ho cercato di organizzarlo, quindi dovrebbe leggere bene!

Inizierò pubblicando gli elementi essenziali della mia classe Database.

database.php

<?php 


class DatabaseMySQL{ 

    private static $dbh; 

    public function __construct(){ 
     $this->open_connection(); 
    } 

    public function open_connection(){ 
     if(!self::$dbh){ 
      return (self::$dbh = new PDO(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME, DB_USER,DB_PASSWORD)) ? true : false; 
     } 
     return true; 
    } 

    public function query($sql, $params=array()){ 
     $this->last_query = $sql; 
     $stmt = self::$dbh->prepare($sql); 
     $result = $stmt->execute($params); 
     return $result ? $stmt : $stmt->errorInfo(); 
    } 

    public function fetch_all($results, $class_name=''){ 
     return $results->fetchAll(PDO::FETCH_CLASS, $class_name); 
    } 

} 

?> 

Questo è il mio file di classe di database. Questa classe mi consente di creare tutte le istanze che mi piacciono di questa classe e riutilizzerà l'oggetto PDO già istanziato memorizzato come proprietà statica della classe. Inoltre recupera i dati dal set di risultati utilizzando PDO per ottenere i dati come oggetti di una classe specificata.

Il mio prossimo file che ho è la mia classe da cui ereditano tutte le mie altre classi. L'ho chiamato MainModel. Non ho idea se questo segue la convenzione o no.

MainModel.php

<?php 



abstract class MainModel{ 

    protected static $table; 

    public function __construct($array=array()){ 
     $this->assign_known_properties($array); 
    } 

    public function assign_known_properties($array){ 
     foreach($array as $key=>$value){ 
      $this->$key = $value; 
     } 
    } 

    public static function find_by_id($id){ 
     $db = new DatabaseMySQL(); 
     self::intialise_table_name(); 
     $id = (int) $id; 
     $sql = "SELECT * FROM ".static::$table." "; 
     $sql .= "WHERE id = {$id} ";  
     $result = self::find_by_sql($sql);  
     return array_shift($result); 
    } 

    public static function find_all(){ 
     $db = new DatabaseMySQL(); 
     self::intialise_table_name(); 
     $sql = "SELECT * FROM ".self::$table." "; 
     return self::find_by_sql($sql); 
    } 

    public static function fetch_as_objects($results){ 
     $db = new DatabaseMySQL(); 
     $called_class = get_called_class(); 
     $results = $db->fetch_all($results, $called_class); 
     return $results; 
    } 

    public static function find_by_sql($sql){ 
     $db = new DatabaseMySQL(); 
     $results = $db->query($sql); 
     return $results ? self::fetch_as_objects($results) : false; 
    } 

    public static function intialise_table_name(){ 
     $called_class = get_called_class(); 
     static::$table = strtolower($called_class).'s'; 
    } 

    public function get_table_fields(){ 
     self::intialise_table_name(); 
     $sql = "SHOW FIELDS FROM ".static::$table." "; 
     return self::find_by_sql($sql); 
    } 

    public function set_table_details(){ 
     $fields = $this->get_table_fields(); 
     $total = count($fields); 
     $array = array(); 
     foreach($fields as $object){ 
      $array [] = $object->Field; 
     } 
     $this->table_details = array('objects'=>$fields,'array'=>$array,'total'=>$total); 
     $this->set_placeholders_for_new_record(); 
     $this->set_properties_as_array(); 
     $this->set_properties_as_array(true); 
    } 

    public function set_properties_as_array($assoc=false){ 
     $array = array(); 
     if (!$assoc){ 
      foreach($this->table_details['array'] as $field){ 
       if(isset($this->$field)){ 
        $array [] = $this->$field; 
       }else{ 
        $array [] = NULL; 
       } 
      } 
      $this->table_details['values'] = $array; 
     }else{ 
      foreach($this->table_details['array'] as $field){ 
       if(isset($this->$field)){ 
        $array[$field] = $this->$field; 
       }else{ 
        $array [$field] = NULL; 
       } 
      } 
      $this->table_details['assoc_values'] = $array; 
     } 
    } 

    public function set_placeholders_for_new_record(){ 
     $string = ''; 
     for($i=0; $i<$this->table_details['total']; $i++){ 
      $string .= '? '; 
      if(($i+1) != $this->table_details['total']){ 
       $string .= ", "; 
      } 
     } 
     $this->table_details['placeholders'] = $string; 
    } 

    public function create(){ 
     $db = new DatabaseMySQL(); 
     $this->set_table_details(); 
     $sql = "INSERT INTO ".static::$table." "; 
     $sql .= " VALUES({$this->table_details['placeholders']}) "; 
     $result = $db->query($sql, $this->table_details['values']); 

     // If array is returned then there was an error. 
     return is_array($result) ? $result : $db->insert_id(); 
    } 

    public function update(){ 
     $db = new DatabaseMySQL(); 
     $this->set_table_details(); 
     $sql = "UPDATE ".static::$table." "; 
     $sql .= " SET "; 
      $count = 1; 
      foreach($this->table_details['array'] as $field){ 
       $sql .= "{$field} = :{$field} "; 
       if($count < $this->table_details['total']){ 
        $sql .= ", "; 
       } 
       $count++; 
      } 

     $sql .= " WHERE id = {$this->id} "; 
     $sql .= " LIMIT 1 "; 
     $result = $db->query($sql, $this->table_details['assoc_values']); 
     return $result; 
    } 

    public function save(){ 
     return isset($this->id) ? $this->update() : $this->create(); 
    } 
} 


?> 

In sintesi questo file. Io uso metodi statici come find_by_id($int) che generano oggetti della classe chiamata in modo dinamico. Sto usando Late Static Bindings per accedere al nome della classe chiamata e io uso lo $stmt->fetchAll(PDO::FETCH_CLASS, $class_name) per istanziare questi oggetti con i dati dal database convertiti automaticamente in oggetti.

In ogni metodo statico istanziato un'istanza della classe DatabaseMySQL. In ogni metodo statico ho impostato il nome statico $table corretto da utilizzare nelle query SQL dinamicamente ottenendo il nome della classe e aggiungendo un s ad esso. Quindi se la mia classe fosse User, ciò renderebbe il nome della tabella users.

Nel mio costrutto ho inserito un array opzionale che può essere utilizzato per inserire alcune variabili come proprietà di un oggetto mentre viene creato. In questo modo tutto è fatto in modo dinamico che mi lascia nella fase finale del mio progetto. Le mie lezioni di ereditarietà.

User.php

class User extends MainModel{ 

} 

Question.php

class Question extends MainModel{ 

} 

Quello che faccio ora è semplice. Posso dire:

$user = new User(array('username'=>'John Doe')); 
echo $user->username; // prints *John Doe* 
$user->save(); // saves (or updates) the user into the database. 

Posso recuperare un utente con una chiamata statica all'utente.

$user = User::find_by_id(1); 
echo $user->username; // prints users name 

Così ora le mie domande:

  • 1) Che nome vuoi chiamare questo modello di progettazione (se ancora è uno a tutti) .Data Mapper? Iniezione di dipendenza? Modello di dominio (qualunque cosa sia)? Livello di accesso ai dati?

  • 2) Così com'è ora questa implementazione è considerata ben strutturata?

  • 3) Se è buono, esiste una convenzione di denominazione, dovrei essere consapevole di ciò che manca nel mio codice?

  • 4) Se è considerato buono, ti dispiacerebbe precisare cosa ti è piaciuto in particolare, quindi saprò quale parte conservare definitivamente?

  • 5) Se non pensi che sia opportuno ti piacerebbe dare una spiegazione dettagliata perché è così?

  • 6) Se la mia create, update, delete che sono tutti i metodi di miei oggetti essere nella stessa classe come la mia find_by_id, find_all che vengono chiamate solo staticamente e in realtà restituire oggetti. Se dovrebbero essere in due classi diverse, come faccio a farlo?

  • 7) Perché tutti gli altri usano le funzioni $load->('UserClass') e le parole di fantasia come i mapper e non ho ancora avuto bisogno di loro una volta?

+4

Questo è più adatto qui: http://codereview.stackexchange.com/ –

+1

@IliaRostovtsev grazie non lo sapevo, ma io ancora voglio una risposta dagli esperti e penso che siano principalmente qui! –

+3

No.Non limitarti a leggere gli argomenti (e sconsiglio di usare SO come riferimento _primary_). Fai esperienza, non aver paura di sporcarti le mani (questa domanda è un buon inizio, comunque). Sappi che non esiste il bianco e nero. Non conosco il contesto, ma i globali non sono necessariamente malvagi, solo orribili se usati male. Stessa cosa per ORM e Singleton e, beh, tutto --- a volte sono buone pratiche ma non sempre le migliori. – skytreader

risposta

1

La soluzione che hai creato si chiama "Active Record"; per i vantaggi e gli svantaggi, è possibile leggere Patterns of Enterprise Architecture di Martin Fowler e molte delle conversazioni che si possono trovare su Internet.

Fwiw, il mio punto di vista personale è che questo è un modo perfetto per costruire applicazioni basate su database in cui la logica aziendale primaria è la lettura e la scrittura nel database; tende a diventare un po 'gravoso quando si costruisce una logica aziendale più complessa.

Inoltre, Stack Overflow non è per domande di stile "per favore discuti questo argomento" - è per le risposte a cui esiste una risposta obiettivamente vera. Quindi, la tua domanda 1 si adatta, ma gli altri non sono proprio adatti per SO ...

+0

Grazie Neville per aver dedicato del tempo, non pensavo che la mia domanda fosse la discussione su questo argomento, e sulla domanda 3 e sulla domanda 6? –

+0

@InGodITrust Forse dovresti chiedere a Dio = p. Ok, risponderò alle domande 3 e 6: per la convenzione sui nomi e il namespace, leggi [PSR-0] (https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) , che è lo standard adesso. Riguardo ai tuoi metodi, è giusto averli tutti nella stessa classe. Dovresti anche pensare a separare il DBAL dall'ORM. È necessario separare la parte di connessione, dalla parte di creazione della query e dalla parte entità/relazioni. Questo va bene per l'apprendimento, l'ho fatto anch'io. Ma per progetti reali dovresti usare librerie ben testate come Doctrine2 o Symfony2. – ChocoDeveloper

+0

@ChocoDeveloper gliel'ho chiesto e sto ancora aspettando una risposta. Grazie per i suggerimenti, ottimo link che hai dato! –

Problemi correlati