Archivo del sitio

POCO, POJO, POPO y otras chunches… Ahhh Y MetaData tambien…


POCO es un acrónimo de Plain Old CLR Object

POJO es un acrónimo de Plain Old Java Object

Ambos acrónimos son utilizados para definir clases simples que pueden ser utilizados por frameworks para ser llenados, como muchas personas que me siguen en este blog, se darán cuenta que son muy utilizados en mis post para crear clases de paso o clases de llenado de datos que a su vez generan listas, hoy me he encontrado con algo que me da flojera a ciencia cierta y es actualizar una clase junto con todos los cambios que estas representan en las diferentes capas que utilizo en el trabajo, por lo que hoy dedique mi tiempo a realizar una herramienta que me permite crear estas clases de “dominio” o “entidad” de manera rapida utilizando la MetaData de la base de datos, veamos el código para armarla y luego podremos descargarlo.

Crearemos una solución de Windows Forms para este caso, y agregaremos los siguientes elementos

  • cbDatabaseManager: Combo que contendrá las bases de datos para conectarse
  • Server: TextBox que nos permitirá escribir el servidor a conectarnos
  • Port: TextBox que nos permitirá cambiar de puerto si es necesario
  • User: TextBox que nos permitirá escribir el Usuario
  • Password: TextBox que nos permitirá escribir el Password
  • Connect: Boton que generará la conexión
  • DirectoryPath: Path donde serán guardadas nuestras clases
  • button1: (Ya comenzamos con problemas de flojera) Boton que nos permitirá seleccionar el Path donde guardaremos nuestras clases
  • cbDataBases: Combo que se llenará con las bases de datos una vez estemos conectados
  • lstbTables: ListBox  que nos permitirá mostrar las tablas y seleccionar más de una tabla para su generación en clase
  • cbLanguage: Combo que nos dirá en que lenguaje serán creados nuestros ¿POCO’s?, ¿POJO’s? lo que ustedes quieran siguien siendo clases simples y dependen de la sintaxis del lenguaje
  • Console: TextBox Multilinea que nos dira que esta pasando en nuestro proceso
  • button2: Boton que nos permitirá Ejecutar la generación de clases
  • fbdDirectory: Control de FileBoxDialog que nos permitirá en conjunto con el Boton1 seleccionar nuestro directorio

Una vez realizado nuestro formulario debe quedar más o menos así:

Bien una vez creado nuestro formulario creemos las clases para utilizarlo, crearemos una carpeta en la raíz de nuestro proyecto llamada Entity, en el cual crearemos las siguientes clases:

Catalog


publicclassCatalog
{
public string Key { get; set; }
public string Value { get; set; }
}

La clase anteriormente mostrada es generica, se utiliza para llenar catalogos tanto estaticos como dinamicos esta se utilizará para los combos y los listbox

Connector

 public class Connector
 {
 public string DataBaseManager { get; set; }
 public string Server { get; set; }
 public int Port { get; set; }
 public string Catalog { get; set; }
 public string User { get; set; }
 public string Password { get; set; }

 public string SourceConnectorMaster { get {
 StringBuilder conn = new StringBuilder();
 conn.Append("Data Source = ")
 .Append(Server)
 .Append("; User ID = ")
 .Append(User)
 .Append("; Password = ")
 .Append(Password);
 return conn.ToString();
 } }

 public string SourceConnectorComplete
 {
 get
 {
 StringBuilder conn = new StringBuilder();
 conn.Append("Data Source = ")
 .Append(Server)
 .Append("; Initial Catalog = ")
 .Append(Catalog)
 .Append("; User ID = ")
 .Append(User)
 .Append("; Password = ")
 .Append(Password);
 return conn.ToString();
 }
 }
 }

La clase connector contendrá la información de conexión a la base de datos así como los datos que recopilemos de la entrada del formulario además de tener 2 propiedades más SourceConnectorMaster y SourceConnectorComplete, las cuales definen las cadenas de conexion que se turilizarán en este caso es .NET, más adelante con un poco de imaginación podemos agregar más cadenas de conexión de diferentes Administradores de Bases de Datos.

MetaInfo

 public class MetaInfo
 {
 public string Table { get; set; }
 public string FieldName { get; set; }
 public string Type { get; set; }
 public bool ISNullable { get; set; }
 public int MaxChar { get; set; }
 }

La clase MetaInfo contendrá la información de la Metadata de la base de datos Información de Tablas, Tamaños, Nombres de Columna, Tipos de Dato, etc…

CacheManager

 public static class CacheManager
 {
 public static List<Catalog> DataBaseManager;
 public static List<Catalog> Language;

 public static void init()
 {
 DataBaseManager = new List<Catalog>() {
 new Catalog() { Key="1", Value="SQLServer" }
 };

 Language = new List<Catalog>()
 {
 new Catalog() { Key="1", Value="C#" },
 new Catalog() { Key="2", Value="Java"}
 };
 }
 }

La clase CacheManager es una clase que se inicializará al abrir nuestro formulario y por lo tanto estatica, la cual contiene las listas estaticas dentro de nuestro programa, se utiliza para llenar los combos de Lenguaje y DataBaseManager, esto nos permite ir mostrando mientras crecemos la implementación, Estamos dieñando un programita, pero recordemos que si trabajamos en diferentes lenguajes seria un error crear una herramienta solo para uno de los lenguajes que utilizamos y peor aun si ligamos los lenguajes a bases de datos podria decirse que nativas o de paridad emmm… que quiero decir con esto, que si usas C# probablemente estes usando .NET o en su defecto si usas PHP, seguramente estas usando MySQL por esto es que voy a hacer el programa de tal manera que pueda crecer a mis necesidades.

Hora de preparar las conexiones a base de datos en este caso por prisa, solo utilizare, net; pero no obstante dejaré el proyecto preparado para crecer, así que crearé una carpeta DataAccess y dentro de ella una interface IData y una clase SQLServer, las cuales me permitirán trabajar y extender mi trabajo despues, veamos el contenido:

IData


public interface IData
{
List<Catalog> GetTables(string Connector);

List<Catalog> GetDataBases(string Connector);

List<MetaInfo> GetMetaInfo(string Connector);
}

Esta clase podemos ver que solo tiene las funciones que voy a utilizar en las demás clases esto me permitirá usar un poco de polimorfismo y algunos factorys para vivir mi experiencia un poco más ordenada (Si, a mi me va bien así…)

SQLServer


public class SQLServer : IData
{
public List GetMetaInfo(string Connector)
{
List<MetaInfo> metaInfo = new List<MetaInfo>();
SqlConnection conn = new SqlConnection(Connector);
try
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
#region Query
cmd.CommandText = @"SELECT
C.TABLE_CATALOG,C.TABLE_NAME,C.COLUMN_NAME,C.IS_NULLABLE,C.DATA_TYPE,C.CHARACTER_MAXIMUM_LENGTH
FROM INFORMATION_SCHEMA.COLUMNS C
INNER JOIN INFORMATION_SCHEMA.TABLES A ON C.TABLE_NAME=A.TABLE_NAME
WHERE A.TABLE_NAME NOT IN('SYSSCHEMAARTICLES','SYSPUBLICATIONS','MSREPL_ERRORS','TRANSACTIONS','MSMERGE_HISTORY','MSMERGE_AGENT_PARAMETERS','MSMERGE_REPLINFO',
'SYSSUBSCRIPTIONS','SYSMERGEARTICLES','VW_TRAMITES_CONCEPTOS','SYSARTICLEUPDATES','MSPUB_IDENTITY_RANGE','SYSTRANSCHEMAS','SYSMERGESCHEMAARTICLES','SYSMERGEPARTITIONINFOVIEW',
'SYSMERGEPARTITIONINFO','MSMERGE_SUPPORTABILITY_SETTINGS','MSMERGE_SESSIONS','MSMERGE_PAST_PARTITION_MAPPINGS','MSMERGE_GENERATION_PARTITION_MAPPINGS','SYSMERGESUBSCRIPTIONS',
'MSDYNAMICSNAPSHOTJOBS','MSDYNAMICSNAPSHOTVIEWS','MSMERGE_AGENT_PARAMETERS','MSMERGE_ARTICLEHISTORY','MSMERGE_ALTSYNCPARTNERS','MSMERGE_CONTENTS','MSMERGE_CURRENT_PARTITION_MAPPINGS',
'MSMERGE_DYNAMIC_SNAPSHOTS','MSMERGE_ERRORLINEAGE','MSMERGE_GENHISTORY','MSMERGE_IDENTITY_RANGE','MSMERGE_LOG_FILES','MSMERGE_METADATAACTION_REQUEST','MSMERGE_PARTITION_GROUPS',
'MSMERGE_SETTINGSHISTORY','MSMERGE_TOMBSTONE','MSPEER_CONFLICTDETECTIONCONFIGREQUEST','MSPEER_LSNS','MSPEER_ORIGINATORID_HISTORY','MSPEER_REQUEST','MSPEER_TOPOLOGYREQUEST',
'SYSARTICLECOLUMNS','SYSARTICLES','SYSEXTENDEDARTICLESVIEW','SYSMERGEEXTENDEDARTICLESVIEW','SYSMERGEPUBLICATIONS','SYSMERGESCHEMACHANGE'
)
AND A.TABLE_TYPE NOT IN('VIEW')
ORDER BY A.TABLE_NAME";
#endregion
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandTimeout = 0;
using (SqlDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
MetaInfo mi = new MetaInfo();
mi.Table = Convert.ToString(r["TABLE_NAME"]);
mi.FieldName = Convert.ToString(r["COLUMN_NAME"]);
mi.Type = Convert.ToString(r["DATA_TYPE"]);
mi.ISNullable = Convert.ToString(r["IS_NULLABLE"]) == "YES" ? true : false;
metaInfo.Add(mi);
}
}
}
conn.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return metaInfo;
}

