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 mifind_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?
Esto se ajusta más aquí: http://codereview.stackexchange.com/ –
@IliaRostovtsev gracias no lo sabía, pero todavía quiero una respuesta de los expertos y creo que están principalmente aquí! –
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