MyRad4PHP (Manual)

El objetivo de esta herramienta es crear aplicaciones al vuelo, generar interfaces tomando como origen una base de datos, haciendo que el desarrollador tenga una aplicación Web sin escribir una sola línea de código, esta inspirado en el Entity Framework y los sitios Web de datos dinámicos ambos de Microsoft y aun que su objetivo es no escribir código puede extenderse o escribirse código para personalizar todas o algunas de sus funcionalidades. Esta herramienta tiene como corazón clases que mapean la base de datos y la metadata útil para el manejo de la base de datos (cruds) y también los elementos que permitan generar la interfase.

Los sitios que se pueden desarrollar con esta herramienta tienen como centro la base de datos, desde ella parte todo con un analizador de estructuras de tablas y las relaciones entre ellas, la mayoría de generadores de aplicaciones Web con PHP parten de una estructura de datos establecida este generador no hace eso, lo que hace es partir de la estructura de la base de datos de tu aplicación y de allí genera todo lo necesario para poder trabajar los CRUD's.

INDICE

Los gallos se ven en la cancha

Personalizando la aplicacion

La filosofia y cosas de arquitectura

Requerimientos y limitaciones

Acerca del desarrollador y licenciamiento

Los gallos se ven en la cancha

Para crear una aplicación descomprima/copie la estructura básica que se suministra (vea Acerca del desarrollador y licenciamiento) en la carpeta que contendrá su sitio Web, para el caso voy a llamarle "ejemplo".

Ejecute un navegador Web y coloque la ruta de su aplicación Web para el caso de este manual será "ejemplo" y lo estoy ejecutando en mi localhost y seria entonces: http://localhost/ejemplo al momento de ejecutarse esa pagina será redireccionado a http://localhost/ejemplo/efm/configapp.php debido a que no encuentra el archivo setings.php en la raíz del aplicativo allí colocara los valores que corresponde a cada campo:

app1

servidor: el nombre del servidor MySQL que alberga la aplicación en este caso: localhost
Base de datos: nombre de la base de datos en este caso: almacén
Usuario: el usuario del servidor de MySQL
Clave: contraseña del servidor de MySQL
Nombre App: Nombre de la aplicación
Ruta server: que seria la ruta en el servidor Web de nuestra aplicación para este caso seria: /ejemplo/

app2

Pulsar sobre "Procesar", en caso de que el archivo setings.php ya exista en la raíz del aplicativo le dará un error y le indicara que tiene que eliminar dicho archivo, si todo sale como debe generara los archivos de configuración y será redireccionado hacia el generador de clases de mapeo de datos y metadata.

app3

En el generador de clases de mapeo de datos y metadata aparecerán los nombres de las tablas y las opciones para generar los archivos de mapeo y metadata ya seleccionados puede seleccionar las tablas que dese mapear, tanbien existe la opcion de la opcion es seguridad si marca una tabla con este atributo le indica al generador que en esa tabla se encuentran tres campos uno para las contraseñas, otro para el login de usuario y uno mas para el nombre de usuario, no soporta encriptacion pero puede ser procesado despues en la clase que mapea la tabla; pulse "procesar" y si todo ha salido correctamente vera un link referido a la pagina inical del aplicativo en este caso http://localhost/ejemplo y vera la aplicación generada.

app5

Con eso ya tenemos nuestra aplicación complemente lista funcionando.

La interfase es muy sencilla el index presenta una tabla con los nombres de cada una de las tablas como hiperlink's al clickear sobre cualquiera de ellos muestra una pagina conformada en principio por los filtros a manera de dorpdownlist (combos) a la derecha de estos se encuentra un enlace que dice "Ordenar por" que al presionar con el mouse sobre el despliega una lista de combos que corresponde a los campos y si selecciona SI pues se ordena por ese campo. Luego de ello aparece el link para insertar nuevos registros a continuación la cuadricula que esta compuesta por una columna con checkbox's, una columna con las opciones Modificar: que te lleva a la ventana de edición, Eliminar: que al accionarlo pregunta si elimina el registro y lo hace si se responde de forma afirmativa, Detalles: muestra el registro actual de forma vertical.