public List<Catalog> GetDataBases(string Connector)
{
List<Catalog> catalogs = new List();
Catalog c = new Catalog();
c.Key = "0";
c.Value = "Select one";
catalogs.Add(c);
SqlConnection conn = new SqlConnection(Connector);
try
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
#region Query
cmd.CommandText = @"SELECT name, database_id FROM sys.databases";
#endregion
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandTimeout = 0;
using (SqlDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
c = new Catalog();
c.Key = Convert.ToString(r["database_id"]);
c.Value = Convert.ToString(r["name"]);
catalogs.Add(c);
}
}
}
conn.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return catalogs;
}

public List GetTables(string Connector)
{
List<Catalog> catalogs = new List<Catalog>();
SqlConnection conn = new SqlConnection(Connector);
try
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
#region Query
cmd.CommandText = @"SELECT * FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_NAME";
#endregion
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandTimeout = 0;
using (SqlDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
Catalog c = new Catalog();
c.Key = Convert.ToString(r["TABLE_NAME"]);
c.Value = Convert.ToString(r["TABLE_NAME"]);
catalogs.Add(c);
}
}
}
conn.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return catalogs;
}
}

Como podemos ver la clase SQLServer hereda de IData, esto me permite implementar los mismos metodos en todas mis clases ahora solo veamos que es lo que debemos hacer en cuanto a los metodos, creo que si eres desarrollador en .NET lo importante no es tanto el código si no más bien los querys veamos la información y expliquemoslos de poco en poco:

Metodo GetDataBases


SELECT name, database_id FROM sys.databases

Con este query obtenemos la información de todas las bases de datos en la instancia

Metodo GetTables

SELECT * FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_NAME

Este query a su vez nos devuelve las tablas que estan dentro de una base de datos seleccionada

Metodo MetaInfo

SELECT 
 C.TABLE_CATALOG,C.TABLE_NAME,C.COLUMN_NAME,C.IS_NULLABLE,C.DATA_TYPE,C.CHARACTER_MAXIMUM_LENGTH
 FROM INFORMATION_SCHEMA.COLUMNS C
 INNER JOIN INFORMATION_SCHEMA.TABLES A ON C.TABLE_NAME=A.TABLE_NAME
 WHERE A.TABLE_NAME NOT IN('SYSSCHEMAARTICLES','SYSPUBLICATIONS','MSREPL_ERRORS','TRANSACTIONS','MSMERGE_HISTORY','MSMERGE_AGENT_PARAMETERS','MSMERGE_REPLINFO',
 'SYSSUBSCRIPTIONS','SYSMERGEARTICLES','VW_TRAMITES_CONCEPTOS','SYSARTICLEUPDATES','MSPUB_IDENTITY_RANGE','SYSTRANSCHEMAS','SYSMERGESCHEMAARTICLES','SYSMERGEPARTITIONINFOVIEW',
 'SYSMERGEPARTITIONINFO','MSMERGE_SUPPORTABILITY_SETTINGS','MSMERGE_SESSIONS','MSMERGE_PAST_PARTITION_MAPPINGS','MSMERGE_GENERATION_PARTITION_MAPPINGS','SYSMERGESUBSCRIPTIONS',
 'MSDYNAMICSNAPSHOTJOBS','MSDYNAMICSNAPSHOTVIEWS','MSMERGE_AGENT_PARAMETERS','MSMERGE_ARTICLEHISTORY','MSMERGE_ALTSYNCPARTNERS','MSMERGE_CONTENTS','MSMERGE_CURRENT_PARTITION_MAPPINGS',
 'MSMERGE_DYNAMIC_SNAPSHOTS','MSMERGE_ERRORLINEAGE','MSMERGE_GENHISTORY','MSMERGE_IDENTITY_RANGE','MSMERGE_LOG_FILES','MSMERGE_METADATAACTION_REQUEST','MSMERGE_PARTITION_GROUPS',
 'MSMERGE_SETTINGSHISTORY','MSMERGE_TOMBSTONE','MSPEER_CONFLICTDETECTIONCONFIGREQUEST','MSPEER_LSNS','MSPEER_ORIGINATORID_HISTORY','MSPEER_REQUEST','MSPEER_TOPOLOGYREQUEST',
 'SYSARTICLECOLUMNS','SYSARTICLES','SYSEXTENDEDARTICLESVIEW','SYSMERGEEXTENDEDARTICLESVIEW','SYSMERGEPUBLICATIONS','SYSMERGESCHEMACHANGE'
 )
 AND A.TABLE_TYPE NOT IN('VIEW')
 ORDER BY A.TABLE_NAME

Y este nos da la información de tablas agrupadas por columnas con sus respectivas informaciónes por cada registro.

Una vez realizado esto tenemos la mitad de nuestro codigo para jugar, en realidad la otra mitad esta en la generación de los archivos, para hacerlo con más velocidad solo crearemos el de generacion de clases en C#; en la carpeta Entity crearemos otra carpeta llamada Servicio en la cual crearemos IFileWriter, CSharpFile y FileWriterFactory. Y veremos como nos queda:

IFileWriter

public interface IFileWriter
{
void GenerateFile(string Table, List meta);
void Save(string Path);
}

Nuevamente definimos una interfaz que nos apoyará en el camino de la extención de nuestro código.

CSharpFile

 public class CSharpFile : IFileWriter
 {
 private string Data;
 private string NameFile;
 public void GenerateFile(string Table, List<MetaInfo> meta)
 {
 this.NameFile = Table + ".cs";
 StringBuilder str = new StringBuilder();
 List<MetaInfo> TableInfo = meta.Where(MI => MI.Table == Table).ToList();
 str.Append("public class ").Append(Table).Append(" {").Append(Environment.NewLine);
 foreach (MetaInfo FieldInfo in TableInfo)
 {
 str.Append("\tpublic ");
 switch (FieldInfo.Type)
 {
 case "nchar":
 str.Append("string").Append(" ");
 break;
 case "varchar":
 str.Append("string").Append(" ");
 break;
 case "text":
 str.Append("string").Append(" ");
 break;
 case "int":
 str.Append("int").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 case "bigint":
 str.Append("long").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 case "smallint":
 str.Append("short").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 case "tinyint":
 str.Append("short").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 case "datetime":
 str.Append("DateTime").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 case "money":
 str.Append("decimal").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 case "bit":
 str.Append("bool").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 }
 str.Append(FieldInfo.FieldName).Append(" { get;set; }").Append(Environment.NewLine);
 }
 str.Append(" } ");
 Data = str.ToString();
 }
 public void Save(string Path)
 {
 string FullPath = System.IO.Path.Combine(Path, NameFile);
 System.IO.StreamWriter file = new System.IO.StreamWriter(FullPath);
 file.WriteLine(Data);
 file.Close();
 }
 }

Este archivo implementa los metodos de IFileWriter y nos permitirá crear una seccion que se dedicará solamente al codigo de .NET, no creo que tenga que explicar gran cosa, simplemente se crea un string que contiene la clase parseada en base a una serie de patrones

FileWriterFactory

 public static class FileWriterFactory
 {
 public static IFileWriter GenerateWriter(string Language)
 {
 IFileWriter writer = null;
 switch (Language)
 {
 case "C#":
 writer = new CSharpFile();
 break;
 case "Java":
//...
 break;
 case "PHP":
//...
 break;
 }
 return writer;
 }
 }

Un factory que contiene un switch con la información de que tipo de lenguaje usaremos.

Una vez realizado es hora de darle funcionalidad a nuestro From inicial.

Agregaremos las siguientes lineas que definirán nuestro “repositorio” de datos (Repository) y nuestro conector


Connectorconnector = newConnector();
IData Repository;

Después agregaremos la funcion Onload del formulario, que nos permitirá ejecutar operaciones al momento de la carga del form

 private void Form1_Load(object sender, EventArgs e)
 {
 CacheManager.init();
 cbLanguage.DisplayMember = cbDatabaseManager.DisplayMember = "Value";
 cbLanguage.ValueMember = cbDatabaseManager.ValueMember = "Key";
 cbLanguage.DataSource = CacheManager.Language;
 cbDatabaseManager.DataSource = CacheManager.DataBaseManager;
 }

