Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > Varios
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 02-05-2020
javicho_villa javicho_villa is offline
Miembro
 
Registrado: feb 2005
Ubicación: Lima - Perú
Posts: 99
Poder: 20
javicho_villa Va por buen camino
Smile Funcion Get usando RTTI

Buenos dias con todos, siempre es un gusto contactarlos, en esta oportunidad estoy tratando de hacer una función generica Get para que me devuelva un objeto de cualquier tipo a traves de una consulta con un Query.
por ejemplo:
1. Creo una clase TCliente
Código Delphi [-]
type
  TCliente = class(TObject)
    private
      Id_Cliente:Integer;
      RazonSocial:String;
      RazonSocialCorta:String;
...
...
    public
      Constructor Create;
      procedure Clear;
      property iId_Cliente:Integer           read Id_Cliente           write Id_Cliente;
      property iRazonSocial:String           read RazonSocial          write RazonSocial;
      property iRazonSocialCorta:String      read RazonSocialCorta     write RazonSocialCorta;
...
...
   End;

2. Creo una function con parametros del nombre de la Tabla y el numero del registro. por ejemplo la tabla se llama Cliente y el Id es el numero 1, estos datos los pongo cuando invoco la función:

Código Delphi [-]
var Obj :TObject;
begin

  Obj := TObject.Create;
  Obj := Dm.GetObject(1,'Cliente');

  showmessage('Razon Social: '+ (Obj as TCliente).iRazonSocial);

aquí les paso la funcion GetObject que es la quiero implementar
Código Delphi [-]

function TDM.GetObject(Id: integer; TableName: String): TObject;
var ClassName,Cad,CProp:String;
    Obj:TObject;
    MiQuery: TFDQuery;
    Campos, Propiedad : TStringList;
    lProp    : TRTTIProperty;
    lContext,C : TRTTIContext;
    lType    : TRTTIType;
    Value: TValue;
    Pos1,Pos2:integer;
begin

  Campos     := TStringList.Create;
  Propiedad  := TStringList.Create;
  Cad           := 'select * from '+TableName;
  ClassName := 'T'+TableName;

  MiQuery := TFDQuery.Create(nil);
  MiQuery.Connection := Connection;
  MiQuery.SQL.Clear;
  MiQuery.SQL.Add(Cad);
  MiQuery.Open;

  //-- Iniciando extraccion de las propiedades de las clases --//
  LContext := TRttiContext.Create;
  C := TRttiContext.Create;

  Obj := (C.FindType('Modelo_Cliente.'+ClassName) as TRttiInstanceType).MetaClassType.Create;
  lType    := lContext.GetType(Obj.ClassInfo);   

  for lProp in LType.GetProperties do  /// Aca guardo las propiedades en un StringList llamado Propiedad
  begin
    Cad  := lProp.ToString;
    Pos1 := Pos('i',Cad);
    Pos2 := Pos(':',Cad);
    CProp := Copy(Cad,Pos1,Pos2-Pos1);
    Propiedad.Add(CProp);
  end;


  if MiQuery.RecordCount>0 then
    begin
      //--- Poniendo los campos en el arreglo --///
      Connection.GetFieldNames('','',TableName,'',Campos);   //--- Aca hay que poner los campos del Query ----//

      //--- Poniendo las propiedades del Objeto ---//
      //(Obj as ClassName).Propiedad[2] := '123456';
      Value := '123456789';


      for lProp in LType.GetProperties do  //// ACA ESTA EL ERROR NO PUEDO GUARDAR LOS VALORES EN EL OBJETO
      begin                                            /// NOSE COMO USAR EL PROPERTY O CUALQUIER METODO PARA MODIFICAR LOS ATRIBUTOS DEL OBJETO.
        Cad  := lProp.ToString;
        //showmessage(Cad);
        if Cad = 'property iId_Cliente: Integer' then
          lProp.SetValue(Obj,Value);
      end;

      //Lprop.SetValue(Obj,'12345');


      //showmessage(Campos[5]);
      //showmessage(Propiedad[4]);

    end;

  result := Obj;
end;

Como comentaba en lineas arriba no se como modificar los atributos del objeto Obj para poder devolver el objeto con todos sus valores.
todo esto lo quiero hacer para cualquier tabla y cualquier tipo de objeto, para esto debo indicar que si creo una tabla Kardex también creo una clase TKardex en un .pas que se llamaría Modelo_Kardex, ese es un standard que sigo.

