2012-08-27 17 views
5

Permítanme comenzar con una pequeña introducción. Estoy en el proceso de aprender OOP en PHP. También investigué Patrones de diseño pero aún no he comprendido completamente los diferentes tipos de conceptos. Estoy en la etapa en la que cada pocos meses me doy cuenta de que no estoy haciendo las cosas correctamente y tengo que cambiar mi estilo. Esto es muy frustrante. Por lo tanto, me gustaría descubrir la forma correcta de hacer las cosas de una vez por todas. He tratado de leer completamente arriba en Stackoverflow sobre los siguientes temas:¿Cuál es la forma correcta de configurar mis clases en programación OOP en PHP?

ORM
Mapper datos
Singleton
Globals son malos
Todo lo relacionado

Sin embargo todavía no estoy claro acerca de algunas cosas. Estoy publicando mi código aquí de una manera clara y concisa y espero que las personas puedan señalar ambas las buenas prácticas y las malas. Enumeraré todas mis preguntas al final.

Por favor, no cierre como un duplicado, honestamente he buscado en casi todas las preguntas sobre el tema, pero todavía quiero saber algunas cosas que no he podido aclarar. Lo siento, es tan largo, pero he intentado organizarlo, ¡así que debería leer bien!

Comenzaré publicando los elementos esenciales de mi clase de base de datos.

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); 
    } 

} 

?> 

Este es mi archivo de clase de base de datos. Esta clase me permite crear tantas instancias como quiera de esta clase y reutilizará el objeto PDO ya instanciado almacenado como una propiedad estática de la clase. También obtiene los datos del conjunto de resultados utilizando PDO para obtener los datos como objetos de una clase especificada.

Mi siguiente archivo que tengo es mi clase de la que todas mis otras clases heredan. Lo he llamado MainModel. No tengo idea si esto sigue la convención 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(); 
    } 
} 


?> 

Para resumir este archivo. Utilizo métodos estáticos como find_by_id($int) que generan dinámicamente objetos de la clase llamada. Estoy utilizando enlaces estáticos tardíos para obtener acceso al nombre de la clase llamada y utilizo el $stmt->fetchAll(PDO::FETCH_CLASS, $class_name) para crear una instancia de estos objetos con los datos de la base de datos convertidos automáticamente en objetos.

En cada método estático instalo una instancia de la clase DatabaseMySQL. En cada método estático establezco el nombre estático correcto $table que se utilizará dinámicamente en las consultas SQL obteniendo el nombre de la clase y añadiéndole un s. Entonces, si mi clase fuera User, eso haría que la tabla nombre users.

En mi constructer he colocado una matriz opcional que se puede utilizar para insertar algunas variables como propiedades de un objeto a medida que se crea. De esta forma, todo se hace de forma dinámica, lo que me deja en la etapa final de mi proyecto. Mis clases de herencia.

User.php

class User extends MainModel{ 

} 

Question.php

class Question extends MainModel{ 

} 

Lo que hago ahora es simple. Puedo decir:

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

Puedo recuperar a un usuario con una llamada estática al usuario.

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

Así que ahora por mis preguntas:

  • 1) ¿Qué nombre se llama a este patrón de diseño (si es que es uno en absoluto) .Data Mapper? ¿Inyección de dependencia? Modelo de dominio (lo que sea que sea)? Capa de acceso a datos?

  • 2) Tal como está ahora, ¿esta implementación se considera bien estructurada?

  • 3) Si es bueno, es que hay una convención de nomenclatura que debería ser consciente de que no se encuentra en mi código?

  • 4) Si se considera bueno, ¿te importaría señalar qué es lo que más te gustó, así sabré qué parte guardar definitivamente?

  • 5) Si usted no piensa que es bueno ¿le importaría dar una explicación detallada por qué esto es así?

  • 6) En caso de que mi create, update, delete que son todos los métodos de los objetos de mi estar en la misma clase que mi find_by_id, find_all que sólo se llama estáticamente y en realidad devolver objetos. Si deberían estar en dos clases diferentes, ¿cómo hago eso?

  • 7) ¿Por qué todos los demás están usando $load->('UserClass') funciones y palabras elegantes como mapeadores y todavía no las he necesitado?

+4

Esto se ajusta más aquí: http://codereview.stackexchange.com/ –

+1

@IliaRostovtsev gracias no lo sabía, pero todavía quiero una respuesta de los expertos y creo que están principalmente aquí! –

+3

No.No solo lea sobre los temas (y desaconsejo usar SO como referencia_primaria_). Obtenga experiencia, no tenga miedo de ensuciarse las manos (esta pregunta es un buen comienzo, sin embargo). Sepa que no hay blanco y negro. No conozco el contexto, pero los globales no son necesariamente malvados, simplemente horribles si se usan mal. Lo mismo para ORM y Singleton y, bueno, todo, a veces son buenas prácticas pero no siempre las mejores. – skytreader

Respuesta

1

La solución que ha encontrado se llama "Active Record"; para conocer los beneficios y desventajas, puede leer el libro de Martin Fowler Patterns of Enterprise Architecture, así como muchas de las conversaciones que puede encontrar en Internet.

Fwiw, mi punto de vista personal es que esta es una forma perfecta de construir aplicaciones basadas en bases de datos donde la lógica de negocios primaria es leer y escribir en la base de datos; tiende a ser un poco oneroso cuando se construye una lógica comercial más compleja.

Además, el desbordamiento de pila no es para "Por favor discutir este tema" Cuestiones de estilo - es en busca de respuestas a las que hay una respuesta objetivamente cierto. Por lo tanto, su pregunta 1 encaja, pero los otros no son realmente adecuados para SO ...

+0

Gracias a Neville por tomarse el tiempo, no pensé que mi pregunta era la de debatir este estilo de tema, ¿y las preguntas 3 y 6? –

+0

@InGodITrust Tal vez deberías preguntarle a Dios = p. Ok, responderé 3 y 6: para nombrar convenciones y espacios de nombres, lee sobre [PSR-0] (https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) , que es el estándar en este momento. Acerca de sus métodos, está bien tenerlos a todos en la misma clase. También debería pensar en separar el DBAL del ORM. Debe separar la parte de conexión de la parte de creación de consulta y la parte de entidades/relaciones. Todo esto es bueno para aprender, yo también lo hice. Pero para proyectos reales, debe usar bibliotecas bien probadas, como Doctrine2 o Symfony2. – ChocoDeveloper

+0

@ChocoDeveloper Le pregunté y todavía estoy esperando una respuesta. Gracias por los consejos, gran enlace que diste! –

Cuestiones relacionadas