Con estas operaciones llenamos los combos iniciales que son cbLanguaje y cbDatabaseManager.


private void Connect_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(Server.Text) && string.IsNullOrEmpty(User.Text) && string.IsNullOrEmpty(Password.Text))
{
MessageBox.Show("The Server, User and Password can't be blank");
}

Catalog databaseManager = (Catalog)cbDatabaseManager.SelectedItem;

connector.Server = Server.Text;
connector.User = User.Text;
connector.Password = Password.Text;

switch (databaseManager.Value)
{
case "SQLServer":
Repository = new SQLServer();
break;
}

List<Catalog> DataBases = Repository.GetDataBases(connector.SourceConnectorMaster);
cbDataBases.DisplayMember = "Value";
cbDataBases.ValueMember = "Key";
cbDataBases.DataSource = DataBases;
}

Con el código anteriror damos funcionalidad al boton connect podemos checar nuestra conección y crear los repositorios.


privatevoid cbDataBases_SelectedIndexChanged(object sender, EventArgs e)
{
if (cbDataBases.SelectedItem != null)
{
Catalog dataBaseSelected = (Catalog)cbDataBases.SelectedItem;
if (dataBaseSelected.Key != "0")
{
connector.Catalog = Convert.ToString(dataBaseSelected.Value);
List<Catalog> Tables = Repository.GetTables(connector.SourceConnectorComplete);
lstbTables.ValueMember = "Key";
lstbTables.DisplayMember = "Value";
lstbTables.DataSource = Tables;
}
}
}

Despues agregamos el evento SelectedIndexChanged de cbDataBases que nos permite que cuando se seleccione un nuevo item del combo automaticamente nos muestre las tablas de la base de datos que se selecciono y los despliega en lstbTables

private void button1_Click(object sender, EventArgs e)
 {
 fbdDirectory.ShowDialog();
 DirectoryPath.Text = fbdDirectory.SelectedPath;
 }

Que nos da la funcionalidad para seleccionar carpeta.

 private void button2_Click(object sender, EventArgs e)
 {
 Catalog languaje = (Catalog)cbLanguage.SelectedItem;
 IFileWriter writer = FileWriterFactory.GenerateWriter(languaje.Value);
 if (writer == null)
 {
 MessageBox.Show("must be select a language");
 return;
 }

 List<MetaInfo> data = Repository.GetMetaInfo(connector.SourceConnectorComplete);
 foreach(Catalog item in lstbTables.SelectedItems) {
 writer.GenerateFile(item.Value, data);
 writer.Save(DirectoryPath.Text);
 Console.Text += Console.Text+ "Se ha Generado el archivo " + item.Value + " en "+ DirectoryPath.Text + Environment.NewLine;
 }
 }

y finalmente el botton2 que nos permitira generar el código…

Es interesante como siguiendo ciertos patrones de programación podemos generar código de manera automatica… y evitarnos la tediosa talacha.

(Descargar)

 

Anuncios

Programando Clon MVC y WebApi en PHP (Parte 4)


Bienvenidos, a esta cuarta y ultima entrega que nos explica, o medio explica como crear un clon de MVC en PHP.

En la entrega anterori hemos visto como crear una pagina para agregar un registro, ahora veremos como presentarla y como editarla, si no has leído el apartado anterior te recomiendo que lo hagas desde aquí.

En este apartado, agregaremos una vista secundaria a nuestra Student que se encuentra dentro de la carpeta vistas, a la cual llamaremos Student.php.

Una vez creado nuestro archivo agregaremos el siguiente código:


<script src="js/DataList/Student.js"></script>
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">
Lista de Estudiantes
</h1>
<ol class="breadcrumb">
<li>
<i class="fa fa-dashboard"></i> <a href="">Dashboard</a>
</li>
<li class="active">
<i class="fa fa-edit"></i>Lista de Estudiantes
</li>
</ol>
</div>
</div>
<div class="row">
<div class="table-responsive">
<table id="lstStudent" class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>NoControl</th>
<th>Nombre</th>
<th>Apellido Paterno</th>
<th>Apellido Materno</th>
<th>CURP</th>
<th>Semestre</th>
<th>Opciones</th>
</tr>
</thead>
</table>
</div>
</div>

Ahora bien, una vez agregado este codigo descargaremos unas librerías que nos permitirán hacer algunas monadas con poco código puedes descargarlas desde aquí.

A partir de esto crearemos en la carpeta js una carpeta llamada CustomControls en la cual pondremos todos los JavaScript que utilizaremos nuestra solución quedará como la siguiente:

Ahora agrgaremos nuestros scripts a nuestro Student.php para que puedan ser localizados por el navegador:

<script src="js/CustomControls/CustomControls.js"></script>
<script src="js/CustomControls/CustomFunctions.js"></script>
<script src="js/CustomControls/CustomSimpleGrid.js"></script>

 

Una vez realizado esto podemos dedicarnos a escribir la parte del script que dará funcionalidad a nuestro código, ya saben nos vamos a la parte de abajo de nuestro codigo dentro de Script.php abrimos nuestras etiquetas de javascript y lo implementaremos de la siguiente manera:


<script>
var students = [];
var gridList;

function GetGrid() {
//Agregamos una lista con los semestres para el DropDown
var semestre = [];
semestre.push({ id: 1, name: "1" });
semestre.push({ id: 2, name: "2" });
semestre.push({ id: 3, name: "3" });
semestre.push({ id: 4, name: "4" });
semestre.push({ id: 5, name: "5" });
semestre.push({ id: 6, name: "6" });
semestre.push({ id: 7, name: "7" });
//Creamos un Grid con la librería, se define un Layout y los campos que queremos que se muestren además del tipo de campo a mostrar y sus botones
gridList = $('#lstStudent').gridView({
layout: { "NoControl": 0, "Name": "", "Paternal": "", "Maternal": "", "CURP": "", "Semestre": 0 },
columns: [
{ Field: "NoControl", ControlType: "Label", Source: null },
{ Field: "Name", ControlType: "Label", Source: null },
{ Field: "Paternal", ControlType: "Label", Source: null },
{ Field: "Maternal", ControlType: "Label", Source: null },
{ Field: "CURP", ControlType: "Label", Source: null },
{ Field: "Semestre", ControlType: "Dropdown", Source: semestre },
{
Field: "Opciones", ControlType: "Buttons",
Buttons: {
Edit: '<button type="button" class="btn btn-success btn-circle btn-edit-item"><i class="fa fa-pencil"></i></button>'
}
}
]
});
}
//Esta funcion nos permite darle funcionalidad a los botones que tengamos en la tabla
function TableFunction() {
$('#lstStudent tbody').on('click', 'button', function () {
var button = $(this);
var item = $(this).parent().parent();
var tbody = $(this).parent().parent().parent();
var data = $(item).attr('data-row');
if (typeof (data) == "string") {
if (data != "") {
data = JSON.parse($(item).attr('data-row'));
}
}
if (button.hasClass('btn-edit-item')) {
//Si existe la clase btn-edit-item se da por sentado que se ha terminado de editar el o los registros del item
//No debemos preocuparnos ya que la librería edita la variable de manera interna
Update(data);
}
});
}

$(document).ready(function () {
GetGrid();
//Se pide la lista de los estudiantes a nuestro webapi desde el javascript student.js
students = getStudents();
//Creamos el Grid con los resultados de la lista
gridList.gridView("Create", students);
//Agregamos funcionalidad al grid
TableFunction();
});
</script>

Este script manda a llamar a nuestro archivo CustomSimpleGrid.js el cual nos da las herramientas para crear un Grid, es muy simple de utilizar y además editable al nivel que decidamos, como podemos ver en las siguientes imagenes:

Y con esto hemos finalizado el tutorial de Clonando MVC y WebApi en PHP, espero que les haya gustado y que les sea de ayuda.

(Download)

Programando Clon MVC y WebApi en PHP (Parte 3)


En el apartado anterior se ha escrito sobre como crear un clon  webApi en php. En este veremos como realizar las vistas y las conexiones con nuestro JavaScript. Si no has leído el apartado anterior te recomiendo que lo hagas desde  aquí.

Para este ejercicio comenzaremos creando una carpeta en la ruta StudentWeb/Views/ con nombre Student dentro de la cual crearemos un archivo php llamado Student.php dentro del cual crearemos nuestra vista:

Como referencia, recordemos que en el primer apartado creamos el archivo _Layout.php el cual se encuentra dentro de la ruta StudentWeb/Views/Shared/ el cual contiene un switch que hace la llamada al archivo Student.php, bueno entonces agreguemos una manera de accesarlo, buscando la sección


...
<li class="active">
<a href="index.html"><i class="fa fa-fw fa-dashboard"></i> Dashboard</a>
</li>
<li>
<a href="charts.html"><i class="fa fa-fw fa-bar-chart-o"></i> Charts</a>
</li>
...

Será cambiado por:


...
<li class="active">
<a href="index.php"><i class="fa fa-fw fa-dashboard"></i> Dashboard</a>
</li>
<li>
<a href="index.php?Source=AddStudents"><i class="fa fa-fw fa-bar-chart-o"></i> Add Students</a>
</li>
<li>
<a href="index.php?Source=Students"><i class="fa fa-fw fa-table"></i> List Students</a>
</li>
...

Como podemos ver hacemos un llamado a un Source con AddStudents el cual no tenemos definido dentro de nuestro _Layout.php así que buscaremos de nueva cuenta nuestro switch y agregaremos el caso del source  AddStudents como se muestra en el siguiente código:


<?php
switch ($_GET["Source"]) {
case "Students":
include('Views/Student/Student.php');
break;
case "AddStudents":
include('Views/Student/AddStudent.php');
break;
default:
include('Views/Home/Dashboard.php');
break;
}
?>

Acto seguido crearemos un archivo llamado AddStudent.php en la ruta /Views/Student/ en el cual nos concentraremos por el momento,  lo siguiente que haremos será crear los componentes html dentro de nuestra vista para esto se utilizará html simple que viene dentro de nuestro template de bootstrap, una vez en nuestra carpeta de bootstrap daremos click sobre el forms.html que trae el contenido que nos interesa, muy similar al siguiente:

Lo que nos interesa realmente son las clases con las que construyeron estos elementos así que abriremos este mismo archivo en notepad y analizaremos los valores principales en este caso solo necesitamos los controles textbox y select, y la configuración del encabezado.

Encabezado:


<div class="row">
<div class="col-lg-12">
<h1 class="page-header">
Forms
</h1>
<ol class="breadcrumb">
<li>
<i class="fa fa-dashboard"></i> <a href="index.html">Dashboard</a>
</li>
<li class="active">
<i class="fa fa-edit"></i> Forms
</li>
</ol>
</div>
</div>

TextBox y Select


<div class="row">
<div class="col-lg-6">
<form role="form">
<div class="form-group">
<label>Text Input with Placeholder</label>
<input class="form-control" placeholder="Enter text">
</div>
<div class="form-group">
<label>Selects</label>
<select class="form-control">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
</div>
</div>

Vamos a agregar este código a nuestro AddStudent.php para poder visualizarlo:

Ahora lo modificaremos un poco para que quede de acorde a lo que necesitamos, que en este caso debería ser similar a nuestro arreglo de Student que hemos utilizado en nuestro clon de webApi y lo formateraremos un poco para que quede del siguiente modo:

<div class="row">
<div class="col-lg-12">
<h1 class="page-header">
 Agregar Estudiantes
 </h1>
<ol class="breadcrumb">
 <li>
 <i class="fa fa-dashboard"></i> <a href="index.html">Dashboard</a>
 </li>
<li class="active">
 <i class="fa fa-edit"></i> Agregar Estudiantes
 </li>
 </ol>
 </div>
</div>
<div class="row">
<form role="form">
<div class="col-lg-6">
<div class="form-group">
 <label>Nombre</label>
 <input class="form-control" placeholder="Escriba su Nombre">
 </div>
<div class="form-group">
 <label>Apellido Materno</label>
 <input class="form-control" placeholder="Escriba el Apellido">
 </div>
<div class="form-group">
 <label>Seleccione el Semestre</label>
 <select class="form-control">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
 </select>
 </div>
 </div>
<div class="col-lg-6">
<div class="form-group">
 <label>Apellido Paterno</label>
 <input class="form-control" placeholder="Escriba el Apellido">
 </div>
<div class="form-group">
 <label>CURP</label>
 <input class="form-control" placeholder="Escriba el CURP">
 </div>
 </div>
 </form>
</div>
<div class="form-group input-group">
 <button id="button" class="btn btn-default">Registrar</button>
</div>

Y se vería del siguiente modo:

Se ve bien ¿no? vamos a conectar esto con un javascript que utilizaremos para conectar con nuestro clon de WebApi que ya se encuentra generado en nuestra ruta StudentWeb/service/controller/Student.php, dicho JavaScript es un pequeño archivo que nos permitirá accessar a los metodos GET,POST,PUT, etc…, en mi propio modo de desarrollo creo las funciones a como las necesito, en este caso solo necesitamos 3, así que en la carpeta js crearemos una carpeta llamada DataList, en la cual crearemos un archivo Student.js que contendrá la siguiente información:


function getStudents() {
var lst = [];
$.ajax({
type: "GET",
url: "service/Student/",
contentType: 'application/json',
dataType: 'json',
async: false,
success: function (responseData, textStatus, jqXHR) {
if (responseData.Students.length > 0) {
lst = responseData.Students;
}
},
error: function (responseData, textStatus, errorThrown) {
}
});
return lst;
}

function Add(request) {
var response = {};
$.ajax({
type: "POST",
url: 'service/Student/',
contentType: 'application/json',
dataType: 'json',
async: false,
data: JSON.stringify(request),
success: function (responseData, textStatus, jqXHR) {
if (responseData.success) {
var additionalData = JSON.parse(responseData.AdditionalData);
alert("Alumno Agregado.");
}
else {
alert(responseData.Description);
throw responseData.Description;
}
},
error: function (responseData, textStatus, errorThrown) {
alert("Error interno");
throw "Error interno";
}
});
return response;
}

function Update(request) {
var response = {};
$.ajax({
type: "PUT",
url: 'service/Student/',
contentType: 'application/json',
dataType: 'json',
async: false,
data: JSON.stringify(request),
success: function (responseData, textStatus, jqXHR) {
if (responseData.success) {
alert("Alumno Actualizado.");
}
else {
alert(responseData.Description);
throw responseData.Description;
}
},
error: function (responseData, textStatus, errorThrown) {
alert("Error interno");
throw "Error interno";
}
});
return response;
}

Una vez creado este script, podemos agregarlo a nuestra vista AddStudent.php en la parte superior


<script src="js/DataList/Student.js"></script>

Con esto tenemos nuestro script de carga, ahora iremos a la parte de abajo de nuestro archivo y abrieremos las etiquetas y dentro pondremos la funcionalidad de la pagina con el siguiente código:

$("#button").on("click", function () {
var Name = $("#Name").val();
var Paternal = $("#Paternal").val();
var Maternal = $("#Maternal").val();
var CURP = $("#CURP").val();
var Semestre = $("#Semestre").val();

if (Name == undefined || Name == "") {
alert("El nombre es obligatorio");
}
else if (Paternal == undefined || Paternal == "") {
alert("El apellido paterno es obligatorio");
}
else if (Maternal == undefined || Maternal == "") {
alert("El apellido Materno es obligatorio");
}
else if (CURP == undefined || CURP == "") {
alert("El CURP es obligatorio");
}
else {
var request = {};
request.Name = Name;
request.Paternal = Paternal;
request.Maternal = Maternal;
request.CURP = CURP;
request.Semestre = Semestre;

response = Add(request);
}
});

Con el siguiente codigo hemos dado algunas validaciones como evitar que los campos vayan vacios, ya enlazado podemos ver que el orden del desarrollo se vuelve rapido y mejoramos el entendimiento de nuestra aplicacción, con esto terminamos el apartado 3, en el apartado siguiente veremos como crear grids (Tablas) en tiempo real, y como hacerlos editables gracias a la siguientes herramientas mostradas aquí.

Para descargar los avances del proyecto (Download)

Programando Clon MVC y WebApi en PHP (Parte 2)


En el apartado anterior hemos visto un ejemplo de como integrar las plantillas de Bootstrap a nuestra aplicación PHP, en este apartado veremos como crear un webApi, apoyandonos en los archivos .htacess y un pequeño modelito de web services rest de php que personalmente uso, si quieres ver como hacer servicios rest con php sin necesidad de Frameworks o librerías externas puedes echar una ojeada a esta entrada. Si has llegado a esta entrada por otros medios te recomiendo que leas esta serie de posts desde el primero.

Luego de todo el choro, manos a la obra, en esta ocasion nuesta atención se centrará en la carpeta service que se encuentra en la carpeta raíz de nuestra solución, este será el arbol de archivos que crearemos:

  • index.php
  • .htaccess
  • controller
    • Student.php

index.php

El archivo index.php nos permitirá obtener la información rest de los siguientes metodos GET,POST,PUT,DELETE, permitiendonos obtener la información directa y la llamada al recurso que corresponde como vemos en el siguiente código:

 


<?php 
header('Content-Type: application/json'); 
ini_set("allow_url_fopen", true); 
error_reporting(0); 
@session_start(); 
$path = parse_url($_SERVER["REQUEST_URI"], 5); 
$path = str_replace("/StudentWeb/service/", "", $path); 
$path = trim($path, "/"); 
@list($resource, $params) = explode("/", $path, 2); 
$method = strtolower($_SERVER["REQUEST_METHOD"]); 
$params = !empty($params) ? explode("/", $params) : array(); 
include("controller/". $resource .".php"); 
if (class_exists($resource)) { 
try { 
$resource = new $resource($params); 
$resource->{$method}();
    }
    catch (Exception $e) {
        @header("HTTP/1.1 500 Internal Server Error");
    }
}
else {
    @header("HTTP/1.1 404 File Not Found");
  echo "Error";
}
?>

Este código nos permite generar una ruta directa del index a cualquier controller, podria decirce que es un reflection o algo así:

.htaccess


<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /StudentWeb/service/index.php
</IfModule>

Este código htaccess nos permite redireccionar cualquier url que vaya en la dirección /StudentWeb/service/ al index.php que anteriormente creamos, dandonos la accesibilidad a la ruta /StudentWeb/service/Student/

Finalmente llegamos a nuestra carpeta controller dentro de la ruta /StudentWeb/service/controller/ la cual se encarga de contener todas nuestras claces relacionadas con nuestro servicio rest (clon de web api) editaremos el archivo Student.php que queda de la siguiente manera:


<?php
class Student {
	var $params;
	var $jsonParams;
    
	function Student($params="") {
		$this->params = $params;
		$content = file_get_contents("php://input", NULL, NULL, 0, 1000);
		$data = json_decode($content,true);
		$this->jsonParams = json_encode($data);
	}

	function get() {
		
		if (!$_SESSION["Students"]) {
			$_SESSION["Students"]  = array();
		}
		$Students["Students"] = $_SESSION["Students"];
		echo json_encode($Students);
	}

	function post() {
		try {
			$Students  = $_SESSION["Students"];
			$Student = $this->jsonParams; 
			$Sutdents = array();
			$Student["NoControl"] = Count($Students)+1;
			$Sutdents[] = $Student;
			$_SESSION["Students"] = $Sutdents;
			$result["success"] = true;
			$result["AdditionalData"] = $Student["NoControl"];
			echo json_encode($result);
		} catch(Exception $e) {
			$result["success"] = false;	
			$result["Description"] = $e;
			echo json_encode($result);
		}
	}
    
	function put() {
		$Student = $this->jsonParams;
		$Students  = $_SESSION["Students"];
		try {
			for ($i = 0; $i <= Count($Students); $i++) {
				if ($Students[$i]["NoControl"] == $Student["NoControl"]) {
					$Students[$i]["Name"] = $Student["Name"];
					$Students[$i]["Paternal"] = $Student["Paternal"];
					$Students[$i]["Maternal"] = $Student["Maternal"];
					$Students[$i]["Semestre"] = $Student["Semestre"];
				}
			}
			$_SESSION["Students"] = $Students;
			$result["success"] = true;
			echo json_encode($result);
		}
		catch (Exception $e) {
			$result["success"] = false;	
			$result["Description"] = $e;
			echo json_encode($result);
		}
	}
	
	function delete() {
		$NoControl = $this->jsonParams["NoControl"];
		$Students  = $_SESSION["Students"];
		$StudentsWithOutItems  = array();
		try {
			for ($i = 0; $i <= Count($Students); $i++) {
				if ($Students[$i]["NoControl"] != $NoControl) {
					$Student["NoControl"] = $Students[$i]["NoControl"];
					$Student["Name"] = $Students[$i]["Name"];
					$Student["Paternal"] = $Students[$i]["Paternal"];
					$Student["Maternal"] = $Students[$i]["Maternal"];
					$Student["Semestre"] = $Students[$i]["Semestre"];
					$StudentsWithOutItems[] = $Student;
				}
			}
			$_SESSION["Students"] = $StudentsWithOutItems;
			$result["success"] = true;
			echo json_encode($result);
		}
		catch (Exception $e) {
			$result["success"] = false;	
			$result["Description"] = $e;
			echo json_encode($result);
		}
	}
}
?>

como podemos ver nuestro código tiene 4 metodos que son los que serán expuestos en el recurso GET,POST,PUT,DELETE, estos metodos serán llamados directamente por el index y seran redirigidos a la clase Student los metodos de entrada serán en GET y DELETE de la siguiente forma

GET /StudentWeb/service/Student/

DELETE /StudentWeb/service/Student/?NoControl=1

POST /StudentWeb/service/Student/

PUT /StudentWeb/service/Student/

En el caso de los metodos POST y PUT se debe enviar en los headers en el modelo de JSON un jemplo de esto lo veremos en la siguiente entrega cuando agregemos las llamadas de Ajax con JQuery, y veremos como hacer pruebas de nuestro servicio.

En la proxima entrega veremos como hacer llamadas javascript y ensamblaremos las vistas.

Download

Programando Clon MVC y WebApi en PHP (Parte 1)


Como muchos desarrolladores .NET saben el MVC de Visual Studio tiene un patron bastante organizado, lo suficientemente organizado como para copiarlo a PHP y tratar de organizar nuestro código de una manera más comoda, en las entradas anteriores vimos como crear un patron de este tipo con Visual Studio, ahora, haremos lo mismo pero para nuestros compañeros desarrolladores en PHP 😉 que tambien es uno de mis lenguajes favoritos.

Comenzaremos creando nuestro “Esqueleto” de aplicación dentro de una carpeta StudenWeb que debe quedar como la siguiente:

Explicación de las carpetas:

Controllers : Es una carpeta reservada para hacer llamadas post internas dentro de las vistas un ejemplo de esto es la subida de archivos, muestra de imagenes protegidas entre otras cosas.

css : Guardará todos los archivos de Estilos de html.

fonts : Donde se guardan los archivos de tipografias e iconos.

Helpers : Funciones que podriamos utilizar dentro de nuestro sistema.

img : Archivos y Carpetas de Imagenes necesrios para nuestras vistas.

js : Archivos de JavaScritp.

Views : Archivos PHP con el código minimo, y en su mayor parte html.

service : Archivo donde crearemos nuestro webApi

A partir de aquí buscaremos una plantilla Bootstrap, como muchos de los lectores de este blog saben no soy muy afecto a crear diseños web, podemos descargarlo de la siguiente ruta https://startbootstrap.com/template-overviews/sb-admin/ (Es de mis favoritos), ahora veremos como integrar nuestro diseño de bootstrap a nuestro cascaron en PHP, analicemos las carpetas que tiene nuestra plantilla:

Bien, podemos encontrar dentro de las carpetas que nos interesan css, fonts-awesome, fonts, js, estas carpetas deben ser copiadas a nuestro proyecto de MVC lo haremos de la siguiente manera:

  1. Todo el contenido de la carpeta css será copiado a la carpeta css de nuestra solucion.
  2. Todo el contenido de la carpeta js será copiado a la carpeta js de nuestra solución.
  3. En cuanto a la carpeta fonts-awesome encontraremos las siguientes carpetas css, less, scss y fonts.
    1. Tomaremos el contenido de css que esta dentro de la carpeta fonts y será copiado a nuestra carpeta css.
    2. Tomaremos el contenido de la carpeta fonts y lo pasaremos a nuestra carpeta fonts

 

Una vez realizados estos pasos crearemos nuestro arbol en la carpeta de vistas, ya que es necesario que tengamos nuestra iformación de los php con contenido de html dentro de carpetas el arbol de archivos es el siguiente:

  • Views
    • Home
    • Shared

Despues de esto en nuestra carpeta raiz, crearemos un index.php, en el cual pondremos nuestro siguiente código:

<?php 
error_reporting(5); 
include('Views/Shared/_Layout.php'); ?>

 

Este codigo nos permite evitar los multiples notices que nos mostrará el wamp e incluir el archivo _Layout.php, el cual debe contener la información del index.hml de nuestro template que acabamos de bajar:

La imagen anteroir contiene el codigo recortado y colapsado para que se pueda apreciar mejor, no agregaremos la información tal como esta haremos unos pequeños cambios, que son en las siguientes lineas:

 

Cambio 1

<link href="font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">

Será cambiado por:

<link href="css/font-awesome.css" rel="stylesheet" type="text/css">

Cambio 2

Se quitará el contenido dentro de la etiqueta <div class=”container-fluid”> (Guardalo en un notepad que lo usaremos despues)

Cambio 3

Se tomarán las referencias a los JavaScripts y se pasarán después de las referencias css, como se ve en la siguiente imagen:

Una vez realizado esto copiaremos nuestro código a nuestro archivo _Layout.php. Una vez copiado crearemos dentro de la etiqueta <div class=”container-fluid”> un switch como el siguiente

 

<?php 
switch ($_GET["Source"]) { 
case "Students": include('Views/Student/Student.php'); break; 
default: include('Views/Home/Dashboard.php'); break; 
} 
?>

Finalizando estos cambios el sistema ya se encuentra funcional, para finalizar vamos a crear un archivo en la ruta Views/Home/ que se llamará Dashboard.php en el cual agregaremos el contenido que anteriormente se encontraba dentro de

que debio ser guardado en algún notepad en el cambio 2.

Una vez hecho esto nuestro esqueleto PHP de MVC con webApi esta terminado y para poder visualizar que mejor que meterlo en el wamp para verlo funcionando:

Con este cambio finalizamos esta entrega, ya tenemos nuestro esqueleto MVC con Bootstrap, y en la proxima entrega veremos como configurar el webApi, esta información tambien es explicada en mi libro que sera puesto a la venta en Amazon en un par de meses.

Download

Ejemplo de Sincronisación de datos con un servidor externo del Móvil Parte 2


En la entrada anterior pudimos ver como se realizaban las conexiones del teléfono para que este decidiera como nonectarse a internet, para que esto no sea tan pesado podríamos organizar un menú de configuración que ya no forma parte de este tutorial, aun que con la poca información que tenemos ya podríamos diseñar un juego Como DeadWalkers.

Debemos tener mucho cuidado con la información que sincronizamos dentro de la red, además existen elementos de seguridad los cuales debemos manejar para estos casos.

Dado que terminamos todas las modificaciones, les dejo la descarga y producto final y quedo de sus observaciones.

Aplicación para Instalar (Download)

Código fuente de la Aplicacion (Download)

Ejemplo de Sincronisación de datos con un servidor externo del Móvil Parte 1


Comenzaremos haciendo una cuenta dentro de http://www.000webhost.com/ esta web nos dará acceso a un servidor por ftp y además nos permitirá acceder a la web en este caso tomaremos el ejemplo anterior de rest que habiamos hecho y lo pondremos en el sitio que hayamos creado en mi caso yo ya cuento con un repositorio donde poner mis ejemplos , una vez que creamos el sitio, tendremos que cambiar algunas cosas en la base de datos para que este sincronizado con nuestro ejemplo anterior el cual tambien conectaremos en red comenzaremos haciendo las modificaciones para la base de datos dentro de los archivos del REST tenemos una carpeta llamada DB dentro de esta carpeta tenemos un archivo items.sql el codigo es el siguiente:


CREATE  TABLE IF NOT EXISTS `items` (
  `itemId` int(11) NOT NULL AUTO_INCREMENT,
  `Nombre` varchar(100) COLLATE latin1_general_ci NOT NULL,
  `Descripcion` varchar(100) COLLATE latin1_general_ci NOT NULL,
  `Precio` int(11) NOT NULL,
  `Existencia` int(11) NOT NULL,
  PRIMARY KEY (`itemId`) ,
  UNIQUE INDEX `Nombre_UNIQUE` (`Nombre` ASC) ,
  UNIQUE INDEX `Descripcion_UNIQUE` (`Descripcion` ASC) )
ENGINE = InnoDB;

Con esto dejamos sincronizada la base de datos con la base de datos que tenemos en el teléfono, ahora bien cambiaremos el item.php el cual debe quedar como el siguiente:

<!-- <?php -->
class Item
{
	var $params;
	var $db;

    function Item($params="") {
		$this->params = $params;
    }

    function get() {

		$this->db = new db("mysql12.000webhost.com","a9327292_rest","291211taj","a9327292_rest","MySQL");
		$this->db->conectar();

		switch ($this->params[0]) {
			case "Agregar":
				$this->Agregar();
			break;
			case "Editar":
				$this->Editar();
			break;
			case "Eliminar":
				$this->Eliminar();
			break;
			case "Listar":
				$this->Listar();
			break;
		}
		$this->db->desconectar();
    }

    function post() {

    }

	function Agregar() {
		$nombre = urldecode($this->params[1]);
		$descripcion = urldecode($this->params[2]);
		$precio = urldecode($this->params[3]);
		$existencia = urldecode($this->params[4]);

		$INSERT="INSERT INTO items (Nombre,Descripcion, Precio, Existencia) VALUES ('".$nombre."','".$descripcion."',".$precio.",".$existencia.")";
		$this->db->query($INSERT);
		$resp["Ok"] = true;
		if (!empty($this->db->Error)) {
		$resp["Ok"] = false;
		$resp["Mensaje"] = $this->db->Error;
		}
		echo json_encode($resp);
	}

	function Editar() {
		$nombre = urldecode($this->params[1]);
		$descripcion = urldecode($this->params[2]);
		$precio = urldecode($this->params[3]);
		$existencia = urldecode($this->params[4]);
		$itemId = urldecode($this->params[5]);

		$UPDATE="UPDATE items SET Nombre='".$nombre."',Descripcion='".$descripcion."', Precio=".$precio.", Existencia=".$existencia." WHERE itemId = ".$itemId;
		$this->db->query($UPDATE);
		$resp["Ok"] = true;
		if (!empty($this->db->Error)) {
		$resp["Ok"] = false;
		$resp["Mensaje"] = $this->db->Error;
		}
		echo json_encode($resp);
	}

	function Eliminar() {
		$itemId = urldecode($this->params[1]);

		$DELETE="DELETE FROM items WHERE itemId = ".$itemId;
		$this->db->query($DELETE);
		$resp["Ok"] = true;
		if (!empty($this->db->Error)) {
		$resp["Ok"] = false;
		$resp["Mensaje"] = $this->db->Error;
		}
		echo json_encode($resp);
	}

	function Listar() {
		if (!empty($this->params[1])) {
		$NoRegistro = $this->params[1];
		} else { $NoRegistro = 0; }
		if (!empty($this->params[2])) {
		$LimiteRegistro = $this->params[2];
		} else { $LimiteRegistro= 30; }

		$SELECT="SELECT itemId, Nombre, Descripcion, Precio, Existencia FROM items LIMIT " . $NoRegistro . " , " . $LimiteRegistro;
		$this->db->query($SELECT);
		echo $this->db->JSON();
	}

}
?>

Listo, despues de esto tomaremos la aplicación del post anterior la cual podemos descargar de aquí. y comenzaremos a modificar los archivos siguientes:

  • main.js
  • dbLocal.js

Además de primero crear un dbRest.js recuerdan el tutorial de JavaScript y JQuery Rapido? bien lo podemos ver aquí, y tomaremos las funciones para jugar de tal manera que dbRest.js quede así:

 

function Ejecutar(url, datos) {
	$.ajax({
		type: "GET",
		url: url,
		data: datos,
		contentType: "application/json; charset=utf-8",
		dataType: "json",
		beforeSend: function(respuesta) {
	},
	success: function(respuesta) {
		if (url.indexOf("Agregar") > -1) {
			EjecutaAgregar(respuesta);
		}
		else if (url.indexOf("Eliminar") > -1) {
			EjecutaEliminar(respuesta);
		}
		else if (url.indexOf("Listar") > -1) {
			EjecutaMostrar(respuesta);
		}
		else {
		}
	},
	error: function(objeto, exception) {
            if (objeto.status === 0) {
                alert('No conectado.');
            } else if (objeto.status == 404) {
                alert('La pagina no se encuentra. [404]');
            } else if (objeto.status == 500) {
                alert('Error Interno del Servidor [500].');
            } else if (exception === 'parsererror') {
                alert('La respuesta JSON ha Fallado.' + exception + objeto.status);
				alert(exception);
				alert(objeto.status);
            } else if (exception === 'timeout') {
                alert('Time Out.');
            } else if (exception === 'abort') {
                alert('La respuesta Ajax ha sido Abordada.');
            } else {
                alert('Error desconocido.\n' + jqXHR.responseText);
            }
	}
});
}

function EjecutaAgregar(msg) {
	if (msg.Mensaje != null) {
		alert(msg.Mensaje);
	}
	else {
		$('#tbNombre').val('');
		$('#tbDescripcion').val('');
		$('#tbPrecio').val('');
		$('#tbExistencia').val('');
	}
}

function EjecutaEliminar(Data) {
	if (Data.Mensaje != null) {
		alert(Data.Mensaje);
	}
	else {
	if (ItemId > 0) {
		alert('El Item fue eliminado.');
		$("#ListaEliminarId" + ItemId).remove();
		ItemId=0;
	}
	}
}

function EjecutaMostrar(Data) {
	if (Data.Mensaje != null) {
		alert(Data.Mensaje);
	}
	else { 
	for (var i=0; i<Data.length; i++){
		try
		{
			$("#ListaEliminarId" + Data(i).itemId).remove();
		}
		catch(err) { }
		var item = '<li class="ui-btn ui-btn-icon-right ui-li-has-arrow ui-li ui-corner-top ui-btn-up-c" data-theme="c" data-iconpos="right" data-icon="arrow-r" data-wrapperels="div" data-iconshadow="true" data-shadow="false" data-corners="false">';
		item += '<div class="ui-btn-inner ui-li ui-corner-top" id="ListaEliminarId'+Data[i].itemId+'">';
		item += '<div class="ui-btn-text" onclick="Borrar(1,'+Data[i].itemId+')">' + Data[i].Nombre + '</div>';
		item += '<span class="ui-icon ui-icon-arrow-r ui-icon-shadow">&nbsp;</span></div></li>';
		$("#ListaEliminar").append(item);
    }
	}
}

 
Ahora bien modificaremos de la siguiente manera el bdLocal.js para que pueda saber que desiciones tomar cuando tenemos red de datos el código debe quedar como el siguiente:

var db = window.openDatabase("Tiendita.db3", "1.0", "Informacion de la Tiendita", 30 * 1024);
var ItemId=0;

function CreaTablas(tx) {
     tx.executeSql('CREATE TABLE IF NOT EXISTS Items (itemId INTEGER PRIMARY KEY, Nombre Varchar(100), Descripcion VARCHAR(100), Precio INTEGER, Existencia INTEGER);');
}