La ventana de inserción/edición esta conformada por cada uno de los campos de la tabla en forma vertical, si un campo es requerido y no se ha completado su valor le indicará el error y no dejara grabar, la ventana de detalles es muy similar solo con la diferencia de que los campos están modo de solo lectura.

Existe un paginados en la parte inferior de la cuadricula que muestra los registros en grupos de 10 por pagina, si se desea ver todos los registros puede seleccionar el que indica "Todos", luego del paginador existe un botón que indica "Eliminar seleccionados" al pulsarlo eliminara a todos los registros a los que el checkbox de la parte izquierda de la cuadricula se haya marcado.

app6

app7

app8

app9

app10

app11

app12

Personalizando la aplicacion

En esta parte explicare como modificar la metadata de cada tabla a continuación una explicación de cada uno de los elementos que componen la metadata.

Elementos para la las tablas:

Elementos para los campos

NOTA: Los archivos el mapeador de tablas y el que contiene la metadata de cada tabla siempre se encuentran en la carpeta "dal" del proyecto.

Es muy normal que los nombres de nuestros campos sean nemonicos y no queremos que en la aplicación final se muestren sino lo que en verdad representan y cuando la aplicación es generada solo se muestran los nemonicos o los nombres que están en la base de datos de nuestra tabla, pero como hacer que salga la cadena que deseamos veamos si en nuestra tabla "ventascab" tenemos un campo que indica "numdoc" y queremos que se muestre "Numero de documento" y también queremos que en lugar de que diga "ventascab" diga cabecera de ventas pues para ello vamos a la metadata y modificaríamos la etiqueta que dice "DisplayName" de la entidad y del campo veamos eso:

class ventascab_meta extends baseentity
{
function __construct()
{
$this->nombre="ventascab";
$this->displayname="Cabecera de Ventas";
$this->mostrar=true;
$this->readonly=false;
$this->grupo="Menu";
$this->campomostrar=idcliente;
$this->aCampos[id_ventacab]=new basecolumn();
$this->aCampos[id_ventacab]->nombre="id_ventacab";
.
.
.
.
$this->aCampos[numdoc]=new basecolumn();
$this->aCampos[numdoc]->nombre="numdoc";
$this->aCampos[numdoc]->displayname="Numero de Documento";
$this->aCampos[numdoc]->mostrar=true;
.
.
.

$this->aCampos[fechapago]->filtroobject="datetime";
}
}

Para cambiar el orden en que se muestran los campos tanto en la cuadricula (listados) para ello tenemos elemento posición en la metadata, solo se tiene que poner el orden siendo cero el primer campo a mostrar y así sucesivamente hasta completar con todos los campos aumentando en uno cada campo, tenga mucho cuidado de no repetir un numero ya que sobrescribiría en la ejecución al campo anterior con la misma posición, en otras palabras dos campos no pueden ocupar la misma posición.

Por defecto el generador toma como campo a devolver el campo siguiente a la llave primaria que en muchos casos viene a ser una llave externa como ejemplo se tiene una tabla productos en el que el campo que le sigue al PK es un FK hacia la tabla categorías por lo que al hacer consultas digamos en detalle de ventas me muestra los id's de la categoría relacionada con ese producto y no la descripción del producto para ello tenemos que modificar el método tostring de el mapeador de la tabla, así que editaremos para este ejemplo seria el archivo productos.php y vamos a cambiar la línea que dice: return $this->idcategoria; por return $this->descripcion; con eso ya tenemos lo que deseamos que nos muestre la descripcion del producto, pero si yo quiero que me muestre no solo la descripcion sino también la categoría ósea en lugar que salga "Tarro de leche gloria x 400ml" y quiero que salga "Lácteos - Tarro de leche gloria x 400ml" pues tendría que modificar la misma función para que trabaje de esta manera:
return $this->_categorias->descripcion." - ".$this->descripcion;
Como pueden ver he hecho una referencia a la entidad categorias e invocado a la propiedad descripcion y esto es posible por que el generado al encontrar un FK crea dos campos en la metadata:tablarelacion y camporelacion y al generar el mapedor de tabla crea un campo que tiene el nombre de la tabla relacionada pero antecedido por un sub guión "_" y que el core se encarga de completar con el registro relacionado y por eso se puede acceder a los valores de la tabla relacionada, es mas si una tabla tiene una tabla relación y esta tabla relación tiene otra tabla relación se pueden acceder a los valores de la tercera tabla relación de esta manera, pongamos que estamos viendo los detalles de una venta de productos y quiero saber la categoría:
$this->_productos->_categorias->descripcion;

