ORM_Connection + ORM_Table
Source: source/classes/fworm.prg
The ORM (Object-Relational Mapping) classes provide a multi-backend abstraction layer that works with ADO, FWH (MariaDB), and Dolphin (DLP) connections. ORM_Connection manages the database link and exposes tables as ORM_Table objects. ORM_Table provides a fluent query-building interface with chainable Where/OrderBy/Select methods, aggregation functions, relationship navigation, and dynamic field access via an error handler.
Architecture
ORM_Connection
ORM_Connection abstracts database connections across three backends: ADO (for any ODBC-compliant RDBMS), FWH (native MariaDB via FWMariaConnection), and DLP (Dolphin library for MariaDB). It accepts either connection parameters or an existing connection object.
DATA Members
| DATA | Type | Description |
|---|---|---|
rdbms | Character | RDBMS type ("MYSQL", "MSSQL", "DBASE", "ORACLE", etc.) |
oCn | Object | Underlying connection object (ADO/FWH/DLP) |
nLib | Numeric | Library constant: LIB_ADO(1), LIB_FWH(2), LIB_DLP(4) |
cServer | Character | Server hostname |
cDataBase | Character | Database name |
cUserName | Character | Database user name |
Methods
| Method | Description |
|---|---|
New( rdbms, cServer, cDataBase, cUserName, cPassword ) | Create and connect to a database. If first parameter is an object, wraps an existing connection. |
Connect( cPassWord ) | Establish the connection |
Tables() | Return an FW_Array of table names in the database |
Table( cTable ) | Return an ORM_Table object for the given table name |
HasTable( cTable ) | Check if a table exists |
Close() | Close the connection |
ORM_Table
ORM_Table provides recordset navigation, query building, and data manipulation for a single database table. Its error handler exposes field values as dynamic properties and sub-tables as related ORM_Table objects.
Key DATA Members
| DATA | Type | Description |
|---|---|---|
cTableName | Character | Table name |
cPrimaryKey | Character | Primary key field name(s) |
cSelect | Character | SELECT clause (field list or SQL expression) |
cWhere | Character | WHERE clause (accumulated via Where() calls) |
cOrderBy | Character | ORDER BY clause (accumulated via OrderBy() calls) |
lAutoSave | Logical | If .T., saves changes automatically (default .F.) |
aRelate | Array | Relationship definition: { cField, oTable, cFKey } |
Methods
| Method | Description |
|---|---|
New( oConnection, cTable ) | Create an ORM_Table bound to a connection |
Where( ... ) | Add filter condition(s). Chainable, returns Self. |
OrderBy( ... ) | Add sort specification(s). Chainable, returns Self. |
Select( ... ) | Add field(s) to the SELECT clause. Chainable. |
First() | Navigate to the first record. Returns Self. |
Last() | Navigate to the last record. Returns Self. |
Next() | Navigate to the next record. Returns Self. |
Prev() | Navigate to the previous record. Returns Self. |
Move( n ) | Navigate forward/backward by n records. Returns Self. |
Add( aFlds, aVals ) | Add a new record with the given field values |
Del() | Delete the current record |
Find( uVal, u ) | Find a record by primary key value, or (cField, uVal) pair |
Count() | Return the number of matching records |
Sum( cField ) | Return the SUM of a numeric field |
Max( cField ) | Return the MAX value of a field |
Min( cField ) | Return the MIN value of a field |
Avg( cField ) | Return the AVG of a numeric field |
Std( cField ) | Return the STD (standard deviation) of a field |
Browse( acFields ) | Display the recordset in an XBrowse dialog |
Relate( cField, oTable, cFKey ) | Define a relationship to another ORM_Table |
Save() | Save pending changes. Returns Self. |
Get( u ) | Get field value by name or position |
Close() | Close the recordset and free resources |
Dynamic Field Access
ORM_Table uses an error handler to provide direct property-style access to field values. Any message that matches a table field name is automatically translated to a field get/set:
// Instead of: oTable:Get( "name" )
? oTable:name // dynamic access via error handler
oTable:city := "Madrid" // set field value
Similarly, accessing a property that matches another table name returns a new ORM_Table for that table, enabling seamless navigation between related tables:
// If "orders" is a table, this returns ORM_Table for orders:
oTable:orders:Where( "status = 'active'" )
Example: Chain Queries with Relationships
#include "FiveWin.ch"
function Main()
local oCn, oCustomers
// Create connection to MariaDB via FWH backend
oCn := ORM_Connection():New( "FWH", "localhost", "mydb", "root", "password" )
if oCn == nil
MsgStop( "Connection failed" )
return nil
endif
// Get a table and chain query filters
oCustomers := oCn:Table( "customers" )
oCustomers:Where( "city", "Madrid" ):OrderBy( "name" )
oCustomers:First()
while !oCustomers:oRs:Eof()
? oCustomers:name, oCustomers:email // dynamic field access
oCustomers:Next()
end
// Aggregation
? "Total customers in Madrid:", oCustomers:Count()
? "Max credit:", oCustomers:Max( "credit_limit" )
// Find by primary key
oCustomers:Find( 101 )
? oCustomers:name
// Add a new record
oCustomers:Add( { "name", "email" }, { "New Client", "client@test.com" } )
oCn:Close()
return nil
Notes
- ORM_Table methods Where(), OrderBy(), and Select() are chainable -- each returns the Self object, allowing fluent syntax like
oTable:Where(...):OrderBy(...):First(). - The Where() method accepts multiple calling conventions:
Where( "field = value" ),Where( "field", value ),Where( "field", "LIKE", "%pattern%" ), orWhere( "field", ">=", 100 ). - The
Relate()method links two ORM_Table instances so that navigating the parent automatically filters the child table's recordset based on the foreign key value. - The dynamic error handler makes field access feel like native property access:
oTable:fieldname(read) andoTable:fieldname := value(write). - Aggregation methods (Count, Sum, Max, Min, Avg, Std) respect the current Where() conditions.