Migrar Base de Datos

En el desarrollo ágil la migración de base de datos es una tarea frecuente. Las bases de datos cambian y los datos deben ser migrados entre bases de datos diferentes.

Una forma sencilla de migrar base de datos del mismo tipo es a través de consultas SQL: Se define una consulta en la antigua base de datos que reúna las características de la nueva base de datos.

Ejemplo:

SELECT CONCAT(
    "INSERT INTO asignatura (id, nombre, formacion, clasificacion, codigo, perfil) VALUES ('", 
    HEX(id), ", ", 
    IF(nombre IS NOT NULL, CONCAT("'", nombre, "'"), 'null'), ", ", 
    IF(formacion IS NOT NULL, CONCAT("'", formacion, "'"), 'null'), ", ", 
    IF(clasificacion IS NOT NULL, CONCAT("'", clasificacion, "'"), 'null'), ", ",
    IF(codigo IS NOT NULL, CONCAT("'", codigo, "'"), 'null'), ", ",
    IF(perfil IS NOT NULL, CONCAT("'", perfil, "'"), 'null'),
    "); "
) FROM asignatura

Explicación:

  • Se inicia la consulta con un CONCAT.
  • Se define el string que tendrá el formato del insert de la nueva base de datos.

Code generation: Algoritmo básico

A continuación se presenta el pseudocódigo base para la generación de código a partir de un esquema de base de datos.

El codigo supone la existencia de las siguientes clases:

  • Entidad: Mapeo de una entidad de la base de datos compuesta por un nombre y un conjunto de campos.
  • Campo: Mapeo de un campo de una entidad de la base de datos, compuesto por un nombre, y un alias (único para cada campo)
abstract class CodeGeneration {
  
  Entidad $entidad;  

  public function main(){
    $this->generarComienzo();
    $this->recursion($this->entidad);
    $this->generarFin();
  }

  abstract protected function generarComienzo();
  abstract protected function generarFin();
  abstract protected function generarCuerpo($entidad, $prefijo);
  
  protected function recursion(Entidad $entidad, array<string> $visitado = [], string $prefijo= ""){
    if (!empty($prefijo)){ $this->body($entity, $prefix); }

    $this->recorrerClavesForaneas(Entidad $entidad, array<string> $visitado, string $prefijo);
  }

  public function recorrerClavesForaneas(Entidad $entidad, array<string> $visitado, string $prefijo){
    array<Campo> $fk = $entidad->obtenerClavesForaneasNoVisitadas($visitado);
    $prefijoAuxiliar = (empty($prefijo)) ? "" : $prefijo. "_";
    array_push($visitado, $entidad->nombre());

    foreach($fk as $campo){
      $this->recursion($campo->entidadReferenciada(), $visitado, $prefijoAuxiliar.$campo->alias());
    }
  }
}

Formularios Bootstrap 4+

Síntesis de las propiedades de Bootstrap 4+ para Formularios.

Para más información consultar https://getbootstrap.com

Por defecto los formularios se definen como display: block, width: 100%

.form-control: Controles básicos <input>, <select>, <textarea>

<input type="email" class="form-control">

.form-control-file: Archivo

<input type="file" class="form-control-file">

.form-control-lg, .form-control-sm: Tamaño de controles

<input class="form-control form-control-lg" type="text">

.form-group: Estructurar controles

Agrupar etiquetas relacionadas (labels, controles, texto opcional, mensajes de validación)

<div class="form-group">
<label for="exampleFormControlTextarea1">Example textarea</label>
<textarea class="form-control" id="exampleFormControlTextarea1" rows="3"></textarea>
</div>

readonly: Solo lectura

<input class="form-control" type="text" placeholder="Readonly input here…" readonly>

disabled: Deshabilitar

Si se aplica a fieldset, deshabilita todos los “form controls”.

Cuidado con los enlaces, pueden estar activados: Más información

<input class="form-control" type="text" placeholder="Disabled input here..." disabled>

.sr-only: Campos ocultos

Pensado para “screen readers”

<label class="sr-only" for="inlineFormInput">Name</label>

.form-control-plaintext: Texto plano, sin diseño

Recomendado usar con readonly

<input type="text" readonly class="form-control-plaintext">

.form-inline: Campos en línea

<form class="form-inline">
<div class="form-group mb-2">…</div>
<div class="form-group mx-sm-3 mb-2">…</div>
<button type="submit">Confirm identity</button>
</form>

.form-control-range: Scroll

<input type="range" class="form-control-range">

.form-check (.form-check-input, .form-check-label): checkboxes y radios

<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="defaultCheck1">
<label class="form-check-label" for="defaultCheck1">Default checkbox</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios1" value="option1" checked>
<label class="form-check-label" for="exampleRadios1">Default radio</label>
</div>

.form-check-inline: checkboxes y radios en línea

<div class="form-check form-check-inline"> ... </div>
<div class="form-check form-check-inline"> ... </div>

.position-static: .form-check que no poseen label

Se recomienda utilizar conjuntamente con aria-label

<div class="form-check">
<input class="form-check-input position-static" type="checkbox">
</div>

Estructuras complejas

Pueden ser construidas a través de las “grid classes”

<form>
<div class="row">
<div class="col">
<input type="text" class="form-control" placeholder="First name">
</div>
</div>
<div class="row">
<div class="col">
<input type="text" class="form-control" placeholder="Last name">
</div>
</div>
</form>

.form-row Reemplaza a .row para un diseño compacto

<form>
<div class="form-row">
<div class="col"> ... </div>
</div>
</form>

Ejemplos de estructuras complejas

Estructuras complejas utilizando grid classes

Formulario horizontal utilizando grid classes 

.col-form-label: centrar verticalmente

<form>
  <div class="form-group row">
  <label for="inputEmail3" class="col-sm-2 col-form-label"> ... </label>
  <div class="col-sm-10"> ... </div>
  </div>

.col-form-label-sm, .col-form-label-lg: Tamaño del label

Utilizar correctamente con form-control-sm, form-control-lg

<form>
  <div class="form-group row">
  <label for="inputEmail3" class="col-sm-2 col-form-label col-form-label-sm"> ... </label>
  <div class="col-sm-10"> ... </div>
  </div>

.col-auto: Auto-tamaño

Utiliza flex-box par crear columnas autoajustables.

Pueden ser utilizadas conjuntamente con columnas de tamaño específico

<form>
  <div class="form-row">
    <div class="col-auto"> ... </div>
    <div class="col-auto"> ... </div>    
  </div>

.form-text: Texto de ayuda

<small class="form-text text-muted">
  La clave debe ser de 8 caracteres
</small>

CSS de validación

Incluir el atributo novalidate en los formularios.

Novalidate requiere de javascript para administrar los eventos del formulario.

<form novalidate>

.valid-feedback, .invalid-feedback: Mensaje de validación o invalidación

<formnovalidate>
  <div class="form-row">
    <div class="col-auto">
      <label for="validationCustom01">First name</label>
      <input type="text" class="form-control" id="validationCustom01" required>
      <div class="valid-feedback">
        Looks good!
      </div>
      <div class="invalid-feedback">
        Error!
      </div>
    </div>

.is-valid, .is-invalid: Indicar si un campo es válido o no

Utilizado cuando se realiza validación en el servidor.

<input type="text" class="form-control is-valid">
<input type="text" class="form-control is-invalid">

.valid-tooltip, .invalid-tooltip: Mensaje de validación o invalidación a través de un tooltip

<form class="needs-validation" novalidate>
  <div class="form-row">
    <div class="col-auto">
      <label for="validationTooltip01">First name</label>
      <input type="text" class="form-control" id="validationTooltip01" placeholder="First name" value="Mark" required>
      <div class="valid-tooltip">
        Looks good!
      </div>
      <div class="invalid-tooltip">
        Error!
      </div>

.input-group, .input-group-prepend, .input-group-append, .input-group-text: Extender controles

<label>s afuera del input-group

<div class="input-group mb-3">
  <div class="input-group-prepend">
    <span class="input-group-text">@</span>
  </div>
  <input type="text" class="form-control">
</div>
<div class="input-group mb-3">
  <input type="text" class="form-control">
  <div class="input-group-append">
    <span class="input-group-text">@example.com</span>
  </div>
</div>
<div class="input-group">
  <div class="input-group-prepend">
    <span class="input-group-text">$</span>
  </div>
  <input type="text" class="form-control">
  <div class="input-group-append">
    <span class="input-group-text">.00</span>
  </div>
</div>

input-group-sm, input-group-lg: Tamaño de input-group

<div class="input-group input-group-sm">
<div class="input-group input-group-lg">

Multiple inputs

<div class="input-group">
  <div class="input-group-prepend">
    <span class="input-group-text">First and last name</span>
  </div>
  <input type="text" aria-label="First name" class="form-control">
  <input type="text" aria-label="Last name" class="form-control">
</div>

Multiple add-ons

<div class="input-group">
  <div class="input-group-prepend">
    <span class="input-group-text">$</span>
    <span class="input-group-text">0.00</span>
  </div>
  <input type="text" class="form-control">
</div>

Fuente

getbootstrap.com: Forms

getbootstrap.com: Input group

ng-bootstrap: Ejemplo navbar responsive

<nav class="navbar navbar-expand-md navbar-dark bg-dark">
  <a class="navbar-brand" href="#">Fines 2</a>

  <button class="navbar-toggler navbar-toggler-right" type="button" aria-controls="appNavigation" aria-expanded="!isCollapsed" aria-label="Toggle navigation" (click)="toggleMenu()">
    <span class="navbar-toggler-icon"></span>
  </button>

  <div class="collapse navbar-collapse" id="appNavigation" [ngbCollapse]="isCollapsed">
    <div class="navbar-nav mr-auto">
      <a (click)="toggleMenu()" class="nav-item nav-link" routerLink="" routerLinkActive="active">Inicio</a>

      <div class="nav-item dropdown" ngbDropdown >
        <a class="nav-link dropdown-toggle" id="dropdownSede" ngbDropdownToggle>Sede</a>
        <div class="dropdown-menu" ngbDropdownMenu aria-labelledby="dropdownSede">
          <a class="dropdown-item" routerLink="/sede-show" routerLinkActive="active" (click)="toggleMenu()">Lista</a>
          <a class="dropdown-item" routerLink="/sede-admin" routerLinkActive="active" (click)="toggleMenu()">Agregar</a>
        </div>
      </div>
    </div>
  </div>
</nav>
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-menu',
  templateUrl: './menu.component.html',
  styleUrls: ['./menu.component.css']
})
export class MenuComponent {

  public isCollapsed = true;

 toggleMenu() {
    this.isCollapsed = !this.isCollapsed;
  }

}

Exportar a XLS con Javascript

function exportarXls(tableId, nombre = ''){
	nombre = (nombre) ? nombre: 'spreadsheet'
	var uri = 'data:application/vnd.ms-excel;base64,'
	, template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><meta http-equiv="content-type" content="application/vnd.ms-excel; charset=UTF-8"><head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}</table></body></html>'
	, base64 = function (s) { return window.btoa(unescape(encodeURIComponent(s))) }
	, format = function (s, c) { return s.replace(/{(\w+)}/g, function (m, p) { return c[p]; }) }

	 var table = document.getElementById(tableId)
	 var ctx = { worksheet: nombre|| 'Worksheet', table: table.innerHTML }

	 var enlaceTmp = document.createElement('a');
	 enlaceTmp.href =  uri + base64(format(template, ctx));
   enlaceTmp.download = nombre+ '.xls';
   enlaceTmp.click();
}
<button class="btn btn-success" onclick="exportarXls('atributoId', 'nombreArchivo')">XLS</button>

WordPress: Visualizar solo los títulos de las entradas (posts)

En el Editor, se debe buscar el archivo que posee la lista de entradas o posts, puede tener diferentes nombres según el tema o su configuración, ejemplo index.php, home.php, … . A continuación se presenta un ejemplo en el que se imprime el título, enlace y fecha de creación del post:

<?php while ( have_posts() ) : the_post(); ?>
  <span><?php the_time('F j, Y'); ?></span>
  <h2 class="entry-title"><a href="<?php the_permalink(); ?>" rel="bookmark"><?php the_title(); ?></a></h2>
<?php endwhile; ?>

Para más información sobre otros métodos asociados con las entradas (posts) ver: https://codex.wordpress.org/The_Loop

Error Visual Studio Code: “PHP executable not found”

PHP executable not found. Install PHP 7 and add it to your PATH or set the php.executablePath setting

Solución Windows 10 – XAMPP

  1. File – Preferences – Settings – User Settings – Options: Abrir settings.json
  2. Agregar el siguiente código:
{
    "php.validate.executablePath": "C:\\xampp\\php\\php.exe",
    "php.executablePath": "C:\\xampp\\php\\php.exe"
}

Solución Windows 10 -WAMP

{
    "php.validate.executablePath": "C:\\wamp64\\bin\\php\\php7.0.4\\php.exe",
    "php.executablePath": "C:\\wamp64\\bin\\php\\php7.0.4\\php.exe"
}

Funciones útiles de arrays

array_map: Crear un array simple a partir de un array múltiple de objetos (array numérico de array asociativos)

$personas = array (
  array ( "id" => "1", "nombre" => "Juan Pérez"),
  array ( "id" => "1", "nombre" => "Natalia Suarez"),
);
$ids = array_map(function($persona) {
    return $persona['id'];
}, $personas);

usort: Order un array de objetos  (array numérico de array asociativos)

function cmp($a, $b) {
return strcmp($a->nombre, $b->nombre); }
usort($personas, "cmp");

enctype = “multipart/form=data”

Formas de POSTear datos

Con HTTP hay dos formas de POSTear datos:
application/x-www-form-urlencoded: Utilizado por defecto.
multipart/form-data: Utilizado cuando se requieren archivos o un volumen abundante de datos. Debe incluirse en el formulario el atributo enctype=”multipart/form-data”.

Sin enctype=”multipart/form-data”

POST /home/uploadfiles HTTP/1.1
Content-Type: application/x-www-form-urlencoded
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64)
Content-Length: 13

submit=Submit

Con enctype=”multipart/form-data”

Significa que el formulario será enviado en múltiples partes, es necesario para enviar archivos.

POST /home/uploadfiles HTTP/1.1
Content-Type: multipart/form-data; boundary=---------------------------7d81b516112482 
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64)
Content-Length: 324

-----------------------------7d81b516112482 
Content-Disposition: form-data; name="file"; filename="\\SERVER\Users\Scott\test.txt"
Content-Type: text/plain

foo
-----------------------------7d81b516112482
Content-Disposition: form-data; name="submit"

Submit
-----------------------------7d81b516112482--

¿Por qué no utilizamos siempre enctype=”multipart/form-data”?

Al no utilizarlo, los datos enviados, simplemente consisten en una serie de “key:values”, y pueden ser parseados como GET o POST.
Al utilizarlo, el volumen de los datos se hace mayor y solo puede utilizarse POST.

Cálculos de montos para combustibles

Introducción

A continuación se incluye un script en PHP para calcular los valores de un item correspondiente a un combustible, de forma tal que sea aceptado por los WebServices de facturación electrónica de AFIP y al mismo tiempo, los valores sean exactos con una impresora fiscal.
A un combustible se asocian dos valores que serán utilizados para calcular impuestos internos: “coeficiente” e “itc”.

Datos de entrada
$cantidad //cantidad vendida. Si es 0 se calcula en base al precio total
$precio //precio total vendido. Si es 0 se calcula en base a la cantidad
$itc //valor entre 0 (si no posee) y 1
$coeficiente //valor entre 1 (si no posee) y 2
$ivaValor //valor del iva, ej 0.21 para 21%
$precioArticulo //precio total del articulo.
Script
$precioNetoArticulo = $precioArticulo * (1 - $ivaValor);

if(!empty($precio)) { 
  $cantidad = round($precio / $precioArticulo, 2); 
} else {
  $precio = round($cantidad * $precioArticulo, 2); 
}

if($coeficiente != 1){
  $precioNeto = round($precio / $coeficiente, 2);
  $importeIva = round($ivaValor * $precioNeto, 2);
  $importeItc = round($itc * $precioNeto, 2);
  $alicuotaItc = round($importeItc * 100 / $precioNeto, 2);
  $impuestoInterno =  $precio - $precioNeto - $importeIva - $importeItc;
  $alicuotaImpuestoInterno = round($impuestoInterno * 100 / $precioNeto, 2);
  $importeTributo = $importeItc + $impuestoInterno;
  $tasaAjuste = $precioNeto / ($precioNeto + $impuestoInterno + $importeItc);
  
} else {
  $ivaAux = round($precio / (1 + $ivaValor), 2);
  $importeIva = $precio - $ivaAux;
  $precioNeto = $precio - $importeIva;
  $impuestoInterno = 0;
  $alicuotaImpuestoInterno = 0;
  $importeItc = 0;
  $alicuotaItc = 0;
  $tasaAjuste = 0;
  $importeTributo = 0;
}

$ret = array(); //array con los valores de retorno
$ret["precio_articulo"] = number_format($precioArticulo, 2, ".", "");
$ret["precio_neto_articulo"] = number_format($precioNetoArticulo, 2, ".", "");
$ret["precio"] = number_format($precio, 2, ".", "");
$ret["cantidad"] = number_format($cantidad, 2, ".", "");
$ret["precio_neto"] = number_format($precioNeto, 2, ".", "");
$ret["importe_iva"] = number_format($importeIva, 2, ".", "");
$ret["impuesto_interno"] = number_format($impuestoInterno, 2, ".", "");
$ret["alicuota_impuesto_interno"] = number_format($alicuotaImpuestoInterno, 2, ".", "");    
$ret["importe_itc"] = number_format($importeItc, 2, ".", "");
$ret["alicuota_itc"] = number_format($alicuotaItc, 2, ".", "");
$ret["importe_tributo"] = number_format($importeTributo, 2, ".", "");    
$ret["tasa_ajuste"] = number_format($tasaAjuste, 8, ".", "");
Prestar especial atención a los valores de redondeo.

En las impresoras fiscales, los calculos son realizados por el mismo controlador fiscal, para tener una concordancia de valores se recomienda utilizar cantidad = 1 y precio_neto en vez de cantidad y precio_neto_articulo. Luego el detalle de la cantidad total vendida y el precio neto del articulo pueden ser incluídos en algun string con información adicional