En algunas ocasiones antes de guardar un registro se necesita realizar cálculos con algunos campos que el usuario no debe de modificar manualmente como los totales de una factura, creo que con un ejemplo me explico mejor, se va a guardar el total de una factura o sea se tiene el importe y se sabe cual es el impuesto (digamos 18%) y se tienen que guardar en los campos igv y total en el primero el 18% del importe y en el segundo el importe sumado el 18% del importe y no queremos que el usuario lo haga sino la aplicación para ello vamos primero a la metadata de la tabla y cambiamos el valor readonly a true en los campos igv y total, segundo vamos al mapeador de tabla y ubicamos el método __set($priedad, $valor) allí ubicaremos el campo "importe" dentro del switch

case "importe":
$this->importe=$valor;
break;
case "igv":
$this->igv=$valor;
break;
case "total":
$this->total=$valor;
break;

Así es como encontraríamos el código de la clase y como queremos cambiar eso para que sea auto calculado colocamos las operaciones pertinentes dejándolo así:

case "importe":
$this->importe=$valor;
$this->igv = $valor*0.18;
$this->total=$this->igv +$this->importe;
break;
case "igv":
break;
case "total":
break;

Ahora que sucede si no quiero que una tabla especifica se muestre como yo he diseñado los templates me refiero a que si no quisiera que al modificar sea siempre vertical sino que quiero un campo a la isquierda y otro a la derecha digamos la cabecera de ventas se muestra en forma vertical pero si quisiera que tomara el formato de una factura algo como:

Cliente: _________________________________________
Fecha: ____________ Fecha de pago: ______________
Factura Nro: ______________
Cancelada: ___
Importe: ____
IGV:___
Total:__

Para hacer esto se tiene que crear una carpeta en <applicationroot>/templates/template/custom_pages/ alli crearia una carpeta con el nmbre de la entidad en este caso "ventascab" y dentro de ella crear un archivo al cual quiero afectar, asi si es para la edicion creara en <applicationroot>/templates/template/custom_pages/ventascab/edit.tpl y en caso que fuera para el insertar insert.tpl o el detalle el archivo seria details.tpl.

Pero que modificar y que no, primero entendamos algo de como trabaja Smarty con los templates smarty es alimentado desde PHP mediate asignaciones de variables si vemos un codigo en php que asigne un valor a una variable seria este:

En la sentencia siguiente le estoy diciendo a Smarty que cree una variable llamada "tabla" y que va a tener el valor de $_GET["tabla"]
$smarty->assign('tabla',$_GET["tabla"]);

En el segundo caso le estoy diciendo a Smarty que le asigne a la variable "acampos" el resultado de la funcion getfields:
$smarty->assign('acampos',getfields($objentity,$objentity->registros[0]->toarray()));

ya dentro de la platilla de Smarty se invocan a las variables de la forma siguiente:
<tr>
{$value = $acampos.id_categoria}
<td>{$value.etiqueta}</td>
<td>{include file=$value.tipocontrol}</td>

{$value = $acampos.descripcion}
<td>{$value.etiqueta}</td>
<td>{include file=$value.tipocontrol}</td>
</tr>

Como pueden ver siempre entre llaves y como si fuesen variables de PHP antecedidos del caracter $ en el caso del ejemplo que muestro es que en lugar que me use dos filas para hacer la edicion de la tabla categoria solo aparesca una, pero si se fijan en {$value = $acampos.descripcion} lo que hago aqui es que creo una variable Smarty llamada $valor a la que le asigno un elemento con el nombre del campo y esto es por que el array que devuelbe la funcion getfields es un array con indices y esta formada de esta manera:

array("nomcamp"=> array("etiqueta"=><etiqueta>, "tipocontrol"=><tipocontrol>, "valor"=><valoractual>, "tamanio"=><tamanio>, "indices"=>array(), "etiquetas"=>array())

donde:

<nomcamp>: Nombre del campo extraido de la metadata.
<etiqueta>: etiqueta del campo extraido de la metadata (displayname).
<tipocontrol>: tipo de control extraido de la metadata.
<tamanio>: Ancho del campo extraido de la metadata.
<valor actual>: si es edicion se cargara el valor del campo que tiene en el registro actual.
indices: si el control es de tipo combobox osea es un fk este un array con los ids de la tabla dependiente.
etiquetas: un array con los valores (textos) par ael combo del fk.

Asi que cuando quiera hacer un template personalizado para ventascab como en el formato anterior tendria que hacer lo siguiente:
<table class="mytable">
<tr>
{$value = $acampos.idcliente}
<td>{$value.etiqueta}</td>
<td>{include file=$value.tipocontrol}</td>
</tr>
<tr>
{$value = $acampos.fecha}
<td>{$value.etiqueta}</td>
<td>{include file=$value.tipocontrol}</td>

{$value = $acampos.fechapago}
<td>{$value.etiqueta}</td>
<td>{include file=$value.tipocontrol}</td>

</tr>
<tr>
{$value = $acampos.id_tipdoc}
<td>{$value.etiqueta}</td>
<td>{include file=$value.tipocontrol}</td>

{$value = $acampos.numdoc}
<td>{$value.etiqueta}</td>
<td>{include file=$value.tipocontrol}</td>
</tr>
<tr>
{$value = $acampos.cancelada}
<td>{$value.etiqueta}</td>
<td>{include file=$value.tipocontrol}</td>
</tr>
<tr>
{$value = $acampos.importe}
<td>{$value.etiqueta}</td>
<td>{include file=$value.tipocontrol}</td>
</tr>
<tr>
{$value = $acampos.igv}
<td>{$value.etiqueta}</td>
<td>{include file=$value.tipocontrol}</td>
</tr>
<tr>
{$value = $acampos.total}
<td>{$value.etiqueta}</td>
<td>{include file=$value.tipocontrol}</td>
</tr>
<tr>
<td><input type="submit" value="Aceptar" name ="aceptar" class="submitButton"/></td>
<td><input type="submit" value="Cancelar" name="cancelar"/></td>
</tr>
</table>

Esto muestra que lo que varia es la posicion en la tabla de los campos y para hacer referencia a los indices del array que se pasa a Smarty se hace de la siguiente manera <arrayensmarty>.<indice> como cuando asigno a la variable $valor el contenido del indice total: {$value = $acampos.total}.

Las paginas personalizadas tienen la misma filosofia que la personalizacion de templates con la diferencia que aqui se maneja el PHP y no el template para ello se debe de crear una carpeta con el nombre de la tabla en <applicationroot>/swdd/custom_pages/ alli se colocaran los archivos que se quieran personalizar por ejemplo si quiera personalizar la insercion en la tabla ventascab pues creo una carpeta con el nombre ventas cab en la ruta indicada y un archivo llamado edit.php y seria algo asi: <applicationroot>/swdd/custom_pages/ventascab/edit.php con eso le estoy dicendo al motor del aplicativo que no usae el archivo estandar sino el que acabo de generar. COn esto se peude programar un nuevo archivo y nuevas formas de procesarlo dejando las demas entidades con el template estadar, digamos que quiero hacer un formulario de uno a muchos pues aun que el template no lo implementa se podria programar uno desde aqui apoyandonos en lo que ya tenemos programado manejado desde el core del framework.

La filosofia y cosas de arquitectura

El corazón de la aplicación seria la definición de las tablas y la metadata, basado en ello se hace toda la interfase y la lógica del procesamiento de los datos, unido a ello un core que se encarga de forma interna del tratamiento de la base de datos así el desarrollador solo ve clases e interactuan con ellas centrándose únicamente en desarrollar, confiando en que el core siempre va funcionar y en este caso el core se basa en una clase llamada entidad que administra el trabajo hacia la data.

Como todo parte de la información extraída de la base de datos para el funcionamiento de la aplicación generada se tiene que dividir en dos partes: el mapeo de la tabla y la metadata de la misma bien explicare cada una de ellas:

La clase que mapea la tabla

Esta seria la definición de cualquier tabla::

clase <nombre de clase>
{
privado <proiedad1>
privado <proiedad2>
privado <proiedad3>
.
.
.
privado <proiedadN>

publico constructor()
publico <valor de un campo> __get(propiedad a devolver)
publico __set(propiedad a darle valor, valor a asignar)
publico manual(array("propiedad1"=><valor1>,...,"propiedadN"=><valorN>))
publico array("propiedad1"=><valor1>,...,"propiedadN"=><valorN>) toarray()
publico cadena tostring()

}1

Cada una de las propiedades reflejan el nombre de un campo en la tabla, el constructor llena el objeto con valores nulos o vacíos, la función __get que se le pasa el nombre del campo cuyo valor deseamos se retorne, la función __set que tiene como parámetros el nombre del campo que se desea asignar y el valor que se asignaría al campo, la función "manual" que recibe como parámetro un array que tiene como índices los nombres de los campos y el valor a asignar como valor del índice, toarray que retorna un array que tiene como índices los nombres de los campos y el valor asignado al campo como valor del índice, tostring que retorna una cadena que es el valor de un campo o una concatenación de ellos, aquí un ejemplo de una clase generada:

class categorias
{
private $id_categoria;
private $descripcion;
function __construct()
{
$this->id_categoria=0;
$this->descripcion="";
}
function __get($propiedad)
{
return $this->$propiedad;
}
function __set($propiedad,$valor)
{
switch($propiedad)
{
case "id_categoria":
$this->id_categoria=$valor;
break;
case "descripcion":
$this->descripcion=$valor;
break;
default:
$this->$propiedad=$valor;
}
}
public function manual($row)
{
$this->__set("id_categoria",$row["id_categoria"]);
$this->__set("descripcion",$row["descripcion"]);

}
public function tostring()
{
return $this->descripcion;
}
public function toarray()
{
$arr=array();
$arr["id_categoria"]=$this->id_categoria;
$arr["descripcion"]=$this->descripcion;
return $arr;
}
}

La clase que contiene la metadata

Si eres de los que al leer la palabreja "metadata" te asustas o es la primera vez que lees y te suena complicado pues eres como yo hace algún tiempo con solo escuchar metadata ya dejaba de leer el articulo pero no es otra cosa que definiciones de algo, metadata es por ejemplo los tipos de datos de tu tabla, si es un índice principal o una llave externa, eso es metadata y no tiene mucha complicación. Bien las clases de metadata que maneja esta herramienta están compuestas muchos elementos que definen como actuara cada tabla y cada campo en la aplicación, pongámonos a pensar que define a una tabla pues tenemos los las llaves primarias, las llaves externas, el tipo de datos, si se modifica o no, que campo representa a la tabla, pero por que detenernos allí, por que no colocar otros valores como que tipo de control se mostrara cuando se modifique/inserte un registro, la posición en la pantalla en que se mostrara en la edición o el listado, eso es la metadata y este generador basa todo su poder en ella.

En la aplicación que se genera (la metadata) se tienen dos clases básicas (baseentity y basecolumn) de la primera se heredan las entidades y de la segunda esta contenida en la primera como un array que representa a los campos de la tabla su definición esta formada por lo siguiente:

clase baseentity
{
publico nombre de tabla
publico etiqueta a mostrar
publico mostrar tabla?
publico array de metacampo
publico campo a devolver
publico solo lectura?
publico grupo al que pertenece
}

clase basecolumn
{
publico nombre a mostrar
publico mostrar el campo?
publico sololectura?
publico es llave primaria?
publico es llave externa?
publico el tipo del campo
publico ancho del campo
publico etiqueta a mostrar
publico tipo de control para la edicion
publico no queda vacio?
publico posicion en las ventanas edicion/insercion
publico si se genera un filtro en los listados?
publico objeto con el que se muestra el filtro
publico subtipo del valor solo para los tipos datetime si es solo fecha o solo tiempo
publico si es una llave externa cual es el campo que referencia este campo en la otra tabla
publico tabla a la que se hace referencia en la relacion
}

La metadata de cada tabla se expresa de esta manera:

clase <nombre tabla>_meta hereda de basentity
{
constructor()
{
<propiedades de baseentity>=<valor>
array de basecolumn
array[<campotabla1>]-><propiedades de basecolumn>=<valor>
array[<campotabla2>]-><propiedades de basecolumn>=<valor>
array[<campotabla3>]-><propiedades de basecolumn>=<valor>
.
.
.
.
array[<campotablaN>]-><propiedades de basecolumn>=<valor>
}
}

Este es un ejemplo de una clase generada de la metadata:

class categorias_meta extends baseentity
{
function __construct()
{
$this->nombre="categorias";
$this->displayname="categorias";
$this->mostrar=true;
$this->readonly=false;
$this->grupo="";
$this->campomostrar=descripcion;
$this->aCampos[id_categoria]=new basecolumn();
$this->aCampos[id_categoria]->nombre="id_categoria";
$this->aCampos[id_categoria]->displayname="id_categoria";
$this->aCampos[id_categoria]->mostrar=true;
$this->aCampos[id_categoria]->posicion=0;
$this->aCampos[id_categoria]->tipo="integer";
$this->aCampos[id_categoria]->tipocontrol="integer";
$this->aCampos[id_categoria]->ancho=10;
$this->aCampos[id_categoria]->requerido=true;
$this->aCampos[id_categoria]->espk=true;
$this->aCampos[id_categoria]->readonly=true;
$this->aCampos[id_categoria]->esfk=false;
$this->aCampos[id_categoria]->filtrar=false;
$this->aCampos[id_categoria]->filtroobject="text";
$this->aCampos[descripcion]=new basecolumn();
$this->aCampos[descripcion]->nombre="descripcion";
$this->aCampos[descripcion]->displayname="descripcion";
$this->aCampos[descripcion]->mostrar=true;
$this->aCampos[descripcion]->posicion=1;
$this->aCampos[descripcion]->tipo="string";
$this->aCampos[descripcion]->ancho=45;
$this->aCampos[descripcion]->tipocontrol="string";
$this->aCampos[descripcion]->requerido=true;
$this->aCampos[descripcion]->espk=false;
$this->aCampos[descripcion]->readonly=false;
$this->aCampos[descripcion]->esfk=false;
$this->aCampos[descripcion]->filtrar=false;
$this->aCampos[descripcion]->filtroobject="text";
}
}


Capas en lugar de MVC

Como ya lo indica el titulo que antecede he apostado por capas a usar MVC y eso es una discusión de hace mucho tiempo cual era mejor usar separación por capas o Modelo-Vista-Controlador y bueno siempre he preferido la primera a la segunda, ¿como he separado las capas? como ya explique anteriormente las clases que mapean la estructura de las tablas y la metadata conforman la capa de datos por eso las puse en la carpeta llamada "dal" por las siglas en ingles de capa de acceso de datos (data access layer: DAL) la capa de negocios donde se procesa la información esta en la carpeta efm y swdd la lógica de procesamiento de interfase ayudada por la librería smarty que me permite separar el código php del html, así podemos ver que:

la clase datacontext maneja las entidades (mapeo y metadata) y la clase entidad, los archivos para generar la interfase (listados, inserciones, modificaciones y detalles) se encuentran en swdd que usan los templates de smarty.

La estructura de directorios de la aplicaciona

Este proyecto tiene una estructura de directorios rígida pero he intentado hacerla lo mas estándar que se podía, sin mas voy a describir cada directorio:

La carpeta raíz: contiene tres archivos index.php el índice de la aplicación, setings.php donde se encuentra los seteos generales de la aplicación y setup.php que contiene la definicion de una clase personalizada de smarty.

css: carpeta que alberga los archivos que definen los estilos para el diseño de toda la aplicación.

dal: esta carpeta contiene todos los archivos que hacen referencia a la data, allí se encuentran las clases que mapean las tablas de la base de datos y los que mapean la metadata los primeros tienen como nombre de clase el nombre de la tabla de la base de datos y los segundos son el nombre de la tabla mas "_meta" así si tenemos una tabla llamada categorias tendremos un archivo llamado categorias.php y categorias_meta.php y así para todas las tablas de la base de datos, existen además tres archivos mas que son dalall.php y metadata.php cuyo contenido son includes de todos los archivos de este directorio separado por su tipo, así los archivos que mapean las tablas están en dalall.php y los que mapean la metadata están en metadata.php, también esta un archivo llamado datacontext.php que define la clase manejadora del contexto de datos.

efm: esta carpeta contiene el core de la aplicación y los generadores de clases; esta compuesto por los siguientes archivos y clases:

Archivos propios del CORE

Archivos para el generador

img: donde se encuentran las imagenes de la aplicacion.

libs: contiene las librerias a usar smarty en su version 3.1.13 es una de ellas y permite separar el codigo HTML de la programacion PHP y datamanager que se encarga de administrar las conecciones hacia el servidor de datos.

scripts: contiene las librerias genericas de javascript para la aplicacion usa jquery 1.4.1 como base y tambien un script para el menu horizontal de la aplicacion.

swdd: esta es la carpeta donse se encuentran los archivos que hacen la aplicacion web mejor dicho la interface de la misma y esta formado por los siguientes directorios:

templates: este directorio contiene los subdirectorios de configuracion de la libreria smarty y son:

Requerimientos y limitaciones

PHP: 5.3.8 o superior
MySQL: 5.5.16 o superior
Smarty: 3.1.13 (incluido en el paquete)
JQuery: 1.4.1 (incluido en el paquete)

Solo funciona con MySQL
No soporta indices primarios de multiples campos
No soporta relaciones de uno a uno o de muchos a muchos

Implementar una ventana de cambio de contraseña
Implementar enviar a pdf los listados de las tablas
Implemetar un template para formularios de uno a varios: insercion, modificacion y detalles.

Acerca del desarrollador y licenciamiento

Aplicacion desarrollada por Jorge Luis Prado Anci, en cuanto al licenciamiento pues esta aplicacion se entrega tal cual y tienen permiso de modifcarla y distribuirla de la manera que deseen, solo se les solicita que respeten el nombre del desarrolador indicando quien lo ha desarrollado o manteniendo los comentarios en los archivos del script, como esta aplicacion se entrega tal cual el creador no se hace responsable de uso o mal uso de la misma, en lo referido al soporte el creador intentara dar el soporte necesario pero dejando en claro que es meramente voluntario.

URL de descarga:
Email del desarrollador: xmeele@hotmail.com
Pagina del desarrollador: http://xmeele.wordpress.com
Pagina del Proyecto: http://myrad4php.wordpress.com