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 03-05-2020
Avatar de gatosoft
[gatosoft] gatosoft is offline
Miembro Premium
 
Registrado: may 2003
Ubicación: Bogotá, Colombia
Posts: 833
Poder: 22
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
  #2  
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.286
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
  #3  
Antiguo 27-05-2020
javicho_villa javicho_villa is offline
Miembro
 
Registrado: feb 2005
Ubicación: Lima - Perú
Posts: 101
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
  #4  
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.286
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
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 12:58:46.


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