jueves, 9 de agosto de 2012

EYui - Widgets para Yii Framework - jQuery

Hola, en esta oportunidad presento un Widget para Yii Framework que sirve para hacer busquedas de entidades en forma de componente abstracto (en el sentido de que puedes buscar cualquier cosa en tu modelo de datos usando este widget).

Utiliza: Ajax-jQuery.
probado en: Php 5.4.5,  Yiiframework: 1.10, 1.11, 1.12








Caso de Ejemplo:

Tengo una lista de propiedades (casas, apartamentos, etc) las cuales deben tener asignado un propietario (una persona), por tanto el widget se inserta en el formulario de la propiedad y tras una minima configuracion este muestra un componente que hara la busqueda en la lista de propietarios segun el texto que el usuario introduzca.

es muy simple, pero muy util y ayuda a sectorizar ("componentizar") el código en partes reutilizables.

Todos los detalles de instalación y el material estan disponibles aqui.

https://bitbucket.org/christiansalazarh/eyui


lunes, 6 de agosto de 2012

Generando un Sitemap Dinamico en el website

Hola,

en primer lugar, necesitas saber que es y para que es un sitemap.

  • Sitemap es un protocolo,  esta es la URL principal que informa todo acerca de: http://www.sitemaps.org/protocol.html
  • Sitemap sirve (principalmente) para que el buscador (google), pueda saber que páginas existen en tu website y de ese modo pueda saber que contenidos tiene.

Ejemplo que muestra la necesidad de un sitemap:

Tienes un e-commerce, con cientos de productos en tu base de datos, OJO: en tu base de datos, no son páginas físicas, por tanto, si estan en tu base de datos cómo sabe google que productos tienes ?  Solución fácil...proveyendole un catálogo...si...pero vas a poner un catálogo con todos los productos ? no verdad ?.

(Nota para usuarios de Yii Framework: Yii trae el URL manager para que las url puedan escribirse de una forma SEO-Friendly, pero eso no basta...y es otra cosa totalmente distinta !)

Cómo entonces..bueno, me imagino que no estas pensando hacer una página html con el listado de todos los productos del e-commerce, por tanto ya me entendiste que habrá que hacerlo programáticamente:

Formato del Sitemap:

El archivo sitemap.xml es un archivo de texto con formato XML, es decir, su contenido esta escrito de forma que un programa consumidor de XML pueda entenderlo.

Este es el contenido de un archivo sitemap.xml:
<?xml version="1.0" encoding="UTF-8"?>

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">

   <url>

      <loc>http://www.example.com/</loc>

      <lastmod>2005-01-01</lastmod>

      <changefreq>monthly</changefreq>

      <priority>0.8</priority>

   </url>

</urlset> 

Si sigo el ejemplo del e-commerce y la requerida lista de productos para el buscador, entonces, por -cada producto- hallado en la base de datos de productos debe crearse -una entrada en el archivo sitemap.xml-....ojo de nuevo...no un archivo sitemap por cada producto.

Como se escriben las entradas de pagina en el sitemap ?

Si ves el ejemplo arriba, hay un forma: <url>....blabla....</url>   cada una de estas representa a un producto, por tanto el archivo sitemap final debería lucir así:

<?xml version="1.0" encoding="UTF-8"?>

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<!-- una de estas por cada producto: --> <url><loc>http://www.myecommerce.com/index.php?r=item/create&amp;idproduct=1&amp;amp;desc=descripcion aqui del producto </loc><changefreq>weekly</changefreq></url>
<!-- una de estas por cada producto: --> <url><loc>http://www.myecommerce.com/index.php?r=item/create&amp;idproduct=2&amp;amp;desc=descripcion aqui del producto </loc><changefreq>weekly</changefreq></url>
</urlset> 

Cómo lee la araña de google al archivo sitemap.xml ?

Leerá cada entrada <URL>..</Url> del archivo sitemap.xml, luego, ira a la URL indicada por la entrada <Loc>, mirará dentro de la URL (por tanto recibirás un GET a tu website proveniente de google) y finalmente analizará el contenido que esta página tenga.  Google hará cada vez que tu lo indiques, no siempre sino cuando tu lo indiques según el parámetro "changefreq".

Ahora,  si los productos cambian en mi website, debo crear cada vez un sitemap.xml nuevo ?

NO. Porque para eso está la tecnología.  Se hará en dos etapas:

1. Primero, pedirle a tu sitio web que mediante un comando haga "echo" (php) de un contenido XML cuando uno se lo pida. es decir, al ejecutar a mano la siguiente URL debe generarse dinámicamente un archivo sitemap.xml. (de nuevo...ojo..no estoy diciendo que vayamos a crear un archivo interno llamado sitemap.xml..no, sino emitir el contenido al browser directamente).

http://www.tuwebsite.com/index.php?r=site/sitemap 

2. Segundo, usando .htaccess, se hara que cuando al sitio web se le pida un archivo sitemap.xml este en cambio retorne lo que la url creada en el paso 1 emita.

para ello se crea en la raiz del site un archivo .htaccess con lo siguiente dentro:

RewriteEngine On
RewriteRule ^sitemap.xml? index.php?r=site/sitemap [L]

Verificacion de .htaccess

importante:  El servidor que aloja la pàgina debe admitir el mod_rewrite, para saber esto crea un archivo info.php con lo siguiente: "<?php echo phpinfo(); ?>" y correlo. Eso emitirá un listado de parametros de tu sitioweb. En ese resultado busca con el browser al palabra "mod_rewrite". Si aparece es porque el servidor soporta archivos .htaccess, si no aparece es porque no tiene instalado ese modulo y este tema no podrá continuarse.

Manejo de Headers para emitir contenido XML

cuando en el browser se tipea:  http://www.tuwebsite.com/index.php?r=site/sitemap

entonces este deberá emitir un archivo xml como salida.  cómo se hace esto ? con el manejo de los headers (al menos en php):

ejemplo:  crea un script cualquiera llamado prueba.php:

<?php 

header("Cache-Control: public");
header("Content-Description: Sitemap");
header("Content-Type: application/xml");


echo "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">";

echo "<url>vacio</url>";

echo "</urlset>";
?>


cuando tu corres:   http://tuwebsite.com/prueba.php  se emitirá un cotenido que el browser iterpreta como XML.  esto es solo una demostración para que comprendas mejor lo que hare a continuación:

Generando el sitemap.xml desde un action en YiiFramework:

public function actionSitemap(){
header("Cache-Control: public");
header("Content-Description: Sitemap");
header("Content-Type: application/xml");

$a = "xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"";

echo "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<urlset $a>\n";

// lista (array) de todos los productos del site
$lista = Product::model()->findAll();

// url base, ejemplo:  http://www.miwebsite.com/
$baseUrl = Yii::app()->params['sitemap_baseurl'];

// frecuencia deseada, ejemplo: weekly
$changeFreq = Yii::app()->params['sitemap_changefreq']; // frecuencia

// por cada producto crear una entrada <url><loc>..</loc></url>
foreach($lista as $p){
$fullUrl = $baseUrl."index.php?r=item/create&idproduct=".$p->getPrimaryKey();
$fullUrl .= "&amp;desc=".$p->publicproductname;
$fullUrl = CHtml::encode($fullUrl);
echo "<url><loc>$fullUrl</loc><changefreq>$changeFreq</changefreq></url>\n";
}
echo "</urlset>\n";
}


Finalmente, si todo sale bien, cuando en tu site se hace esta consulta:

http://www.tuwebsite.com/sitemap.xml  

el browser deberá responder con una pagina en formato XML, indicativo de que todo salió bien.

Lo que sucede internamente es que:

1. tu tipeas:   http://www.tuwebsite.com/sitemap.xml

2. tu servidor recurre al .htaccess para "mapear" tu "request" y convertirlo internamente en:
   http://www.tuwebsite.com/index.php?r=site/sitemap

3. tu action (caso yiiframework) siteController.php::actionSitemap se ejecuta y devuelve con "echo" el contenido del sitemap, y el hecho de haber puesto los "headers" hace que el browser reconozca este contenido como XML.

suerte.

sábado, 4 de agosto de 2012

Control de Usuarios y Roles para Yii Framework


Cruge - Control de Usuarios y ROLES (RBAC) para Yii Framework

En esta oportunidad quiero presentarles a Cruge, es un trabajo -serio- con el propósito de aislar todo el proceso de creación de cuentas de usuario, registro, login, edición de usuarios, asignación y edición de roles, tareas y operaciones para Yii Framework.

Documentación de Cruge

  1. Tutorial de Cruge (ir a la wiki)
  2. Documentación Detallada de Cruge (ir a wiki) (descarga PDF)
  3. Repositorio Bitbucket Oficial de Cruge (ir al repo)
  4. Extensiones para Cruge: (CrugeConnector , CrugeMailer)


screenshots

Programacion PHP / Yii Framework / Cargos y Abonos a Cuentas

Los Cargos y Abonos.

Se conoce por "Sistema de Cargos y Abonos" a aquel componente de software encargado de proveer una interfaz de programación para registrar las entradas y/o salidas regularmente de dinero (pero pudiese ser cualquier valor) para una cuenta.  Para mayor profundidad sería de útil ayuda leer un libro de economía acerca de los conceptos de balance, cuenta, débito y crédito.

Normalmente un sistema de cargo y abono tiene tres funciones muy claras:
  1. Crear un cargo a una cuenta.
  2. Crear un abono a una cuenta.
  3. Consultar una cuenta.

Ejemplo:

Tienes un sistema de administración de condominios, a los propietarios se les hacen cargos mensuales, estos abonan o pagan completo y finalmente consultan cuando deben.

Otros casos, pueden ser muchos, en donde un cliente dice "voy a pagar esta factura...esta....y esta otra"...."cuanto es ?"...."ok aqui va el cheque.".  Quiza este haciendo pagos parciales o pagos totales, o mixtos.

Este sistema de Cargos y Abonos funciona para Yii Framework, el cual considero un muy buen framework PHP de cual estoy muy feliz de conocer.  A pesar de que la configuración es sencilla, la implementación es mas que todo larga, mas que complicada, pero en el fondo simple: Solo tienes que recibir peticiones muy basicas del sistema "guardar esto....insertar esto"...nada mas, no tienes que manejar logicas de negocio y calculos, ni verificaciones nada, todo eso lo hace el modulo, ni siquiera tienes que hacer formularios, estos también los hace el módulo.

Si ves las imagenes de muestra abajo, la interfaz esta hecha con bootstrap...y escribiendo este blog me acabo de acordar que debo documentar eso...porque si pretenden usarla sin bootstrap entonces recibiran un error porque algunos componentes de la UI (ninguno indispensable) estan hechos en base a bootstrap, pero es algo simple de resolver.

Cargo y Abono

sitio oficial:
https://bitbucket.org/christiansalazarh/cargoyabono

@author: Christian Salazar H. christiansalazarh@gmail.com @bluyell
Modulo para el manejo de cargos y abonos a una Persona (natural o juridica).
Es independiente del modelo de datos seleccionado, opera bajo interfaz.
ver diagramas adjuntos en carpeta diseno\
crearcargo crearabonoconsultacuenta

Cómo funciona.

Un sistema de manejo de cargos y abonos casi siempre es lo mismo, con varianzas que pueden abstraerse, dicho de otro modo todos tienen algo en comun: agregar cargos a una cuenta, agregar abonos que neutralizen los cargos y finalmente ver el saldo o listar las cuentas.
Lo que hace este modulo es dejarte a ti solo la responsabilidad de decir donde guardar y de donde leer, siendo el modulo capaz de manejar todo lo demás de forma encapsulada.

Instalación

en config/main.php
'modules'=>array(
    'cargoyabono'=>array(
        'debug'=>true,
        'layout'=>'//layouts/column1',
        'config'=>array(

            // esta es una cuenta, referenciada en el api bajo la palabra KEY
            // se hace asi para que se pueden tener distintos tipos de cuenta
            // en el mismo sistema, cada uno con su grupo de tablas y valores.
            // equivalente a un namespace para el sistema de cargo y abono.
            //
            'cuenta'=>array(
                'persona'=>'Persona',       
                'cuenta'=>'CuentaPersona',
                'historia'=>'HistoriaAbono',
                'cargo'=>1, // codigo que se usa para indicar que es cargo
                'abono'=>2, // codigo para abono
            ),

        ),
    ),
),
'components'=>array(
    'cyaui'=>array('class'=>'application.modules.cargoyabono.components.CyaUI'),
    'cyaApi'=>array('class'=>'application.modules.cargoyabono.components.CyaApi'),

    // necesario porque se usa el tipo de formato 'money' que esta hecho en la clase CyaFormat
    // del modulo cargoyabono
    'format' => array(
        'class'=>'application.modules.cargoyabono.components.CyaFormat',
        'datetimeFormat'=>"d M, Y h:m:s a",
        'dateFormat'=>"d-m-Y",
        'simboloMoneda'=>'Bsf.',
    ),
),

Explicacion de la configuración.

Layout:
Indica que layout se usara para presentar los formularios.
Config:
Es un array, presenta los tipos de cuenta a manejarse.  en una misma aplicacion pueden
haber distintos tipos de cuenta.
    a) El argumento 'persona'=>'Persona' indica que la clase "Persona" sera el objetivo de la 
    cuenta, es decir a quien se le haran los cargos o abonos.
    b) El argumento 'cuenta'=>'CuentaPersona' indica donde se haran los cargos y los abonos relativos
    a la persona seleccionada.

Usandolo

Empiezo por mostrar un sistema de ejemplo que tiene dos tablas
CREATE TABLE zlm_persona (
  idpersona serial,
  rifced VARCHAR(20) NULL ,
  nombre VARCHAR(250) NULL ,
  direccion VARCHAR(100) NULL ,
  telefonos VARCHAR(100) NULL ,
  tipopersona integer,

  PRIMARY KEY (idpersona) )
;

CREATE TABLE zlm_cuentapersona (
  idcuentapersona serial,
  fechahora bigint,
  fecha bigint,
  monto float,
  concepto varchar(512),
  itemno varchar(20),
  idpersona int not null,
  tipocuenta integer,
  idcuentapersonapagada int,
  estatuscuenta integer DEFAULT 0,
  montoabonado double precision DEFAULT 0,

  -- estatus de como se hizo el pago --
  docnum varchar(45),       -- numero del cheque o transfer -- 
  doctipo varchar(20),      -- CHECK, TRANS, DEPOS --
  docentidad varchar(45),   -- nombre de la entidad, nombre del banco --

  CONSTRAINT fk_cuentapersona_persona
    FOREIGN KEY (idpersona )
    REFERENCES zlm_persona (idpersona )
    ON DELETE RESTRICT
    ON UPDATE NO ACTION,

  PRIMARY KEY (idcuentapersona) )
;

CREATE TABLE zlm_historiaabono (
  idhistoriaabono serial,
  idcargo int not null,
  idabono int not null,
  monto float,
  fechahora bigint,

  CONSTRAINT fk_historia_cargo
    FOREIGN KEY (idcargo )
    REFERENCES zlm_cuentapersona (idcuentapersona)
    ON DELETE CASCADE
    ON UPDATE NO ACTION,

  CONSTRAINT fk_historia_abono
    FOREIGN KEY (idabono )
    REFERENCES zlm_cuentapersona (idcuentapersona)
    ON DELETE CASCADE
    ON UPDATE NO ACTION,

  PRIMARY KEY (idhistoriaabono) )
;
Pues bien, pueden ser usadas cualquier tipo de tablas ya que el modulo cargoyabono es abstracto.
En el modelo Persona.php que representa al modelo de datos: persona (arriba sql), se debe implementar una interfaz que el modulo cargoyabono provee:
class Persona extends CActiveRecord implements IcyaPersona
{
    public function cya_buscarPersonas($texto){
        return 
        Yii::app()->db->createCommand()
            ->select()
            ->from($this->tableName())
            ->where("nombre like :patron", array(
                ':patron'=>"%".$texto."%",
            ))
            ->queryAll();
        ;
    }
    public function cya_getobject($obj){
        return array('id'=>$obj['idpersona'],'label'=>$obj['nombre'],'extra'=>$obj['rifced']);
    }
    public function cya_buscarPersona($id){
        return self::model()->findByPk($id);
    }

    ..
    ..
}
En el modelo CuentaPersona.php, se implementan los siguientes metodos de la interfaz:
class CuentaPersona extends CActiveRecord implements IcyaCuenta
{
    const CUENTA_CARGO = 1;
    const CUENTA_ABONO = 2;

    const ESTATUSCUENTA_PENDIENTE = 0;
    const ESTATUSCUENTA_PARCIAL = 1;
    const ESTATUSCUENTA_TOTAL = 2;
    const ESTATUSCUENTA_NOAPLICA = 3;

    // recibe un array con atributos para crear una cuenta nueva de tipo cargo
    //  ejemplo:
    //      [idpersona,1][concepto,hola][fecha,01-08-2012][monto,2000][itemno,4555][key,cuenta]
    public function cya_crearcargo($campos){

        $cargo = new CuentaPersona();
        $cargo->tipocuenta = self::CUENTA_CARGO;
        $cargo->fechahora = time();
        $cargo->fecha = time($campos['fecha']);
        $cargo->monto = 1*($campos['monto']);
        $cargo->concepto = $campos['concepto'];
        $cargo->itemno = $campos['itemno'];
        $cargo->idpersona = $campos['idpersona'];
        $cargo->estatuscuenta = self::ESTATUSCUENTA_PENDIENTE;
        if($cargo->insert()){
            return $cargo->getPrimaryKey();
        }else{
            return null;
        }
    }
    // recibe un array con atributos para crear una cuenta nueva de tipo cargo
    //  ejemplo:
    //      [idpersona,1][concepto,hola][fecha,01-08-2012][monto,2000][itemno,4555][key,cuenta]
    //  por ser un abono, recibe tres campos mas: (a diferencia de crearcargo)
    //      [docnum,1298918291][doctipo,CHECK][docentidad,banco mercantil]
    public function cya_crearabono($campos){

        $cargo = new CuentaPersona();
        $cargo->tipocuenta = self::CUENTA_ABONO;
        $cargo->fechahora = time();
        $cargo->fecha = time($campos['fecha']);
        $cargo->monto = 1*($campos['monto']);
        $cargo->concepto = $campos['concepto'];
        $cargo->itemno = $campos['itemno'];
        $cargo->idpersona = $campos['idpersona'];
        $cargo->estatuscuenta = self::ESTATUSCUENTA_NOAPLICA;

        $cargo->docnum = $campos['docnum'];
        $cargo->doctipo = $campos['doctipo'];
        $cargo->docentidad = $campos['docentidad'];

        if($cargo->insert()){
            return $cargo->getPrimaryKey();
        }else{
            return null;
        }
    }
    /** Lista las cuenta de la persona seleccionada.

        $params:
            es un array de parametros que el API envia a la clase host.
            se cuenta con:
                'pagadas'=>true o false,  
                    para indicar que entrege solo las cuentas pagadas o no.

        se espera que retorne:

            return  self::model()->findAllByAttributes(array('idpersona'=>$idpersona));
    */
    public function cya_listarcuentas($idpersona,$params=array()){
        if(isset($params['pagadas'])){

            if($params['pagadas'] == true){
                $criteria=new CDbCriteria();
                $criteria->compare('idpersona',$idpersona);
                $criteria->compare('tipocuenta',self::CUENTA_CARGO);
                $criteria->compare('estatuscuenta',self::ESTATUSCUENTA_TOTAL,false);
                return self::model()->findAll($criteria);
            }else{
                return self::model()->findAll(
                         'idpersona = '.$idpersona.' and tipocuenta = '.self::CUENTA_CARGO.' and '
                        .'(estatuscuenta = '.self::ESTATUSCUENTA_PENDIENTE.') or '
                        .'(estatuscuenta = '.self::ESTATUSCUENTA_PARCIAL.')'
                    );
            }
        }else{
            return self::model()->findAllByAttributes(array('idpersona'=>$idpersona));
        }
    }
    /** pide al modelo host que devuelva un array con los campos solicitados.

        array('id'=>'x','fecha'=>'x','tipo'=>'x','concepto'=>'x','monto'=>1000,'idpersona'=>1
            ,'tipocuenta'=>'1','tipocuentatxt'=>'CARGO','estatus'=>1,'estatustxt'=>'pendiente'
            ,'montoabonado'=>900,'montopendiente'=>100,'refno'=>'12287',
            ,'docnum'=>'19289812', 'doctipo'=>'check', 'docentidad'=>'banco mercantil')     
    */
    public function cya_getobject($obj){
        return array(
            'id'=>$obj->getPrimaryKey(),
            'fecha'=>$obj->fecha,
            'tipo'=>$obj->tipocuenta,
            'concepto'=>$obj->concepto,
            'monto'=>$obj->monto,
            'refno'=>$obj->itemno,
            'idpersona'=>$obj->idpersona,
            'tipocuenta'=>$obj->tipocuenta,
            'tipocuentatxt'=>$obj->tipocuenta==self::CUENTA_CARGO ? "CARGO" : "ABONO",
            'estatus'=>$obj->estatuscuenta,
            'estatustxt'=>self::etiquetarEstatus($obj->estatuscuenta),
            'montoabonado'=>$obj->montoabonado,
            'montopendiente'=>$obj->monto-$obj->montoabonado,
            'docnum'=>$obj->docnum,
            'doctipo'=>$obj->doctipo,
            'docentidad'=>$obj->docentidad,
        );
    }

    /** registra una historia de abono a un cargo por un valor especifico.

        sirve para registrar que abonos se le hicieron a cual cargo y viceversa.

        idAbono:    
            el identificador primario del abono obtenido con cyaApi.crearAbono

        idCargo:
            el identificador primario del cargo a ser abonado.

        montoAbonado:   
            el valor que se le quiere abonar al cargo

        returns:
            nada
    */
    public function cya_crearhistoriaabono($idAbono,$idCargo,$montoAbonado){
        Yii::app()->db->createCommand()
            ->insert("zlm_historiaabono", array(
                'idabono'=>$idAbono,
                'idcargo'=>$idCargo,
                'fechahora'=>time(),
                'monto'=>$montoAbonado
            ));
    }

    /** suma el monto al cargo indicado, para ser acumulado en cargo.montoabonado.

        deberia ser usada en conjunto con crearHistoriaAbono, para que quede historia
        de los abonos realizados a un cargo.

        idcargo:
            el identificador primario del cargo a ser abonado.
        monto:
            el valor que se quiere sumar a cargo.montoabonado

        returns:
            nada.
    */
    public function cya_actualizarcargo($idcargo,$monto){
        $cargoInst = self::model()->findByPk($idcargo);
        $cargoInst->montoabonado = $cargoInst->montoabonado + $monto;
        $cargoInst->estatuscuenta = self::ESTATUSCUENTA_PARCIAL;
        if($cargoInst->montoabonado >= $cargoInst->monto)
            $cargoInst->estatuscuenta = self::ESTATUSCUENTA_TOTAL;
        $cargoInst->update();
    }

    public function etiquetarEstatus($valor){
        if($valor == self::ESTATUSCUENTA_PENDIENTE)
            return "pendiente";
        if($valor == self::ESTATUSCUENTA_PARCIAL)
            return "parcial";
        if($valor == self::ESTATUSCUENTA_TOTAL)
            return "total";
        if($valor == self::ESTATUSCUENTA_NOAPLICA)
            return "...";

        return "estatus desconocido";
    }

    ...
    ...
}
finalmente la tabla de historia queda asi:
class HistoriaAbono extends CActiveRecord implements IcyaHistoria
{

    /** lista los ABONOS que se hicieron para este CARGO (id)

        ejemplo:
            valor que retorna model()->findAllByAttributes()

        returns:
            array de instancias de clase del modelo de datos
    */
    public function cya_listarhistoriacargo($id){
        return self::model()->findAllByAttributes(array('idcargo'=>$id));
    }

    /** lista los CARGOS que se abonaron con el ABONO indicado (id)

        ejemplo:
            valor que retorna model()->findAllByAttributes()

        returns:
            array de instancias de clase del modelo de datos
    */
    public function cya_listarhistoriaabono($id){
        return self::model()->findAllByAttributes(array('idabono'=>$id));
    }

    /** obtiene los valores del objeto en forma de array.

        obj:
            instancia del modelo de datos recibida por funciones cya_listarhistoriaXXXX()

        returns:
            array con lista de campos:

            id:         identificador primario del registro de historia
            idcargo:    identificador primario de la cuenta cargo
            idabono:    identificador primario de la cuenta abono
            fechahora:  valor numerico del timestamp de fechahora
            monto:      valor float del monto abonado
            adata:      data (string) serializada del registro abono referenciado
            cdata:      data (string) serializada del registro cargo referenciado
    */
    public function cya_getobject($obj){
        return array(
            'id'=>$obj->getPrimaryKey(),
            'idcargo'=>$obj->idcargo,
            'idabono'=>$obj->idabono,
            'fechahora'=>$obj->fechahora,
            'monto'=>$obj->monto,
            'adata'=>serialize($obj->idabono0),
            'cdata'=>serialize($obj->idcargo0),
        );
    }

    ...
    ...
}

Muy bien todo esta configurado, pero ahora cómo lo uso ?

Existen dos componentes configurados, estos podrian servirte para hacer consultas a bajo nivel.
<?php 
    echo CHtml::link("crear cargo"  
    ,Yii::app()->cyaui->getCrearCargoUrl('cuenta'))); ?>

usalo para que se cree un enlace "crear cargo" que lanzara el formulario de nuevo cargo. El argumento "cuenta" hace referencia a la entrada en config main llamada 'cuenta', la cual indica cuales seran las clases
involucradas.

<?php 
    echo CHtml::link("crear abono"  
    ,Yii::app()->cyaui->getCrearAbonoUrl('cuenta'))); ?>

usalo para que se cree un enlace "crear abono" que lanzara el formulario de nuevo abono. El argumento "cuenta" hace referencia a la entrada en config main llamada 'cuenta', la cual indica cuales seran las clases
involucradas.

<?php 
    echo CHtml::link("consultar"    
    ,Yii::app()->cyaui->getConsultarUrl('cuenta'))); ?>

usalo para que se cree un enlace que muestra la consulta de la cuenta.
El componente Yii::app()->cyaApi te provee funciones de acceso de bajo nivel, no todas son de uso libre, algunas son usadas internamente por el modulo, pero una como esta puede servir:
<?php 
    $saldo = Yii::app()->cyaApi->calculaSaldo('cuenta',$idpersona,&$totalCargos,&$totalAbonos); 
?>
Puedes hallar documentacion de cada metodo del api con un ejemplo en components/CyaApi.php

Resumen

  • Como muestro arriba, la unica responsabilidad del modelo de datos, de tu aplicacion, es decir donde y como vas a guardar la informacion que el modulo requiere.
  • Si te das cuenta ambas interfaces proveen un metodo llamado "public function cya_getobject($obj)", este funciona asi:
    cuando el api interna de CargoyAbono quiere conocer digamos, la lista de cuentas, o una lista de personas invoca a tus metodos implementados en tus modelos para traerse la lista original de instancias,pero no toca ninguna..porque el modulo no conoce y no debe saber que campos tienes ahi, por tanto de nuevo, te pregunta a ti mediante cya_getobject($obj) para que tu devuelvas lo que el modulo requiere para la instancia especifica $obj.
  • Como un ejemplo en pseudocodigo, lo que haria seria como esto:
    modulo_pidiendo_lista_de_cuentas:
    
        $lista = interfaz.cya_listarcuentas(idpersona);
        // lista trae un monton de instancias que tu devolviste en crudo con findAllByAttributes(..)
    
        $item = array();
    
        foreach($lista as $obj)
            $item[] = interfaz.cya_getobject