What is Active Record?
My first experience with active record patterning was with Ruby on Rails, it was amazing to see all the SQL C(reate)R(ead)U(pdate)D(estroy) work disappear. You can read about it more
here.
Feel free to ask any questions, comments, etc.
Advantages
- Automatically converts SQL data into their respective Object form
- Handles nearly all SQL business for you
- Dynamic Query Builder
- Easily Implemented
Disadvantages
- A little bit slower than directly implementing SQL due to Object generation, and dynamic query building. I haven't done that much testing but the biggest performance increase comes from disabling SQLite's file sync, after that I was able to create 10000 Objects in one run in 2~ seconds.
- No relationship (I.e: belongs to, has many) support yet, but if there's interest I will go that route.
Example Setup
DB-NPC: Object
PHP Code:
function onCreated() {
// DB Migrations
this.migrations = {
{"new", "acct:string", "number:integer"}, // Creates Table
{"seed"}, // Calls seed()
{"add_index", "acct", "number"}, // Adds Index to Table
{"echo", "Migrations Completed!"} // Optional Message
};
// Configuration
// To Reset DB Set:
//this.resetdb = true;
// Table Prefix
//this.table_prefix = "test_";
// Set Exact Table
//this.table = "some_table_name";
// Model Object's Specific Class
// Comment out if not-required
this.model_class = "activerecord_object_" @ this.name.lower();
// Model Functionality
this.join("activerecord_model");
}
function seed() {
// Creates some example data
for (a: allplayers) {
this.create({"acct=" @ a.account, "number=" @ int(random(10000,90000))});
}
// Alert RC
echo("Seeded " @ this.table @ " with " @ allplayers.size() @ " entries!");
// Successful
return true;
}
public function validations(obj) {
// Model specific validations
return true;
}
public function relationships() {
// Planned in future update if there's interest.
// I.e: hasmany(objects, through_column)
}
class: activerecord_object_object
PHP Code:
// Returns the pretty string version of the object
public function pretty() {
return 0+this.number SPC this.acct;
}
Example
Just a small example showcasing the CRUD functionality.
PHP Code:
function onCreated() {
test_activerecord();
}
function test_activerecord() {
// Reset Query Builder
Object.reset();
// Create
for (temp.i = 0; temp.i < 10; temp.i++) {
temp.obj = Object.create({"acct=fowlplay4", "number=" @ 0+temp.i});
assert_not_null(temp.obj, "Object Creation");
}
// Read
temp.obj = Object.first();
echo(temp.obj.pretty());
assert(temp.obj.pretty(), "Objects Pretty Method");
// Update
temp.obj.acct = "Shenanigans";
if (temp.obj.save()) {
echo("Object saved successfully!");
assert_success();
} else {
assert_failure("Object saving");
}
// Dump (Alternate Reading/Debug Function)
temp.obj.dump();
// Save ID for Verification Later
temp.obj_id = temp.obj.id;
// Destroy
temp.obj.destruct();
// Verify Destroy
temp.obj = Object.find(temp.obj_id);
if (temp.obj == NULL) {
echo("Object was destroyed");
}
assert_null(temp.obj, "Object Destroyed");
}
Query Builder Example
In activerecord_model
PHP Code:
/*
Query Building Functions
*/
// Current it just accepts an SQL SELECT clause
// I.e: select("id, name") or select("*")
public function select(args) {
this.clause_select = args;
return this;
}
// Currently it just accepts an SQL WHERE clause
// I.e: where("id = 3")
public function where(args) {
this.clause_where = args;
return this;
}
// Currently it just accepts an SQL ORDER BY clause
// I.e: order("name ASC")
public function order(args) {
this.clause_order = args;
return this;
}
// Sets the query to delete mode
// Currently it just accepts an SQL WHERE clause
// I.e: del("id = 3") - Deletes row with id 3
// del() - WILL DELETE EVERY ROW IN TABLE
// UNLESS where clause specified
public function del(args) {
this.clause_delete = true;
if (args) {
this.clause_where = args;
}
return this;
}
// Limits the results retrieved
// It accepts an SQL LIMIT clause OR {startrow, endrow}
// I.e: limit("10") - Rows 0 - 10
// limit("10 OFFSET 100") - Rows 100 to 110
// limit({10, 20}) - Rows 10 to 20
public function limit(args) {
if (args.size() == 2) {
args = int(args[1] - args[0]) SPC "OFFSET" SPC int(args[0]);
}
this.clause_limit = args;
return this;
}
/*
Query Functions
*/
// Returns first result by setting limit to 1
public function first() {
this.clause_limit = "1";
return this.query();
}
// Returns all results by clearing any limit
public function all() {
this.clause_limit = "";
temp.result = this.query();
return (temp.result.size() > 0 ? temp.result.link() : {temp.result});
}
// Resets query building values
// If you're unsure of your current queries clauses
// reset it beforehand.
public function reset() {
this.clause_select = "";
this.clause_limit = "";
this.clause_where = "";
this.clause_order = "";
this.clause_delete = "";
return this;
}
// Alias for reset
public function build() {
return this.reset();
}
/*
Query Aliases
*/
public function get() {
return this.query();
}
public function go() {
return this.query();
}
public function now() {
return this.query();
}
Small Example Usage
PHP Code:
function onCreated() {
// Find all objects where acct is fowlplay4
temp.objs = Object.where(format("acct = '%s'", player.account.escape())).all();
// Find the first 10 objects
temp.objs = Object.limit({0, 10}).get();
// Delete all objects
Object.del().now();
}