function errorCB(err) {
	// Esto se puede ir a un Log de Error diría el purista de la oficina, pero como este es un ejemplo pongo el MessageBox.Show 😛
    alert("Error processing SQL: Codigo: " + err.code + " Mensaje: "+err.message);
}

function successCB() {
	if (ItemId > 0) {
	alert('El Item fue eliminado.');
	$("#ListaEliminarId" + ItemId).remove();
	ItemId=0;
	}
	$('#tbNombre').val('');
	$('#tbDescripcion').val('');
	$('#tbPrecio').val('');
	$('#tbExistencia').val('');
}

function CreaDB() {
	db.transaction(CreaTablas, errorCB, successCB);
}

function Agregar(n) {
	switch(n)
	{
	case 1:
		if (!Online) {
		db.transaction(AgregaItem, errorCB, successCB);
		}
		else {
		url = "http://ejemplos.webatu.com/wsREST/Item/Agregar/" + $('#tbNombre').val() + "/" + $('#tbDescripcion').val() + "/" + $('#tbPrecio').val() + "/" + $('#tbExistencia').val() + "/" ;
		Ejecutar(url,"");
		}
		break;
	default:
  		
	}
}

function AgregaItem(tx) {
     tx.executeSql('INSERT INTO Items (Nombre,Descripcion,Precio,Existencia) VALUES (?,?,?,?)',[$('#tbNombre').val(),$('#tbDescripcion').val(),$('#tbPrecio').val(),$('#tbExistencia').val()]);
}

function Mostrar(n) {
	switch(n)
	{
	case 1:
		if (!Online) {
	  	db.transaction(ObtenerItems, errorCB);
		} else {
			url = "http://ejemplos.webatu.com/wsREST/Item/Listar/" ;
			Ejecutar(url,"");
		}
	  break;
	case 2:
		if (!OnLine) {
	  	db.transaction(ObtenerItem, errorCB);
		} else {
		
		}
	  break;
	  break;
	default:
  		
	}
}

function ObtenerItems(tx) {
    tx.executeSql('SELECT * FROM Items', [], MuestraItems, errorCB);
}

function ObtenerItem(tx) {
    tx.executeSql('SELECT * FROM Items WHERE itemId=' + ItemId, [], MuestraItem, errorCB);
}

function MuestraItem(tx, results) {
    var len = results.rows.length;
	if (len > 0) {
		if (results.rows.item(0).Nombre != "") {
		$('#tbTarjetaDescripcion').val(results.rows.item(0).Nombre);
		}
		if (results.rows.item(0).Descripcion != "") {
		$('#tbFechaCorte').val(results.rows.item(0).Descripcion);
		}
		if (results.rows.item(0).Precio != "") {
		$('#tbFechaLimitePago').val(results.rows.item(0).Precio);
		}
		if (results.rows.item(0).Existencia  != "") {
		$('#tbLimiteCredito').val(results.rows.item(0).Existencia);
		}
	}
}

function MuestraItems(tx, results) {
    var len = results.rows.length;
    for (var i=0; i<len; i++){
		$("#ListaEliminarId" + results.rows.item(i).itemId).remove();
		$("#ListaEliminar").append('<li class="ui-btn ui-btn-icon-right ui-li-has-arrow ui-li ui-corner-top ui-btn-up-c" data-theme="c" data-iconpos="right" data-icon="arrow-r" data-wrapperels="div" data-iconshadow="true" data-shadow="false" data-corners="false"><div class="ui-btn-inner ui-li ui-corner-top" id="ListaEliminarId'+results.rows.item(i).itemId+'"><div class="ui-btn-text" onclick="Borrar(1,'+results.rows.item(i).itemId+')">' + results.rows.item(i).Nombre + '</div><span class="ui-icon ui-icon-arrow-r ui-icon-shadow">&nbsp;</span></div></li>');
    }
}

function Borrar(n,itemId) {
	ItemId = itemId;
	switch(n)
	{
	case 1:
		if (!Online) {
	  	db.transaction(BorrarItem, errorCB);
		} else {
			url = "http://ejemplos.webatu.com/wsREST/Item/Eliminar/" + ItemId + "/" ;
			Ejecutar(url,"");
		}
	  break;
	default:
  		
	}
}

function BorrarItem(tx) {
	tx.executeSql('DELETE FROM Items WHERE itemId=?', [ItemId], successCB, errorCB);
}

y finalmente modificaremos el main.js

var Online = false;

function main() {
		document.addEventListener("deviceready", aplicacionIniciada, false); // Al inciar la app
		document.addEventListener("pause", aplicaciónPausada, false);        // Al pausar la app
		document.addEventListener("resume", aplicaciónReiniciada, false);    // Al reiniciar la app
		document.addEventListener("online", phonegapOnline, false);          // Phonegap tiene acceso a internet
		document.addEventListener("offline", phonegapOffline, false);        // Phonegap NO tiene acceso a internet
		document.addEventListener("backbutton", atrasPulsado, false);        // Se ha pulsado la tecla atrás
		document.addEventListener("menubutton", menuPulsado, false);         // Se ha pulsado la tecla menú
		document.addEventListener("searchbutton", menuPulsado, false);       // Se ha pulsado la tecla búsqued
}

function aplicacionIniciada()
{
	CreaDB();
}
 
function aplicaciónPausada()
{
}
 
function aplicaciónReiniciada()
{
}
 
function phonegapOnline()
{
Online = true;
}
 
function phonegapOffline()
{
Online = false;
}
 
function atrasPulsado()
{
}
 
function menuPulsado()
{
}
 
function busquedaPulsado()
{
}

Por hoy lo dejaremos hasta aquí en la proxima entrega subiré el proyecto ya terminado, con estas modificaciónes el sistema sabrá cuando conectarse a la red y cuando trabajar los datos en el teléfono

WebServices en REST con PHPDBC


La Transferencia de Estado Representacional (Representational State Transfer) o REST. Para leer más teoría sobre REST aquí.

Bien dado que la teoria no es lo mio comenzaremos con la creación de un pequeño WebService de ABM pero nuestro servicio regresará JSON y utulizaremos a demás PHPDBC manos a la obra, nuestro árbol de archivos tiene que quedar de la siguiente manera:

  • index.php
  • .htaccess
  • includes
    • phpdbc2.min.php
  • modulos
    • item.php

index.php

<!--?php -->include("includes/phpdbc2.min.php");

//Comienzan los metodos del servicio
include("modulos/item.php");

$path = parse_url($_SERVER["REQUEST_URI"], 5);
$path = str_replace("/RESTTutorialRapido/", "", $path);
$path = trim($path, "/");
@list($resource, $params) = explode("/", $path, 2);
$resource = ucfirst(strtolower($resource));
$method = strtolower($_SERVER["REQUEST_METHOD"]);
$params = !empty($params) ? explode("/", $params) : array();

if (class_exists($resource)) {
try {
$resource = new $resource($params);
$resource->{$method}();
}
catch (Exception $e) {
@header("HTTP/1.1 500 Internal Server Error");
}
}
else {
@header("HTTP/1.1 404 File Not Found");
}
?>

Como podrán ver hicimos un cambio con la estructura que manejabamos con NuSOAP con este cambio podemos manejar la información como si fueran recursos un ejemplo de como accesar es:

http://127.0.0.1:4001/RESTTutorialRapido/Item/Agregar/Pluma/Esta es una pluma/

Agregar: es el nombre del metodo.
Pluma: es el nombre del elemento.
Esta es una pluma: es la descripcion de la pluma.

http://127.0.0.1:4001/RESTTutorialRapido/Item/Editar/Pluma Roja/Estoy modificando la pluma/1/

Agregar: es el nombre del metodo.
Pluma: es el nombre del elemento.
Esta es una pluma: es la descripcion de la pluma.
1: id del elemento.

http://127.0.0.1:4001/RESTTutorialRapido/Item/Eliminar/1/

1: id del elemento.

http://127.0.0.1:4001/RESTTutorialRapido/Item/Listar/0/30/

0: El número de registro donde veremos la información
30: El número de registros que serán mostrados

.htaccess

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /RESTTutorialRapido/index.php

Podemos ver en el .htaccess una linea quenos interesará mucho que es /RESTTutorialRapido/index.php para un dominio comprado /RESTTutorialRapido/ esta linea la podemos quitar.

item.php

