Use the ORM

FMVC provides a mapper for PHP classes to database tables.

I mean, my data looks good but is there a way it can be a model?

FMVC provides models, you may know this from other packages such as ActiveModel or django.db.models. Models are a way to move your application logic from associative arrays, returned from the database, (or even worse numeric arrays) to full-blown PHP classes that even can have custom methods or customized constructor methods(we know, wooow so original, it's not like everyone else can do that... but where's your PHP framework Karen?).

Yes, but how can I do that?

So at first, we have like a base class. Core\Data\DataObject.

This Base class is hold the basic methods to read, write, update and delete your models.

Then we create out desired model like this:

/**
* Class User
* @package Models
* @table User
* @connection local
*/
class User extends DataObject
{
/**
* @var integer PRIMARY KEY AUTOINCREMENT
*/
public $id;
/**
* @var VARCHAR(50)
*/
public $Username;
/**
* @var VARCHAR(50)
*/
public $Firstname;
/**
* @var VARCHAR(50)
*/
public $Lastname;
function __construct()
{
parent::__construct();
$this->CreatedAt = date("Y-m-d H:i:s");
}
}
}

All fields we want to be represented as column in our dataset are being declared as public variable. Please be careful, as the names of the field will be used as the field names as-is. This means, that casing will also be taken into account. To tell the database adapter, what the name of the table is, we annotate the class with PHPDoc-property @table.

If we want to create the table for our class automatically, we need to also add PHPDoc to our fields. use the @var property to tell the database how the field should be realized in the database.

Primary Keys

As especially the primary key is being created differently in each SQL dialect, FMVC has declared the SQLite 3 dialect as the default dialect for annotating a database field as primary key. Each available SQL dialect implementation will take this annotation and replace it with its own version of it.

Ashort word on sharding

As relational databases can grow quite large over the course of time, queries executed against them can also grow in size. That's why so-called database sharding is a common approach to split the database disks into multiple shards and distribute them on multiple database servers.

Especially when working with different types of data, a single database type often doesn't cover all the requirements. That's why an application can have multiple databases at the same time.

To tackle both the large disk and multiple databases at the same time, FMVC introduced the @connection annotation. When annotation a class with this PHPDoc comment, the ORM will open a connection to the connection specified and will execute all manipulations of this model against it.

An example:

At first we have our database connection. In this case it will be an SQLite 3 database.

//db.json
{
"users": {
"protocol": "sqlite",
"host": "file.db",
"username": "",
"password": "",
"port": "",
"database": ""
}
}

Now we add the @connection annotation to our model:

/**
* Class User
* @package Models
* @table User
* @connection local
*/
class User extends DataObject
{
...
}
}

Object Relations

Sometimes you want to go all out crazy and create connnections between the stuff you're declaring in your database. That's when we talk about Object relations. Take for example a blogging page, where multiple authors/users are able to create blog posts. So when you create the User Object for your data structure, you might as well want to connect it to the articles it wrote. Like so:

/**
* Class User
* @package Models
* @table User
* @connection local
*/
class User extends DataObject
{
/**
* @var integer PRIMARY KEY AUTOINCREMENT
*/
public $id;
}
/**
* Class Article
* @package Models
* @table Articles
* @connection local
*/
class Article extends DataObject
{
/**
* @var integer PRIMARY KEY AUTOINCREMENT
*/
public $id;
/**
* @var integer
*/
public $owner_id;
public function __construct()
{
parent::__construct();
$this->belongsTo(User::class, 'owner', $this->owner_id);
}
}

The model API

Now we have our entity. But behold! This entity is also equipped with some ass-kicking methods:

# instance methods
# stores current object in database
$user->store();
# deletes current object from database
$user->delete();
# updates current object in database
$user->update();
# and class methods
# searches for entity via Id
User::findById(int $id);
# @param $conditions: 'field' => 'value' will be evaluated to 'field' = 'value'
User::find(array $conditions);