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());
    }
  }
}

WordPress: Instalar “Theme” desde el Sistema de Archivos

Copiar el código del “theme” a la carpeta: /wp-content/themes/

Ejecutar las siguientes sentecias de código SQL en la base de datos de wordpress (desde el archivo /wp-config.php se puede obtener los datos de acceso a la base de datos). En “nombre_del_tema” se deberá incluir el nombre del directorio copiado en wp-content

UPDATE options SET option_value = 'nombre_del_tema' WHERE option_name = 'template';
UPDATE options SET option_value = 'nombre_del_tema' WHERE option_name = 'stylesheet'

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");