<!--?php -->class Item
{
var $params;
var $db;

function Item($params="") {
$this->params = $params;
}

function get() {

$this->db = new db("localhost","root","","PHPDBC","MySQL");
$this->db->conectar();

switch ($this->params[0]) {
case "Agregar":
$this->Agregar();
break;
case "Editar":
$this->Editar();
break;
case "Eliminar":
$this->Eliminar();
break;
case "Listar":
$this->Listar();
break;
}
$this->db->desconectar();
}

function post() {

}

function Agregar() {
$nombre = urldecode($this->params[1]);
$descripcion = urldecode($this->params[2]);

$INSERT="INSERT INTO items (Nombre,Descripcion) VALUES ('".$nombre."','".$descripcion."')";
$this->db->query($INSERT);
$resp["Ok"] = true;
if (!empty($this->db->Error)) {
$resp["Ok"] = false;
$resp["Mensaje"] = $this->db->Error;
}
echo json_encode($resp);
}

function Editar() {
$nombre = urldecode($this->params[1]);
$descripcion = urldecode($this->params[2]);
$itemId = urldecode($this->params[3]);

$UPDATE="UPDATE items SET Nombre='".$nombre."',Descripcion='".$descripcion."' WHERE itemId = ".$itemId;
$this->db->query($UPDATE);
$resp["Ok"] = true;
if (!empty($this->db->Error)) {
$resp["Ok"] = false;
$resp["Mensaje"] = $this->db->Error;
}
echo json_encode($resp);
}

function Eliminar() {
$itemId = urldecode($this->params[1]);

$DELETE="DELETE FROM items WHERE itemId = ".$itemId;
$this->db->query($DELETE);
$resp["Ok"] = true;
if (!empty($this->db->Error)) {
$resp["Ok"] = false;
$resp["Mensaje"] = $this->db->Error;
}
echo json_encode($resp);
}

function Listar() {
if (!empty($this->params[1])) {
$NoRegistro = $this->params[1];
} else { $NoRegistro = 0; }
if (!empty($this->params[2])) {
$LimiteRegistro = $this->params[2];
} else { $LimiteRegistro= 30; }

$SELECT="SELECT itemId, Nombre, Descripcion FROM items LIMIT " . $NoRegistro . " , " . $LimiteRegistro;
$this->db->query($SELECT);
echo $this->db->JSON();
}

}
?>

Como podemos ver se genero una clase  muy simple, el sistema que tenemos en el index.php nos permitirá llamar a los metodos y además nos enviará las propiedades para poder utilizarlas, el PHPDBC nos da la tranquilidad de las conecciones a SQL.

Con esto terminamos el tutorial de REST

Ver el Tutorial de Programación Móvil
RESTTutorialRapido Download

PHP y MySQL con PHPDBC


Esta es la sección de PHP para dispositivos móviles, y será muy rapida y breve, me permitiré explicar la conexión a la base de datos y la muestra de información de Registros y Resultados, comenzaremos por el árbol de archivos como siempre lo hemos hecho:

  • Index.php
  • modulos\
    • ActualizarItem.php
    • AgregarItem.php
    • SeleccionarItems.php
    • BorrarItem.php
  • includes\
    • phpdbc2.min.php

Comenzaremos con la presentación de los archivos, el archivo index tiene el siguiente código:
index.php

<!--<? -->
$db->conectar();
switch($_GET["opcion"]) {
case "agregar":
include("modulos/AgregarItem.php");
break;
case "borrar":
include("modulos/BorrarItem.php");
break;
case "buscar":
include("modulos/SeleccionarItems.php");
break;
case "actualizar":
include("modulos/ActualizarItem.php");
break;
default:
echo "No se ha seleccionado ninguna opción conocida.";
}
$db->desconectar();
?>

En el código anterior podemos apreciar un esqueleto de tipo switch el cual dependiendo de la opcion que le demos por metodo $_GET nos llevará a cualquiera de las otras opciones.

Ahora bien como dato siguiente veremos los archivos que se encuentran en la carpeta modulos, los cuales nos permitirán las ejecuciones llamadas:

AgregarItem.php

El archivo tiene llamadas a la clase PHPDBC como lo sería $db->query(“Mi SQLString”); este metodo nos permitirá ejecutar cualquier tipo de cadena SQL y la mandará directamente a la base de datos si queremos controlar un error podemos hacerlo con la propiedad $db->Error; esta última regresará vacia si todo ha ido bien, en caso de que no nos mostrará el mensaje del error que la base de datos nos haya devuelto.

$db->query($INSERT);
$resp["Ok"] = true;
if (!empty($db->Error)) {
$resp["Ok"] = false;
$resp["Mensaje"] = $db->Error;
}
var_dump($resp);

La ejecución de este código se vería así:

SeleccionarItems.php

En esta opcion podemos encontrar otro metodo más dentro de PHPDBC que nos permite obtener los datos en un array ya procesado, el método $db->Datos();

$db->query($SELECT);
$Datos=$db->Datos();
var_dump($Datos);

La ejecución de este código se vería de la siguiente manera:

ActualizarItem.php

$db->query($UPDATE);
$resp["Ok"] = true;
if (!empty($db-&gt;Error)) {
$resp["Ok"] = false;
$resp["Mensaje"] = $db->Error;
}
var_dump($resp);

Así mismo mostraremos la versión del Error de actualización si algo sale mal que es la siguiente:

Y su versión sin error:

BorrarItem.php

$db->query($DELETE);
$resp["Ok"] = true;
if (!empty($db->Error)) {
$resp["Ok"] = false;
$resp["Mensaje"] = $db->Error;
}
var_dump($resp);


Y final mente el Borrado del Item, que se ve de la siguiente manera:

PHPDBCTutorial Download

Ver Tutorial de Programación de Dispositivos Móviles

Metodo para Webservices en PHP que recibe un Archivo


Como se crearía un metodo en WebServices de php para la Obtención y envio de Archivos desde la Base de Datos esta vez me iré muy rapido y solo pondré los métodos ya que tengo mucho trabajo, si quieres saber más sobre la creación de web services puedés verlo aquí

La tabla de la Base de Datos


CREATE TABLE `Archivo` (
`ArchivoId` BIGINT(20) NOT NULL AUTO_INCREMENT,
`NombreArchivo` VARCHAR(50) NOT NULL,
`NombreFisico` VARCHAR(50) NOT NULL,
`Mime` VARCHAR(50) NOT NULL,
`TipoArchivo` int(11) NOT NULL,
`Size` BIGINT(20) NOT NULL,
`ArchivoBinario` BLOB NOT NULL,
`FechaHora` datetime NOT NULL,
PRIMARY KEY (`ArchivoId`),
UNIQUE KEY `NombreFisico` (`NombreFisico`)
)

El archivo Tipos


$server->wsdl->addComplexType(
'Archivo',
'complexType',
'struct',
'all',
'',
array(
'ArchivoId' => array('name' => 'ArchivoId', 'type' => 'xsd:int')
,'NombreArchivo' => array('name' => 'NombreArchivo', 'type' => 'xsd:string')
,'NombreFisico' => array('name' => 'NombreFisico', 'type' => 'xsd:string')
,'TipoArchivo' => array('name' => 'TipoArchivo', 'type' => 'xsd:int')
,'Mime' => array('name' => 'Mime', 'type' => 'xsd:string')
,'Tamaño' => array('name' => 'Tamaño', 'type' => 'xsd:int')
,'Archivo' => array('name' => 'Archivo', 'type' => 'xsd:base64Binary')
,'Fecha' => array('name' => 'Fecha', 'type' => 'xsd:string')
)
);


Debemos notar en el tipo que acabamos de definir la siguiente linea:

'Archivo' => array('name' => 'Archivo', 'type' => 'xsd:base64Binary')

xsd:base64Binary uno de los tantos sistemas de codificación de binario a texto. esta es la clave para poder transportarlo a la base de datos.

El metodo para Obtener


conectar();
$SELECT = "SELECT NombreArchivo,NombreFisico,TipoArchivo,Mime,Size,Archivo,Fecha FROM Archivos WHERE ArchivoId = " $ArchivoId;
}
$db->query($SELECT);
$Archivo= $db->datos()[0];
$db->desconectar();
return new soapval('return', 'tns:Archivo', $Archivo);
}
?>

El Metodo para Subir


function UploadArchivo($Archivo){
$db = new db($_SESSION["host"],$_SESSION["usuario"],$_SESSION["pass"],$_SESSION["base"],$_SESSION["gestor"]);
$db->conectar();
$INSERT = "INSERT INTO Archivos (NombreArchivo,NombreFisico,TipoArchivo,Mime,Size,Archivo,Fecha) VALUES ('".$Archivo["NombreArchivo"]."','".$Archivo["NombreFisico"]."',".$Archivo["TipoArchivo"].",'".$Archivo["Mime"]."',".$EArchivo["Size"].",'".base64_encode($Archivo["Archivo"])."',NOW());";

$db->query($INSERT);
if (empty($db->Error)) {
$Respuesta["Ok"] = true;
$Respuesta["Mensaje"] = "Se ha guardado el registro con exito.";
}
else {
$Respuesta["Ok"] = false;
$Respuesta["Mensaje"] = "No se ha podido Guardar el registro Error:". $db->Error . "Consulta:";// . utf8_encode($INSERT);
}
$db->desconectar();
return new soapval('return', 'tns:Respuesta', $Respuesta);
}

base64_encode($Archivo[“Archivo”]) Nos permitirá hacer paso transparente de nuestro archivo a BLOB de la base de datos guardando la integridad de la información.

Descarga (Pendiente…)

A %d blogueros les gusta esto: