New question

Question:

Date: 29-07-2017 08:55:58 (In Spanish)

Opinión y sugerencias sobre clase de conexión a base de datos MySQL[Resolved]

Hola, he estado trabajando con esta clase que implemente para realizar tareas de base de datos con mysql. En general resuelve cualquier problema que he visto durante el tiempo que voy trabajando con PHP y MySQL, espero les sea también de utilidad.
<?php
	require_once('zeusafk.database.lib.php');

	$host = 'localhost';
	$user = 'root';
	$password = 'root';
	$database = 'test_db';
	// El puerto es opcional, por defecto 3306
	$port = 3306;

	// Podemos obtener una instancia compartida de la clase Database sin tener que inicializarla
	$zdb = ZeusAFK\Database::GetInstance();

	// O podemos crear una nueva instancia, lo cual es util si vamos a trabajar con distintas bases de datos
	$zdb = new ZeusAFK\Database();

	// Creamos la conexion a mysql a utilizarse
	$zdb->CreateConnection($host, $user, $password, $database, $port);

	// Podemos verificar si tenemos problemas de conexion u otro tipo
	if(!$zdb->Success()){
		die($zdb->GetError());
	}

	// La funcion principal es Query, esta funcion cuenta con varios parametros opcionales que permiten modificar su funcionalidad para cumplir el objetivo que necesitemos

	/*
		Funcion: Query
		Parametros:
			query [String] [Requerido]: Consulta a ejecutar
				Ejemplo: SELECT * FROM mdl_user WHERE id = ? OR firstname LIKE CONCAT('%',?,'%') LIMIT 1
			
			types [String] [opcional]: Tipos de datos para parametros en la consulta
				Ejemplo: 'is' // (In]t, String)
				Advertencia: La cantidad de parametros en la consulta (?) debe coincidir con los enviados en este parametro

			params [Array|Var] [opcional]: Parametros en la consulta
				Ejemplo: array($id, $firstname) | o simplemente una variable si es solo un parametro
				Advertencia: La cantidad de parametros (?) en la consulta y los enviados en este parametro deben coincidir

			results [Array] [opcional]: Nombres asignados a los resultados devueltos por la consulta, si no se especifica se utiliza los mismos devueltos por la consulta
				Ejemplo: array('id_abc', 'firstname_abc', 'lastname_abc', 'idnumber_abc', 'email_abc' .... etc)
				Advertencia: La cantidad de columnas devueltas por la consulta y los enviados en este parametro deben coincidir, generalmente resulta mas simple omitir este parametro

			callback [Closure, Funcion anonima] [opcional]: Funcion encargada de trabajar los datos recibidos para luego ser devueltos por GetResults()
				Ejemplo: $zdb->GetHandler('FETCH_FIELD_HANDLER'), $zdb->GetHandler('FETCH_ROW_HANDLER'), $zdb->GetHandler('FETCH_ROWS_HANDLER'), 
					$result = array();
					// Siempre es posible pasar variables desde el contexto actual hacia el callback utilizando use(&$var1, &$var2, ... etc)
					function($results) use (&$result){
						$row = array();
						foreach($results as $key => $value) $row[$key] = $value;

						// SetResults y AppendResults afectan a los resultados devueltos por GetResults(), SetResults establece los resultsdos y AppendResults los agrega a un resultado anterior existente
						$this->SetResults($row);
						// o
						$this->AppendResults($row);
						$result[] = $row;
						// o $result = $row;
					}
			fetch [bool] [opcional]: True si queremos obtener los resultados directamente luego de la ejecucion de Query()
			connection [mysqli_connection] [opcional]: Conexion a base de datos mysqli a utilizar para la consulta actual

			Solo el primer parametro es requerido, todos los demas pueden omitirse utilizando 'false' para utilizar su comportamiento por defecto
	*/

	// Ejemplos:

	// Automaticamente retorna un solo valor
	$version = $zdb->Query('SELECT VERSION()')->GetResults();
	
	// Automaticamente retorna un array()
	$user = $zdb->Query('SELECT id, firstname, lastname FROM mdl_user WHERE id = 123')->GetResults();
	
	// Automaticamente retorna un array de arrays: array(array(), array())
	$zdb->Query('SELECT id, firstname, lastname FROM mdl_user WHERE id IN (123, 321)')->GetResults();

	// Pasando un parametro
	$id = 123;
	$user = $zdb->Query('SELECT id, firstname, lastname FROM mdl_user WHERE id = ?', 'i', $id)->GetResults();

	// Pasando varios parametros
	$user = $zdb->Query('SELECT id, firstname, lastname FROM mdl_user WHERE id = ? AND suspended = ?', 'ii', array($id, 0))->GetResults();

	// Renombrando los valores devueltos
	$user = $zdb->Query('SELECT id, firstname, lastname FROM mdl_user WHERE id = ?', 'i', 123, array('userId', 'userLastname', 'userFirstname'))->GetResults();

	// Ejecutando un callback personalizado y utilizando una variable fuera del contexto del callback
	$users = array();
	$zdb->Query("SELECT * FROM mdl_user LIMIT 100", false, false, false, function($results) use (&$users){
		if($results['suspended'] == 1){
			$user = array('id' => $results['id']);
			$user['firstname'] = utf8_encode($results['firstname']);
			$user['lastname'] = utf8_encode($results['lastname']);
			$users[] = $user;
		}
	});

	// Utilizando una nueva conexion a mysql para una db diferente solo para una consulta
	$database_2 = 'test_db_2';
	$connection_new = new mysqli($host, 'root', $password, $database_2, $port);
	$user = $zdb->Query('SELECT id, firstname, lastname FROM mdl_user WHERE id = 123', false, false, false, false, false, $connection_new)->GetResults();

	// Puede verificarse que una consulta se ejecute correctamente utilizando Success()
	$params = array(1, 'ZeusAFK', 'Technologies');
	$query = "INSERT INTO mdl_user (id, firstname, lastnme) VALUES (?,?,?)";
	if($zdb->Query($query, 'iss', $params)->Success()){
		die('Usuario creado');
	}else{
		die($zdb->GetError());
		// Unknown column 'lastnme' in 'field list'
	}

	echo 'Hola mundo';


Link de la libreria:
zeusafk.database.lib.php
Tags: Input - MySQL - MySQL Developing - PHP - PHP Class - PHP MySQLi - Script PHP - Security Votes: 2 - Answers: 4 - Views: 28 Share on: Google Facebook Twitter LinkedIn Link
 

Answers:

  • Date: 29-07-2017 20:39:15 Hola Jorge,
    Voy a darte una crítica constructiva. Espero no me la tomes a mal.
    Aquí mis observaciones:
    1. No sigues ningún principio SOLID.
    2. Dado que no sigues ningún principio SOLID caes en un anti patrón llamado god methods
    3. Si te fijas muy bien tu clase no implementa correctamente el patrón Singleton
    4. No sigues los estándares PSR
    5. Dado que no sigues SOLID tu código tiene dependencias muy fuertes.
    6. No veo ninguna prueba unitaria que me garantice que funciona correctamente.
    7. Te faltan modificadores de acceso.
    8. Y los bloques de documentación???

    En conclusión es una aproximación muy básica a un ORM.
    No te preocupes, todos empezamos así y muchos hicimos cosas similares al iniciar.

    Si no te vas a dedicar a programar es un buen esfuerzo pero no fomentes su uso porque no es código profesional. Si por el contrario piensas ser programador y destacar del montón, este es un buen comienzo. Ahora tienes que reforzar conocimientos de OOP y desarrollo de aplicaciones a nivel empresarial. Te recomiendo empaparte bien de buenas prácticas de desarrollo OOP, aprender principios SOLID, familiarizarte con PSR y por supuesto leer más de patrones de diseño (Singleton es el más sencillo y su implementación es conocimiento básico).

    Saludos y espero que esta crítica te sirva.

    No dejes de preguntar y, sobre todo, leer mucho.
      Votes: 1 - Link answer
     
  • Date: 29-07-2017 23:00:16 Hola Ernesto,
    Gracias por las observaciones, es cierto que debería cumplirse varios de los puntos que mencionas para ser una opción por la que otras personas puedan optar, estoy consciente de ello y es la razón por la cual la he mantenido para uso en proyectos personales, pero decidí publicarlo luego de estar algún tiempo viendo los problemas que publican los usuarios y la forma en que manejan el tema de base de datos. La mayoría tiene problemas de SQL Injection y seguramente no saben que es eso :(

    1. No sigues ningún principio SOLID.
    2. Dado que no sigues ningún principio SOLID caes en un anti patrón llamado god methods
    - Si, pero nunca fue mi idea que siga los principios SOLID, podría haberla implementado diferente para eso, pero en su inicio y aun ahora su objetivo fue resolver un problema simple: Demasiado código para ejecutar consultas simples que se repite todo el tiempo. Así que busque acercarme lo mas posible a "resolverlo con una sola linea", dando como resultado lo que llamas "god methods". En mis proyectos me ha dado buen resultado, me reduce bastante el tiempo en temas de base de datos.
    3. Si te fijas muy bien tu clase no implementa correctamente el patrón Singleton
    - No hace mucho es que veo el tema de Singleton, te agradeceria si pudieras darme mas info sobre como deberia haberse implementado.
    4. No sigues los estándares PSR
    - Cierto, eso puede mejorarse pero personalmente algunos puntos de los PSR no me gustan :)
    5. Dado que no sigues SOLID tu código tiene dependencias muy fuertes.
    - Yes :P
    6. No veo ninguna prueba unitaria que me garantice que funciona correctamente.
    - Si, debería haberlas hecho, para mas adelante :D
    7. Te faltan modificadores de acceso.
    - Si, de hecho recién me di el trabajo de escribir la clase para que sea mas practica de utilizar, antes solo trabajaba con la función Query() y sus parámetros.
    8. Y los bloques de documentación???
    - Si, debería haberlos hecho, para mas adelante :D

    En conclusión es una aproximación muy básica a un ORM.
    - Ahí si no entendí que relación tiene con un ORM, creo que no tiene ninguna similitud a un ORM.
      Votes: 1 - Link answer
     
  • Date: 30-07-2017 08:49:15 Si sigues los principios SOLID puedes hacer tu código más flexible.
    Tan solo con Dependency inversion puedes ahorrarte lógica de tu método Query y reducir dependencias.

    Otra ventaja de SOLID, dado que piensas hacer las pruebas unitarias después, es que si las sigues te vas a facilitar enormemente el trabajo y alcanzar una cobertura de 100%

    Respecto al patrón Singleton, la primera indicación de que algo no va bien es que tienes un contructor público y no estás cancelando la posibilidad de clonar la clase. Esto te permite crear múltiples instancias de la clase (precisamente lo que Singleton pretende evitar).

    Dejaste por ahí un código que he visto muchas veces en javascript:

    $that = $this;
    


    Te traicionó el subconsciente eh jajajajaja.

    Se me hizo muy interesante tu implementación de handlers. Kudos!

    Mencioné el ORM porque la primera cosa que he visto muchos hacen es implementar un wrapper para la conexión y la consulta para posteriormente crear clases para los resultados de la consulta y cuando miras ya estás en el terreno de un ORM jajajaja
      Votes: 0 - Link answer
     
  • Date: 30-07-2017 15:59:15 Si, usando SOLID tendría las ventajas que mencionas pero me alejaría de mi objetivo "lo mas cerca posible a una sola linea de código", pero supongo que podría implementar el resto siguiendo SOLID y mantener mi función Query como una excepción, así ya cualquiera decide como trabajar.

    Respecto al patrón Singleton, mi objetivo era dar las 2 opciones, trabajar con una instancia única y también permitir generar nuevas instancias, así cualquiera decide con cual trabaja según su gusto y necesidades, Ej: conexión a varias bases de datos en un solo proyecto. Así que supongo podría decirse es una implementación incompleta de Singleton o al final que no es Singleton :D

    $that = $this;, seee, creo q estaba haciendo algo que luego descarte y olvide quitar eso xD

    Me toco pensar un buen rato para llegar a esa solución de la implementación de handlers, anteriormente siempre definía un callback en cada llamada a la función Query lo cual resultaba en aun demasiado código repetitivo.

    // Me toco pensar un buen rato para llegar a esa solución de la implementación de handlers, anteriormente siempre definía un callback en cada llamada a la función Query lo cual resultaba en aun demasiado código repetitivo.
    $user = array();
    $zdb->Query('SELECT id, firstname, lastname FROM mdl_user WHERE id = ?', 'i', $id, false, function($results) use (&$user){
    	foreach($results as $key => $value) $user[$key] = $value;
    });
    
    // Luego pase a implementar los handlers para evitar tener que definirlos cada vez en el callback
    $zdb->Query('SELECT id, firstname, lastname FROM mdl_user WHERE id = ?', 'i', $id, false, $zdb->GetHandler('FETCH_ROW_HANDLER'));
    $user = $zdb->GetResults();
    
    // Al final decidi implementar 3 handlers por defecto: FETCH_FIELD_HANDLER, FETCH_ROW_HANDLER, FETCH_ROWS_HANDLERS
    // Y luego implemente lo necesario para que decida entre esos 3 handlers por defecto el que mas convenga y lo use por defecto para luego ser retornado por GetResults()
    $user = $zdb->Query('SELECT id, firstname, lastname FROM mdl_user WHERE id = ?', 'i', $id)->GetResults();
    
    // Como ya tenia proyectos grandes funcionando siempre tuve que mantener la compatibilidad con lo usado anteriormente asi que todas las opciones anteriores siguen funcionando
    


    Creo que un ORM no es ni sera el objetivo de mi clase, eso ya es mucho trabajo y hay opciones muy buenas listas para usar, personalmente me encanta trabajar con Doctrine y Symfony. Me mantengo en el objetivo de "Ejecutar una consulta (select, insert, update, delete, sp, func, etc...) y trabajar sus resultados en lo mas cerca posible a una linea de código."

    Saludos :)
      Votes: 1 - Link answer
     
To actively participate in the community first must authenticate, enter the system.Sign In