martes, 1 de marzo de 2016

Explicando ejemplo de POO con PHP - PARTE 4

1. Respuestas a preguntas frecuentes de la clase DBAbstractModel

1.1 ¿Por qué la clase está definida como abstracta?
Una base de datos, puede tener varios métodos, como insertar datos, editar datos, eliminar datos o simplemente consultar datos. El algoritmo de cada uno de esos métodos no puede definirse con exactitud ¿por qué? Porque cada dato que se inserte, modificque, elimine o consulte, variará en infinidad de aspectos: desde los tipos de datos hasta las tablas y los campos a los que se deba acceder, etc.
Basados en el principio de modularidad de la POO, es necesario tener en cuenta, que HOY, necesito un ABM de usuarios, pero mañana, este requisito puede ampliarse y, debo dejar todo preparado para que el sistema pueda ser escalable y modular (otros módulos pueden ser requeridos en el futuro).
Si solo contemplara los métodos de inserción, edición, consulta y eliminación de usuarios en
esta clase, estaría cometiendo dos errores imperdonable:

1. No estaría respetando el principio de modularidad de la POO
2. Los métodos mencionados ¿no son a caso métodos de un nuevo objeto llamado Usuario?

Por otro lado, por cuestiones de seguridad, se hacía necesario no permitir instanciar esta clase desde ningún lugar de la aplicación y controlar con mucha precaución, el acceso a sus métodos y propiedades, ya que una base de datos, es el "alma" de toda aplicación.

1.2 ¿Para qué declarar propiedades estáticas?
En principio, tanto el host como el usuario y contraseña de la base de datos, en esta aplicación, son únicos. No varían. De allí, que como característica se les asignó ser estáticos, a fin de, por cuestiones de seguridad, no puedan ser modificados dinámicamente (tengamos en cuenta que una clase abstracta debe ser heredada sí o sí para ser utilizada).

1.3 ¿Por qué propiedades estáticas y no constantes de clase?

Porque a las constantes, aunque lógicamente guardan en sí mismas la condición de "estáticas" en el sentido de que "no pueden ser modificadas", pueden ser accedidas desde cualquier clase que herede de la clase que las declare. Pues entonces, la pregunta es ¿Para qué querría una clase "hija" conocer el host, usuario y contraseña de una base de datos?
Es una cuestión nétamente de seguridad, ya que a una constante no se la puede definir como privada y si se la quiere ocultar a las clases "hijas", no quedará otra alternativa que declararlas como propiedades estáticas.

1.5 ¿Con qué sentido declarar las propiedades como protegidas?
Es simple. Algunos módulos pueden necesitar utilizar una base de datos diferente. Todos los módulos, necesitarán queries personalizados para sus consultas. Y lógicamente, todos necesitarán acceder y conocer los resultados de los datos arrojados por una consulta. Es decir, que tanto la propiedad db_name como query y rows, deben tener permisos para ser leídas y modificadas, pero ¡Ojo! Solo por las clases hijas de DBAbstractModel ¿Qué sentido tendría permitir a un programador acceder a estas propiedades si puede ser la clase hija, quien las controle?

1.6 ¿Por qué los métodos open y close_connection son privados?
Pues porque la clase es la única con permiso para abrir y cerrar conexiones a la base de datos. Es una cuestión de control y seguridad.
Si necesitas insertar datos, consultarlos o hacer con ellos otras actividades, tienes métodos protegidos pero no privados, que te permiten hacerlo.

1.7 ¿Por qué los métodos de consulta y ejecución son protegidos y no públicos?
Consultar una base de datos, modificar o eliminar sus datos, así como insertar nuevos, no puede ser una tarea que se deje librada al azar y al libre acceso de cualquiera. Es necesario "proteger" los datos a fin de resguardar su integridad y, la forma de hacerlo, es "proteger" los métodos que se encuentran al servicio de dichos datos.

1.8 ¿Por qué hay métodos declarados como abstractos y además protegidos?
Esta pregunta, en gran parte, la responde la respuesta de la pregunta 1.1. El hecho de que se definan como abstractos, es porque necesariamente DEBEN ser métodos comunes a toda clase que los hereden. Pero solo la clase hija, podrá definirlos (re-definirlos técnicamente hablando) ya que tal cual se
explicó en la pregunta 1.1., solo ellas conocen los requerimientos específicos.
Se declaran además como protegidos, por una cuestión de seguridad ("por las dudas" para que solo puedan ser vistos por clases heredadas). Esto, da lugar a las clases hijas, a que modifiquen su visibilidad de acuerdo al criterio de cada una de ellas.

2. Respuestas a preguntas sobre la clase Usuario
 
2.1 ¿Por qué la clase Usuario es una extensión de DBAbstractModel?
La clase usuario TIENE necesariamente que heredar de DBAbstractModel ya que DEBE utilizar sus métodos (propios y definidos para acceder a la base de datos) y redefinir obligadamente aquellos declarados como abstractos, a fin de continuar un modelo de administración de los ABM (altas, bajas y
modificaciones).

2.2 ¿Con qué fin nombre, apellido e email son propiedades públicas mientras que clave es privada y id, protegida?
Pensemos esto como en la vida real: tu nombre, apellido e e-mail, puede ser necesario para cientos de "trámites" y acciones de tu vida diaria y no andas por la vida negándolos a todo quien te los pida.
Podríamos decir, que no tiene nada de malo que facilites públicamente estos datos, a quien decidas.
Ahora bien ¿le das tu contraseña a cualquiera? Si lo haces, deja de hacerlo. La contraseña es un dato que debe ser privado. Nadie puede tener acceso a él. Es una cuestión de seguirdad.
El número de ID, sería como tu número de documento o pasaporte. Es un dato que debes mantener protegido pero que en algunos casos, te será requerido y necesitarás darlo obligadamente. Por ejemplo, no podrías negarte a mostrar tu pasaporte en migraciones. Sin embargo, DEBES negarte a decirle el PIN de tu tarjeta de crédito a cualquier persona. 

2.3 ¿Por qué se utiliza el método __construct() para modificar el nombre de la base de datos? ¿Por qué no se encarga la clase madre de modificar ese valor?
El nombre de la base de datos, es algo que variará de acuerdo al módulo que lo esté trabajando. Seguramente, de existir en el futuro una clase llamada Producto, ésta, sobreescribirá el valor de db_name. Modificarlo automáticamente utilizando un cosntructor, nos asegura instanciar el objeto con un valor de propiedad adecuado.

2.4 ¿Por qué se utiliza require_once y no include_once?
La diferencia entre include y require es que require, si no puede incluir el archivo al que se está importando, frena la ejecución del script, siendo que include, solo arroja un error, pero continúa la ejecución. Al tratarse de una clase que "requiere" sí o sí, de su clase madre, no tendría sentido permitir que se continúe ejecutando, si no ha podido cargarse el archivo.

3. Respuestas a preguntas sobre el archivo de instancias
 
3.1 ¿Por qué el archivo tiene cuatro instancias al mismo objeto?
Es necesario hacer notar que si bien los objetos comparten propiedades y métodos en común, no todos son idénticos. Es decir, todos los seres humanos tenemos ciertas características en común y realizamos acciones similares. Pero cada uno de nosotros, tiene una única identidad. Por eso, no puede
decirse que se instancia cuadro veces el mismo objeto, porque en realidad, son cuatro objetos diferentes donde cada uno de ellos, tiene una identidad propia. 

Ejemplo de POO con PHP - PARTE 3



Archivo abm_example.php
<?php
require_once('usuarios_model.php');
# Traer los datos de un usuario
$usuario1 = new Usuario();
$usuario1->get('user@email.com');
print $usuario1->nombre . ' ' . $usuario1->apellido . ' existe<br>';
# Crear un nuevo usuario
$new_user_data = array(
'nombre'=>'Alberto',
'apellido'=>'Jacinto',
'email'=>'albert2000@mail.com',
'clave'=>'jacaranda'
);
$usuario2 = new Usuario();
$usuario2->set($new_user_data);
$usuario2->get($new_user_data['email']);
print $usuario2->nombre . ' ' . $usuario2->apellido . ' ha sido creado<br>';
# Editar los datos de un usuario existente
$edit_user_data = array(
'nombre'=>'Gabriel',
'apellido'=>'Lopez',
'email'=>'marconi@mail.com',
'clave'=>'69274'
);
$usuario3 = new Usuario();
$usuario3->edit($edit_user_data);
$usuario3->get($edit_user_data['email']);
print $usuario3->nombre . ' ' . $usuario3->apellido . ' ha sido editado<br>';

# Eliminar un usuario

$usuario4 = new Usuario();
$usuario4->get('lei@mail.com');
$usuario4->delete('lei@mail.com');
print $usuario4->nombre . ' ' . $usuario4->apellido . ' ha sido eliminado';
?>
 

Ejemplo de POO con PHP - PARTE 2



Archivo usuarios_model.php
<?php
require_once('db_abstract_model.php');
class Usuario extends DBAbstractModel {
public $nombre;
public $apellido;
public $email;
private $clave;
protected $id;
function __construct() {
$this->db_name = 'book_example';
}
public function get($user_email='') {
if($user_email != ''):
$this->query = "
SELECT id, nombre, apellido, email, clave
FROM usuarios
WHERE email = '$user_email'
";
$this->get_results_from_query();
endif;
if(count($this->rows) == 1):
foreach ($this->rows[0] as $propiedad=>$valor):
$this->$propiedad = $valor;
endforeach;
endif;
}
public function set($user_data=array()) {
if(array_key_exists('email', $user_data)):
$this->get($user_data['email']);
if($user_data['email'] != $this->email):
foreach ($user_data as $campo=>$valor):
$$campo = $valor;
endforeach;
$this->query = "
INSERT INTO usuarios
(nombre, apellido, email, clave)
VALUES
('$nombre', '$apellido', '$email', '$clave')
";
$this->execute_single_query();
endif;
endif;
}
public function edit($user_data=array()) {
foreach ($user_data as $campo=>$valor):
$$campo = $valor;
endforeach;
$this->query = "
UPDATE usuarios
SET nombre='$nombre',
apellido='$apellido',
clave='$clave'
WHERE email = '$email'
";
$this->execute_single_query();
}
public function delete($user_email='') {
$this->query = "
DELETE FROM usuarios
WHERE email = '$user_email'
";
$this->execute_single_query();
}
function __destruct() {
unset($this);
}
}
?>