Les pido me den una mano para poder solucionar este problema, este metodo me serviria para extraer cualquier objeto de cualquier tabla.

Muchas gracias de antemano por leer esta consulta y gracias por su tiempo.

Saludos,

Javier Villa.
__________________
Javier Villa Sánchez
jvilla@andreaproducciones.com
Responder Con Cita
  #2  
Antiguo 02-05-2020
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Poder: 29
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Hola, Javier.

Sin afán de molestar, veo un poco vago tu avance de esa implementación, aunque celebro cada vez que alguien se pone a trabajar con los valiosos recursos de la RTL. Por lo visto quieres implementar algo similar al "streamming" de Delphi o más formalmente persistencia de objetos: lectura/escritura de instancias de clases en un medio de almacenamiento.

No es que sea imposible, hay varias formas de conseguirlo, pero primero debes superar barreras más elementales. Por ejemplo, al hacer esto:
Cita:
Empezado por javicho_villa Ver Mensaje
Código Delphi [-]
var Obj :TObject;
begin

  Obj := TObject.Create;
  Obj := Dm.GetObject(1,'Cliente');
causas que la instancia creada con TObject.Create quede en el limbo, porque inmediatamente reemplazas el valor de la variable Obj con otra instancia (la devuelta por el método GetObject). Así que ya desde ahí parten los problemas.

Quizá deberías, por un lado, estudiar y practicar un poco más la POO en Delphi. Y, por otro, compartir cómo llegaste a la conclusión de que necesitas implementar persistencia de objetos en tu aplicación. Claro, si te nace darnos a conocer el panorama general de tu idea.

Saludos. :-)
Responder Con Cita
  #3  
Antiguo 03-05-2020
Avatar de gatosoft
[gatosoft] gatosoft is offline
Miembro Premium
 
Registrado: may 2003
Ubicación: Bogotá, Colombia
Posts: 833
Poder: 21
gatosoft Va camino a la fama
Cita:
Empezado por javicho_villa Ver Mensaje
Buenos dias con todos, siempre es un gusto contactarlos, en esta oportunidad estoy tratando de hacer una función generica Get para que me devuelva un objeto de cualquier tipo a traves de una consulta con un Query.

Es una iniciativa interesante, en mi opinión es un inicio hacia un ORM, lo cual no es algo tan sencillo de implementar,y menos si no se trabaja con una concepción mas orientada a objetos, es decir el get es solo una parte de todo el proceso.

Me llama la atención lo que haces con el prefijo i para identificar las propiedades del objeto, o el mismo hecho de que la. En lugar de eso, te invito a que revises la documentación asociada a TCustomAttribute

es la forma como puedes etiquetar tus clases, métodos, propiedades y darles un tratamiento.

Por ejemplo


Código Delphi [-]
[TMyORMField('GEN_CLIENTES')]
TCliente = Class(TObject)
Private
Public
end;


Con esto, puedes indicarle a tu sistema que la clase definida tiene un atributo de tipo TMyORMField (creado por ti) que indica algo, en este caso tu decides que te va a representar el nombre de la tabla en la base de datos. con esto, tu clase y tu tabla pueden llamarse diferente.


De igual forma puedes agregar atributos a tus "Fields" y "properties", asi:

Código Delphi [-]
    [TMyORMField('GEN_CLIENTES')]
    TCliente = Class(TObject)
    Private

    Public

    [TMyORMField('CLI_RUT')]
  RUT: String; //este es un field, a diferencia de las properties, no tienen getters and setters
    
    [TMyORMField('CLI_ID',True,True)]  
  property Id_Cliente:Integer  read FId_Cliente           write FId_Cliente;
    
  [TMyORMField('CLI_RAZONSOCIAL')]   
  property RazonSocial:String           read FRazonSocial          write FRazonSocial;
  
  Property FechaConstitucion: TDateTime read FFechaConstitucion write FFechaConstitucion
    
  [TMyORMField('CLI_RAZONSOCIALCORTA')]   
  property RazonSocialCorta:String      read FRazonSocialCorta     write FRazonSocialCorta;
  
  end;

Mira que aqui utilizo el mismo atributo TMyORMField (podrian ser diferentes), con la diferencia que uno opera sobre la clase, indicando el nombre de la tabla y el otro indica el nombre del campo y otros datos.

Fijate tambien que los atributos pueden ir sobre properties o sobre fields.

En el caso del ID_Cliente, recibe dos parametros mas de tipo boleano (o cualquier tipo), tu decides que interpretación le puedes dar, por ejemplo, el primero podria indicar si es un campo requerido dentro de tu tabla y el segundo si es parte de la llave primaria.

Otra opcion es utilizar multi-atributos para indicar esto

Código Delphi [-]
[TMyORMField('CLI_RAZONSOCIAL')] 
[TMyORMFieldFlags('ESLLAVE')] 
Property Id_Cliente


Para llegar a estom a grosso modo debes definir una calse para el atributo:

Código Delphi [-]
TMyORMField = class(TCustomAttribute)
  private
    FIsKeyField: Boolean;
    FFieldName: String;
    FIsNotNull: Boolean;
    procedure SetFieldName(const Value: String);
    procedure SetIsKeyField(const Value: Boolean);
    procedure SetIsNotNulll(const Value: Boolean);
  public
   Property FieldName: String read FFieldName write SetFieldName;
   Property IsNotNull: Boolean read FIsNotNull write SetIsNotNulll;
   Property IsKeyField: Boolean read FIsKeyField write SetIsKeyField;
   Constructor Create(pFieldName: String;
                      pIsNotNulll: Boolean = False;
                      pIsKey: Boolean = False
                      ); reintroduce;
   Procedure Inicializar; //digamos para asignar valores por defecto
  end;//TMyORMField


Posteriomente en tu "getObjeto(pObjeto: TObject)"

debes revisar estas properties:


Código Delphi [-]
function TMyORMEngine.getAtributosObjeto: Boolean; 
//TMyORMEngine, deberia ser la clase que hace todo el trabajo sucio con los objetos
var ctx : TRttiContext;
    rt : TRttiType;
    prop : TRttiProperty;
    xfield: TRttiField;
    Attr: TCustomAttribute;
  
  
    vCampos: TCampo; //puedes guardar lo que encuentres en una lista de campos... 
                   //TList, o en un StringList (depende de lo que quieras) 
begin
 Result:= False;

 if not Assigned(Objeto) then //objeto es una variable que le asignas a tu TMyORMEngine
    exit;

 FTabla:='';
 LimpiarCampos; //la lista

 Try
   ctx := TRttiContext.Create();
   rt := ctx.GetType(Objeto.ClassType);
   
   //buscas los atributos de tus properties
   for prop in rt.GetProperties() do
     for Attr in prop.GetAttributes() do
        if Attr is TMyORMField then
           Begin
             Result:= True;
             vCampos:= TCampo.Create;       
       //le indico que esto es una property. este valor lo defiens tu con
       // TipoCampo = (ptProperty, ptField)
             vCampos.PropType:= ptProperty; 
             vCampos.Name:= prop.Name;
             vCampos.FieldName:= (Attr as TMyORMField).FieldName;
             vCampos.IsNotNull:= (Attr as TMyORMField).IsNotNull;
             vCampos.IsKeyField:= (Attr as TMyORMField).IsKeyField;
             FlstCampos.Add(vCampos);
           End; //if

    //buscas los atributos de tus Fields
    for xfield in rt.GetFields do
      for Attr in xfield.GetAttributes do
        if Attr is TMyORMField then
           Begin
             Result:= True;
             vCampos:= TgtsCampo.Create;
             vCampos.PropType:= ptField;
             vCampos.Name:= xfield.Name;
             vCampos.FieldName:= (Attr as TMyORMField).FieldName;
             vCampos.IsNotNull:= (Attr as TMyORMField).IsNotNull;
             vCampos.IsKeyField:= (Attr as TMyORMField).IsKeyField;
             FlstCampos.Add(vCampos);
           End; //if

    //buscas los atributos de la clase
    for Attr in rt.GetAttributes do
      if Attr is TMyORMField then
         begin
          Result:= True;
          FTabla:= (Attr as TMyORMField).FFieldName;
         end;
 Finally
   ctx.Free;
 End;//Try..finally
end;

la asignacion de valores , que es lo que creo que quieres la harias con


Código Delphi [-]
procedure TMyORMEngine.AsignarPropiedades(pDataSet: TDataSet);
Var i:Integer;
    vCampo: TgtsCampo;
begin

  if (not Assigned(pDataSet)) or
     (not pDataSet.Active) or
     (pDataSet.IsEmpty) then
     exit;

  if not Assigned(Objeto) then
     exit;

  pDataSet.First; //solo el primer registro... para el demo
  For i:=0 to pDataSet.FieldCount-1 do
  begin
    vCampo:= getCampoByFieldName(pDataSet.Fields[i].FieldName); //tares el campo listado
    if Assigned(vCampo) then
       begin
        if vCampo.IsKeyField then
           Continue;

        if vCampo.PropType = ptProperty then
           SetPropValue(vCampo.Name, pDataSet.Fields[i].Value);

        if vCampo.PropType = ptField then
           SetFieldValue(vCampo.Name, pDataSet.Fields[i].Value);
       end;
  end;//for i

end;

SetField y setProp:

Código Delphi [-]
procedure TMyORMEngine.SetFieldValue(pName: String; pValor: Variant);
var ctx : TRttiContext;
    rt : TRttiType;
    xfield: TRttiField;
begin
 if not Assigned(Objeto) then
    exit;
 ctx := TRttiContext.Create();
 Try
   rt := ctx.GetType(Objeto.ClassType);
   xfield := rt.GetField(pName);
   if Assigned(xfield) then
      xfield.SetValue(Objeto, TValue.From(pValor));
 Finally
   ctx.Free;
 End;

end;

procedure TMyORMEngine.SetPropValue(pName: String; pValor: Variant);
var ctx : TRttiContext;
    rt : TRttiType;
    prop : TRttiProperty;
begin
 if not Assigned(Objeto) then
    exit;
 ctx := TRttiContext.Create();
 Try
   rt := ctx.GetType(Objeto.ClassType);
   prop := rt.GetProperty(pName);
   if Assigned(prop) then
      prop.SetValue(Objeto, TValue.From(pValor));
 Finally
   ctx.Free;
 End;
end;


Lo anterior es un extracto de algunas pruebas que hice pensando tal vez en lo mismo que tu quieres, pero solo a manera de ejercicio... funcionó lo que hice, pero nunca lo implementé. Como te dije lo anterior es un "a grosso modo"

Con la clase TMyORMEngine, iban muchos, muchos metodos y propiedades, como la persistencia en la DB a partir de los datos obtenidos... o la asignacion de sentencias SQL custom (insert, update, delete),

Como te digo, GetObject es solo una tarea de todo lo que deberias organizar sobre este tema.

Última edición por gatosoft fecha: 03-05-2020 a las 10:40:20. Razón: etiquetas delphi
Responder Con Cita
  #4  
Antiguo 04-05-2020
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is offline
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 18.275
Poder: 10
Neftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en bruto
Tal como han dicho, estás incursionando en un ORM o un "Framework de Persistencia".
La idea es interesanta, pero a mi entender te falta completar "la pieza" importante.

Tienes:
1) La clase TCliente. No se si realmente la tienes así definida o simplemnte la has puesto así aquí para simplificar, pero un primer consejo es que todas tus clases de "persistenca" deriven de una clase base (si no lo tienes así).
2) Tienes la tabla de CLIENTE con sus campos.
3) Tienes la pieza que te "convierte" un elemento de Entidad-Relacion a un Objeto persistente.

A esta clase, a mi entender, le falta información.
Está claro que el resultado de la ejecución (en tu caso) debe ser un objeto TCliente. De alguna forma esta función debe llegar a conocer la referencia a la clase TCliente, de forma que el objeto que se cree no sea utilizando TObject sino la referencia a esa clase.
  • Una primera prueba que podrías hacer, sería pasar esa referencia como parámetro del procedimiento.
  • Una segunda, sería conseguir la referencia de la clase (GetClass) utilizando RTTI. Para ello deberás registrar las clases.
Revisa esta documentación:
http://docwiki.embarcadero.com/RADSt...ass_References
__________________
Germán Estévez => Web/Blog
Guía de estilo, Guía alternativa
Utiliza TAG's en tus mensajes.
Contactar con el Clubdelphi

P.D: Más tiempo dedicado a la pregunta=Mejores respuestas.
Responder Con Cita
  #5  
Antiguo 27-05-2020
javicho_villa javicho_villa is offline
Miembro
 
Registrado: feb 2005
Ubicación: Lima - Perú
Posts: 99
Poder: 20
javicho_villa Va por buen camino
Smile Me han dado una buena luz para continuar

Muchas gracias por todas las respuestas.

Quiero comentarles que defino en archivo .pas las clases que defino, por ejemplo: Modelo_Cliente.pas. Aquí defino las clase TCliente. en otro archivo .pas llamado Cx_Cliente, defino funciones que me devuelvan valores consultando la base de datos, por ejemplo:

Código Delphi [-]
{ TCx_Cliente }

class function TCx_Cliente.GetCliente(Id: integer): TCliente;
var iCliente:TCliente;
    MiQuery: TAdoQuery;
begin
  iCliente := TCliente.Create;
  MiQuery  := TAdoQuery.Create(nil);
  MiQuery.SQL.Clear;
  MiQuery.Connection := Datos.DatosDM.CabeceraADOQuery.Connection;
  MiQuery.SQL.Clear;
  MiQuery.SQL.Add('select * from Cliente where Id_Cliente='+IntToStr(id));
  MiQuery.Active := true;
  if MIQuery.RecordCount>0 then
    begin
      MiQuery.First;
      iCliente.iId_Cliente           := Id;
      iCliente.iRazonSocial          := MiQuery.FieldByName('RazonSocial').AsString;
      iCliente.iRazonSocialCorta     := MiQuery.FieldByName('RazonSocialCorta').AsString;
....
      iCliente.iFormaPago            := MiQuery.FieldByName('FormaPago').AsString;
      iCliente.iTituloDoc1           := MiQuery.FieldByName('TituloDoc1').AsString;
      iCliente.iTituloDoc2           := MiQuery.FieldByName('TituloDoc2').AsString;
      iCliente.iTituloDoc3           := MiQuery.FieldByName('TituloDoc3').AsString;
      iCliente.iId_Usuario           := MiQuery.FieldByName('Id_Usuario').AsInteger;
      //if length(MiQuery.FieldByName('FRegistro').AsString)>0 then
        //iCliente.iFRegistro     := MiQuery.FieldByName('FRegistro').AsDateTime;
.....
    end;
  MiQuery.Free;
  result := iCliente;
  iCliente := TCliente.Create;
  iCliente.Free;
end;

tambien tengo procedimientos almacenados de sql que lo invoco de esta manera:

Código Delphi [-]
class function TCx_Cliente.ClienteTerceroInsertar(Tercero: TClienteTercero): String;
var MiProcedimiento:TADOStoredProc;
    MyClass:TComponent;
    MiError:String;
begin
  MiProcedimiento := TADOStoredProc.Create(nil);
  MiProcedimiento.Connection := DatosDM.PrincipaADOConnection;
  MyClass := TComponent.Create(nil);
  MiError := '';
  try
    with MiProcedimiento do
      begin
        Close;
        Parameters.Clear;
        ProcedureName := 'ClienteTerceroInsertar';
        //Parameters.ParseSQL('ClienteTerceroInsertar',true);
        //Parameters.CreateParameter('@Id_ClienteTercero',ftInteger,pdInput,0,0);
        Parameters.CreateParameter('@Id_Cliente',ftInteger,pdInput,0,0);
        Parameters.CreateParameter('@Id_ParametroTipoTercero',ftInteger,pdInput,0,0);
        Parameters.CreateParameter('@RazonSocial',ftString,pdInput,100,0);
        Parameters.CreateParameter('@RazonSocialCorta',ftString,pdInput,50,0);
        Parameters.CreateParameter('@RUC',ftString,pdInput,20,0);
        Parameters.CreateParameter('@Direccion',ftString,pdInput,100,0);
        Parameters.CreateParameter('@DNI',ftString,pdInput,20,0);
        Parameters.CreateParameter('@Brevete',ftString,pdInput,20,0);
        Parameters.CreateParameter('@Marca',ftString,pdInput,20,0);
        Parameters.CreateParameter('@Placa',ftString,pdInput,10,0);
        Parameters.CreateParameter('@CertificadoVehicular',ftString,pdInput,20,0);
        Parameters.CreateParameter('@Error',ftString,pdOutPut,100,0);
        Parameters.ParamByName('@Id_Cliente').Value              := Tercero.iId_Cliente;
        Parameters.ParamByName('@Id_ParametroTipoTercero').Value := Tercero.iId_ParametroTipoTercero;
        Parameters.ParamByName('@RazonSocial').Value             := Tercero.iRazonSocial;
        Parameters.ParamByName('@RazonSocialCorta').Value        := Tercero.iRazonSocialCorta;
        Parameters.ParamByName('@RUC').Value                     := Tercero.iRUC;
        Parameters.ParamByName('@Direccion').Value               := Tercero.iDireccion;
        Parameters.ParamByName('@DNI').Value                     := Tercero.iDNI;
        Parameters.ParamByName('@Brevete').Value                 := Tercero.iBrevete;
        Parameters.ParamByName('@Marca').Value                   := Tercero.iMarca;
        Parameters.ParamByName('@Placa').Value                   := Tercero.iPlaca;
        Parameters.ParamByName('@CertificadoVehicular').Value    := Tercero.iCertificadoVehicular;
        Parameters.ParamByName('@Error').Value                   := '';
        ExecProc;
      end;
      MiError := MiProcedimiento.Parameters.ParamByName('@Error').Value;
    finally
    MyClass.Free;
  end;
  result := MiError;
  MiProcedimiento.Free;
end;

este procedimiento lo repito para cualquier tabla por ejemplo para las ventas tengo una clase TVenta guardado en un archivo Modelo_Venta, ademas de los procedimientos y funciones en un archivo Cx_Venta donde me conecto a la base de datos y hago consultas y modificaciones a la tabla venta.

Lo que quiero hacer es lo mismo pero un solo archivo donde pueda invocar los valores de cualquier tabla, es decir si es Cliente, Venta, etc, pero usando los objetos pertinentes. entonces hago un GetObject(Id: integer; TableName: String): TObject; y me devuelve un objecto de tipo TObject.

quiero volver agradecer por su tiempo y ojala me puedan ayudar a realizar esta operación, que me ahorraría muchas lineas de código al hacer cualquier proyecto.

Saludos,

Javier Villa.
__________________
Javier Villa Sánchez
jvilla@andreaproducciones.com
Responder Con Cita
  #6  
Antiguo 28-05-2020
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is offline
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 18.275
Poder: 10
Neftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en bruto
Cita:
Empezado por javicho_villa Ver Mensaje
...defino en archivo .pas las clases que defino, por ejemplo: Modelo_Cliente.pas. Aquí defino las clase TCliente. en otro archivo .pas llamado Cx_Cliente, defino funciones que me devuelvan valores consultando la base de datos

...
este procedimiento lo repito para cualquier tabla por ejemplo para las ventas tengo una clase TVenta guardado en un archivo Modelo_Venta, ademas de los procedimientos y funciones en un archivo Cx_Venta donde me conecto a la base de datos y hago consultas y modificaciones a la tabla venta.
...
Lo que quiero hacer es lo mismo pero un solo archivo donde pueda invocar los valores de cualquier tabla, es decir si es Cliente, Venta, etc, pero usando los objetos pertinentes.

Lo ideal sería que tuvieras un objeto que defina cada objeto de clase, Modelo_Cliente.pas (TCliente), Modelo_Venta.pas (TVenta),... y que tal como dices, puedas tener centralizado en un único lugar el procedimiento que recupera los campos de una tabla en el objeto correspondiente.

Tal como lo tienes ahora, el problema es que, debes tener en algún sitio la correlación entre los campos de la tabla y las propiedades de cada objeto. como no la tienes en ningún sitio, implementas un procedimiento por cada una de esas parejas (Cx_Cliente, Cx_Venta,...).

Creo que puedes conseguir eso, en la propia clase TCliente, TVenta,... utilizando RTTI y atributos.

De esa forma en un único lugar (procedimiento/función) podrás cargar los campos de la tabla en el objeto relacionado.
Sería algo así:

Código Delphi [-]
type
  TBaseObject = class
    ...
  protected
    procedure FillObjectFromdataBase;
  end;

  [AttTableName('CLIENTES')]
  TCliente = class (TBaseObject)
  private
    FiFormaPago: String;
    FiRazonSocial: string;
    FiId_Cliente: integer;
  published
    [AttrFieldName('ID')]
    [AttrDescripcion('Identificador del cliente')
    property iId_Cliente:integer read FiId_Cliente;
    [AttrFieldName('RazonSocial')]
    [AttrDescripcion('Razón social del cliente')
    property iRazonSocial:string read FiRazonSocial;
    [AttrFieldName('FormaPago')]
    [AttrDescripcion('Forma de pago del cliente')
    property iFormaPago:String read FiFormaPago;
    ...
  end;

  [AttrTableName('VENTAS')]
  TVentas = class (TBaseObject)
  published
    [AttrFieldName('ID')]
    [AttrDescripcion('Identificador del cliente asiciado à la venta')
    property iId_Venta:integer read FiId_Venta;
    [AttrFieldName('ID_venta')]
    [AttrDescripcion('Identificador del cliente asiciado à la venta')
    property iId_Cliente:integer read FiId_Cliente;
    ...
  end;


El procedmiento:
Código Delphi [-]
procedure FillObjectFromdataBase;

Definido en la clase base (TBaseObject) debería poder acceder al atributo de cada clase (AttTableName) para conocer la tabla, y a las propiedades published del objeto vía RTTI.
Para cada propiedad puedes conocer su atributo (AttrFieldName) para acceder al campo.

De esta forma, podrías utilizar:

Código Delphi [-]
var
  cli:Tcliente;
  venta:TVentas;

begin
  ...
  cli.FillObjectFromdataBase;
  ...
  venta.FillObjectFromdataBase;
  ...

Bueno, más o menos esa es la idea. No se si me he explicado bien.

Tienes información sobre cómo definir atributos aquí:
http://docwiki.embarcadero.com/RADSt...tom_Attributes

Cómo acceder a ellos en runtime:
http://docwiki.embarcadero.com/RADSt...es_at_Run_Time
__________________
Germán Estévez => Web/Blog
Guía de estilo, Guía alternativa
Utiliza TAG's en tus mensajes.
Contactar con el Clubdelphi

P.D: Más tiempo dedicado a la pregunta=Mejores respuestas.
Responder Con Cita
  #7  
Antiguo 19-07-2021
javicho_villa javicho_villa is offline
Miembro
 
Registrado: feb 2005
Ubicación: Lima - Perú
Posts: 99
Poder: 20
javicho_villa Va por buen camino
Smile Uso de RTTI - Solución encaminada

Muchas gracias por todas sus respuestas anteriores, me han dado mucha luz por donde debo ir, y la solución que hasta va bien es usar RTTI.
El objetivo es crear funciones clasicas como ObjectInsert(Obj: TObject):String, de iguakl manera un ObjectUpdate; ObjectDelete y GetObject:TObject;

Para el caso de ObjectInsert, lo ha desarrolado un amigo y hasta funciona bien, pero lo estoy volviendo hacer para poder incorporar la opción de maestro/detalle y tambien que guarde en una tabla llamada BITACORA para poder controlar cualquier movimiento de los usuarios.

Código Delphi [-]
function TDm.ObjectInsert(Obj: TObject): String;
var
  MiProcedimiento: TFDStoredProc;
  MiError:String;
  Detalle : widestring;
  ListaProp: TStringList;
  ListaParam: TStringList;
  ListaMetaInfo : TStringList;
  NombreProc: string;
  NombreProp: string;
  NombreParam: string;
  TipoProp  : string;
  TipoDato  : string;
  TipoIO    : string;
  AnchoCampo: string;
  contexto : TRTTIContext;
  propiedad : TRTTIProperty;
  valor : TValue;
  I, J : word;

  function BuscaLong(B: string): string;
  var
    Encontrado : boolean;
    Nombre: string;
    Tipo : string;
    Ancho:string;
    J: Integer;
  begin
    Encontrado := False;
    for J := 0 to Pred(ListaMetaInfo.Count) do
    begin
       if B = Copy(ListaMetaInfo[J], 1, Length(B)) then
       begin  // := Copy(ListaMetaInfo[J], 1, Length(NombreBuscar)) then
         Encontrado := True;
         Break;
       end;
    end;
    if Encontrado then
      TLibreria.ParseMetaInfo(ListaMetaInfo[J], Nombre, Tipo, Ancho)
    else
      Ancho := '100';
    result := Ancho
  end;

  function BuscaProp(B: string): integer;
  var
    Encontrado : boolean;
    Nombre: string;
    Tipo : string;
    Ancho:string;
    J: Integer;
  begin
    Encontrado := False;
    for J:= 0 to Pred(ListaProp.Count) do
    begin
      if Copy(B, 2, Length(B)) = Copy(ListaProp[J], 2, Length(B)-1) then
      begin
        Encontrado := True;
        Break;
      end;
    end;
    if Encontrado then
      result := J
     else
       result := -1;
  end;

begin
  MiError := '';
  ListaProp  := TStringList.Create;
  ListaParam := TStringList.Create;

  if not Assigned(Obj) then
    exit;

  try
    MiProcedimiento := TFDStoredProc.Create(nil);
    MiProcedimiento.Connection := DM.EmpresaConnection;  // SislibroConnection

//  1- Carga los propiedades de la clase
    ListaProp := TLibreria.DaListaPropConTipo(Obj);

    try
      with MiProcedimiento do
      begin
        Close;
        Params.Clear;

//  2- Ubicar el store procedure de la tabla  + INSERTAR
        NombreProc := Copy(Obj.ClassName, 2, Length(Obj.ClassName)-1) + 'Insertar';
        StoredProcName := NombreProc;
        FetchOptions.Items := MiProcedimiento.FetchOptions.Items - [fiMeta]; // quitar fiMeta para insertar parametros manualmente

//  3- Conseguir los parametros del store procedure
//--   Para el caso de Sislibro se extrae del componente EmpresaConecction --//
       ListaParam := DM.DaListaParam(Dm.EmpresaConnection.Params.Database , NombreProc);

//  4- Obtiene los meta datos de la tabla
        ListaMetaInfo := TLibreria.DaListaMetaInfo(Copy(Obj.ClassName, 2, Length(Obj.ClassName)-1));

//  5- Crear los parametros en el FDStoredProc dinamico y pasa el valor a los parametros
        contexto := TRTTIContext.Create;

//        showmessage(IntToStr(ListaParam.Count));

        for I := 0 to Pred(ListaParam.Count) do
        begin
          TLibreria.ParseParam(ListaParam[i], NombreParam, TipoDato, TipoIO);  // separa nombre de tipo
          AnchoCampo := BuscaLong( Copy(NombreParam, 2, length(NombreParam)-1)); // Busca ancho de campo de la tabla
          if (TipoDato = 'varchar') And (StrToInt(AnchoCampo) = 8000) then   // si longitud de parametro = MAX
          begin                 {Wide}
            Params.CreateParam( ftWideString,  NombreParam, TLibreria.StrToParamType(TipoIO));  // se crea con widestring
            for J := 0 to Pred(ListaProp.Count) do // busca el tipo de la propiedad iDetalle
            begin
              TLibreria.ParseProp(ListaProp[J], NombreProp, TipoProp);        // separa nombre de tipo
              if TipoProp = 'TObjectList' then  // si es del tipo TObjectList
              begin  // obtiene string con datos y los pasa al paremetro
                Detalle := Dm.GetDetalleText(TObjectList(contexto.GetType(Obj.ClassType).GetProperty(NombreProp).GetValue(Obj).  AsObject));
                Params.ParamByName( NombreParam).Value :=  Detalle;
              end;
            end;
          end
          else // de lo contrario
          begin // procesa los demas campos
            Params.CreateParam( TLIbreria.StrToFieldType(TipoDato), NombreParam, TLibreria.StrToParamType(TipoIO)); // crea paramero
            if (Uppercase(NombreParam) = '@ERROR') then  // si es campo @Error
            begin // pasa datos en blanco
              Params.ParamByName( NombreParam).Value :=  '';
            end
            else   // sino
            begin  // procesa normal
              J := BuscaProp(NombreParam);  // busca parametro en lista de propiedades, obtiene la posicion
              TLibreria.ParseProp(ListaProp[J], NombreProp, TipoProp);        // separa nombre de tipo
              propiedad :=  contexto.GetType(Obj.ClassType).GetProperty(NombreProp); // tipo de la propiedad
              valor := propiedad.GetValue(Obj).ToString;            // obtiene valor
              Params.ParamByName( NombreParam).Value :=  valor.AsString;
            end;
          end;
        end;

        contexto.Free;

        if ParamCount > 0 then
        begin
//          Prepared := True;
          ExecProc;
          MiError := Params.ParamByName('@Error').AsString;
        end;
      end;

    finally
      MiProcedimiento.Free;
    end;
  finally
    result := MiError;
    ListaProp.Free;
    ListaParam.Free;
  end;
end;


hay varias funciones que las estoy depurando, cuando las tenga lista se las paso, me parece super interesante estas funciones básicas para cualquier tabla.

les reitero mi agradecimiento por el apoyo brindado.

un fuerte abrazo.
__________________
Javier Villa Sánchez
jvilla@andreaproducciones.com
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
Usando RTTI para devolver lista de eventos elrayo76 Varios 13 03-06-2016 08:34:57
Recuperación información a través de RTTI jocaro Varios 15 10-05-2016 08:18:00
Error en funcion usando stdcall Ramsay Varios 2 11-03-2016 02:45:55
Herencia externa de componentes (BPL)(RTTI) jednavlop OOP 4 05-11-2010 18:37:14
Acceder al objeto asociado a una prop. (RTTI) Neftali [Germán.Estévez] Trucos 0 30-05-2007 13:39:22


La franja horaria es GMT +2. Ahora son las 13:42:57.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi