Ver Mensaje Individual
  #5 (permalink)  
Antiguo 06/04/2018, 18:41
Avatar de hhs
hhs
Colaborador
 
Fecha de Ingreso: junio-2013
Ubicación: México
Mensajes: 2.995
Antigüedad: 10 años, 9 meses
Puntos: 379
Respuesta: Clases POO. Teoría.

Cita:
Estoy comenzando con POO. En internet hay manuales y un sinfín de artículos repetitivos con el funcionamiento básico, pero a la hora de decir aquí estamos y esto quiero conseguir es cuando se nos abre el millón de preguntas. Necesito algún ejemplo avanzado del que aprender (que vaya más allá de la sintaxis propia de definir clases).
Creo que con la explicación de mortiprogramador cubres la parte que corresponde a como hacer las cosas con el lenguaje. Me gustaria como complemento tratar un punto importante que es el diseño orientado a objetos y que se basa en los conceptos básicos ademas de otros principios que se han derivado de la solución y aplicación de los principios básicos que ya debes de saber.

Vamos a usar un ejemplo sencillo; Imagina que tienes que crear una aplicación de recursos humanos para una empresa y uno de los requerimientos es que cuando se contrate a un nuevo Empleado se pueda guardar el nombre junto con un puesto asignado y un salario, ademas debe de ser posible promoverlo a un nuevo puesto con su respectivo cambio de salario.

Muy posiblemente con lo que sabes ahora crees tu clase Empleado :
Código PHP:
Ver original
  1. class Employee
  2. {
  3.     private $name;
  4.     private $position;
  5.     private $salary;
  6.    
  7.     private static $server = 'localhost';
  8.     private static $user = 'miusuario';
  9.     private static $pass = 'micontraseña';
  10.     private static $dbname = 'mibasededatos';
  11.  
  12.  
  13.     public function __get($attribute) {
  14.         if(property_exists($this, $attribute)) {
  15.             return $this->{$attribute};
  16.         }
  17.        
  18.         throw new Exception('No existe esta propiedad');
  19.     }
  20.    
  21.     public function __set($attribute, $value) {
  22.         if(property_exists($this, $attribute)) {
  23.             $this->{$attribute} = $value;
  24.         }
  25.        
  26.         throw new Exception('No existe esta propiedad');
  27.     }
  28.    
  29.     public function save() {
  30.         $conn = mysqli_connect(self::$server, self::$user, self::$pass, self::$dbname);
  31.        
  32.         $query = 'INSERT INTO person (name, position, salary) VALUES (?,?,?)';
  33.         $stmt = mysqli_prepare($conn, $query);
  34.         mysqli_stmt_bind_param($stmt, 'ssd', $this->name, $this->position, $this->salary);
  35.         mysqli_stmt_execute($stmt);
  36.    
  37.         $affected_rows = mysqli_stmt_affected_rows($stmt);                
  38.    
  39.         $stmt->close();
  40.         $conn->close();
  41.    
  42.         return $affected_rows;
  43.     }
  44. }
Creas tu formulario y registras los datos de la siguiente forma (resumida):
Código PHP:
Ver original
  1. $_POST = [
  2.     'name' => 'Enrrique Martinez',
  3.     'position' => 'Jr Developer',
  4.     'salary' => 1024,
  5. ];
  6.  
  7. $employee = new Employee;
  8. $employee->name = $_POST['name'];
  9. $employee->position = $_POST['position'];
  10. $employee->salary = $_POST['salary'];
  11. $enployee->save();
Hasta este punto queda resuelto el problema y funciona, pero no refleja exactamente lo que te pidieron, y comienzas a ver tu código y te das cuenta que puedes usar un constructor para simplificar el código y ademas estas mas cercano a lo que pidieron que se agreguen los datos en el momento que se contrata. Asi que agregas el constructor a tu clase:
Código PHP:
Ver original
  1. public function __construct($data) {
  2.         foreach($data as $attribute => $value) {
  3.             $this->{$attribute} = $value;
  4.         }
  5.     }
y cambias la parte de la contratación con el cambio
Código PHP:
Ver original
  1. $_POST = [
  2.     'name' => 'Enrrique Martinez',
  3.     'position' => 'Jr Developer',
  4.     'salary' => 1024,
  5. ];
  6.  
  7. $employee = new Employee($_POST);
  8. $enployee->save();

Mucho mejor verdad ? Pero te diré que falta en este diseño reflejar mejor la solución del problema así que vamos a abstraer mejor las cosas; si te fijas en la vida real no se crea un empleado se contrata, ademas el empleado se promueve de puesto así que diremos que mi abstracción se representa de esta forma:
Código PHP:
Ver original
  1. interface EmployeeInterface {
  2.    
  3.     public static function hire($data);
  4.    
  5.     public function promote($newPosition, $withNewSalary);
  6.    
  7. }
Una interfaz donde el empleado se promueve y se contrata en lugar solo de "instanciarse" asi que implementemos esta abstracción de nuestro problema con los cambios necesarios en nuestra clase Empleado:
Código PHP:
Ver original
  1. class Employee implements EmployeeInterface
  2. {
  3.     private $name;
  4.     private $position;
  5.     private $salary;
  6.  
  7.     private function __construct($data)
  8.     {
  9.         foreach($data as $attribute => $value) {
  10.             $this->{$attribute} = $value;
  11.         }
  12.     }
  13.  
  14.     public static function hire($data)
  15.     {
  16.         return new self($data);
  17.     }
  18.  
  19.     public function promote($newPosition, $withNewSalary)
  20.     {
  21.         $this->position = $newPosition;
  22.         $this->salary = $withNewSalary;
  23.     }
  24.    
  25.     public function __get($attribute) {
  26.         if(property_exists($this, $attribute)) {
  27.             return $this->{$attribute};
  28.         }
  29.        
  30.         throw new Exception('No existe esta propiedad');
  31.     }
  32.    
  33.     public function __set($attribute, $value) {
  34.         if(property_exists($this, $attribute)) {
  35.             $this->{$attribute} = $value;
  36.         }
  37.        
  38.         throw new Exception('No existe esta propiedad');
  39.     }
  40.    
  41.     public function save() {
  42.         $conn = mysqli_connect(self::$server, self::$user, self::$pass, self::$dbname);
  43.        
  44.         $query = 'INSERT INTO person (name, position, salary) VALUES (?,?,?)';
  45.         $stmt = mysqli_prepare($conn, $query);
  46.         mysqli_stmt_bind_param($stmt, 'ssd', $this->name, $this->position, $this->salary);
  47.         mysqli_stmt_execute($stmt);
  48.    
  49.         $affected_rows = mysqli_stmt_affected_rows($stmt);                
  50.    
  51.         $stmt->close();
  52.         $conn->close();
  53.    
  54.         return $affected_rows;
  55.     }
  56. }
Mi formulario para la contratación queda sin cambio pero necesito ajustar la lógica, así que lo hacemos de la siguiente forma:
Código PHP:
Ver original
  1. $_POST = [
  2.     'name' => 'Enrrique Martinez',
  3.     'position' => 'Jr Developer',
  4.     'salary' => 1024,
  5. ];
  6.  
  7. $employee = Employee::hire($_POST);
  8. // si quieres promover al empleado en otro momento
  9. $employee->prmote('Sr Developer', 2048);
  10. $employee->save();
Ahora es mas claro de leer y de entender por tí y tus colaboradores, ademas si tienes que volver a ver el código sera muy claro también para ti saber que esta pasando.
Con cambios mínimos simplificamos el código y a demás hemos hecho un poco mas flexible el código. Y como es esto ?
Sencillo; Ahora imagina que despues de un tiempo de felicidad llega el cliente y te pide que pases todo esto a Laravel y lo primero que puedes pensar es que tienes que hacer todo de nuevo, o posiblemente no?
Ya que tienes una interfaz en tu solución la puedes usar para resolver la implementación, así que puedes tener facilmente un modelo que resuelva esto en Laravel:
Código PHP:
Ver original
  1. class Employee extends Model implements EmployeeInterface {
  2.    
  3.     protected $fillable = ['name', 'position', 'salary'];
  4.    
  5.     public static function hire($data) {
  6.         return self::create($data);
  7.     }
  8.    
  9.     public function promote($newPosition, $withNewSalary)
  10.     {
  11.         $this->position = $newPosition;
  12.         $this->salary = $withNewSalary;
  13.     }
  14.    
  15. }
Ahora si tu lógica original la pasas a un controlador de Laravel todo va a funcionar igual!
Código PHP:
Ver original
  1. $employee = Employee::hire($_POST);
  2. $employee->promote('Sr Developer', 2048);
Ahora solo te falta ajustar el hecho de que en laravel las variable se reciben usando un objeto Request: asi que lo anterior quedara de la siguiente forma
Código PHP:
Ver original
  1. $employee = Employee::hire($request->all());
  2. $employee->promote('Sr Developer', 2048);
Sencillo no lo crees ?

Es importante tomarte siempre un tiempo para diseñar lo que haces de tal forma que puedas hacer cambios futuros sin tener que rehacer todo de nuevo.
En este ejemplo sencillo usar algunos conceptos de POO nos resolvieron el problema pero existen casos que requieren mas, así que te aconsejo que revises sobre estos temas
  • Objeto
  • Clase
  • Abstracción
  • Encapsulamiento
  • Generalización
  • Polimorfismo
y
  • Diseño orientado a objetos
  • Patrones
  • refactorización
  • Pruebas

Para el asunto de interfaces y clases abstractas puedes leer este hilo aquí mismo en el foro: http://www.forosdelweb.com/f18/difer...erfaz-1108350/
Para finalizar te aliento a que experimentes y tengas paciencia crear código legible y bien diseñado lleva su tiempo, y recuerda; piensa en tu diseño sin los detalles de implementación
__________________
Saludos
About me
Laraveles
A class should have only one reason to change.