Introductions

Model class inherits QueryExecuter class (using delegation with the magic method). To understand the model, you need to understand QueryExecuter class too.

Index

Define models

One model class corresponds to one table in the database. One model instance represents one row of the corresponding table.

Model name and table name, model property name and table field name are the same or converted with fixed rule. Transactd PHP ORM converts them with the rule similar to ActiveRecord of Ruby on Rails.

To define a model, inherit Model class.

Define Customer model

use Transactd\Model;

class Customer extends Model
{
}

Model name and table name

Transactd PHP ORM uses naming conventions rules like ActiverRecord. For table names,

Note: ActiverRecord requires snake_case names, but Transactd PHP ORM does not support it. Transactd PHP ORM uses simplelowercase names by default. You can specify the table name which does not follow the rules by static $table.

use Transactd\Model;

class SalesSummary extends Model
{
    protected static $table = 'sales_summary';
}

Property name and field name

The property name of the model is same as the field name of the table. Specify alias for static $aliases if the name is not suitable. The original name will not be defined as the property.

class SalesSummary extends Model
{
    protected static $table = 'sales_summary';
    protected static $aliases = ['sales_amount' => 'salesAmount', 'tax_amount' => 'taxAmount'];
    // 'salesAmount' and 'taxAmount' will be defined.
    // 'sales_amount' or 'tax_amount' will not be.
}

Connection names

You can specify which connection to be used by each model with static $connection.

use Transactd\Model;

class SalesSummary extends Model
{
    protected static $table = 'sales_summary';
    protected static $connection = 'Internal';
}

Export models

Transactd PHP ORM includes the PHP program MdlGen.php which automatically generates models from the database. It generates @property comments according to PHPDoc. You can use the property completion in the IDE.

MdlGen.php has following features:

Usage

Execute from the command line as follows:

// On the directory where install by Composer
>php vendor/TransactdORMPHP-master/src/utility/MdlGen.php -slocalhost -dormtest -tcutomers

Results:

URI=tdap://localhost/ormtest

output = Customer.php
Generate done!

It generates one model at once. To generate multiple models, you are required to execute it for each table.

If it is executed without arguments, the description of the parameter will be displayed.

// On the directory where install by Composer
php vendor/TransactdORMPHP-master/src/utility/MdlGen.php

USAGE: php mdlgen.php -s[server] -d[database] -t[table] -u[userName] -p[password] -a[aliasList] -n[namespace] -o[output-directory]

  -s : Name or ipaddress of a Transactd server.
  -d : Database name.
  -t : Table name of generate target.
  -u : [optional] Username for server access.
  -p : [optional] Password for server access.
  -a : [optional] File name of alias list. (ini file format (key=value))
  -n : [optional] Namespace of the target Model.
  -o : [optional] Output directry name.(Include namespace)

Output directory and file names

The default output directory is where Composer has installed the libraries. If the namespace is specified, output directory is the directory with the same name as the namespace, under there. Namespace directories must be created in advance.

The file name is (MODEL NAME).php.

Conversion dictionary of field and table names

You can specify a conversion dictionary file of field and table names with -a option. The conversion dictionary is an ini format file in which source and destination are described with = as follows. You can convert Japanese names. Save the file with utf8.

Example of the conversion dictionary:

customer_name=customerName
product_name=productName
得意先=customer

Example of generated code

<?php

use Transactd\Model;

/**
 * Original table name:customers
 * 
 * @property integer $id
 * @property string  $name
 * @property string  $update_at
 */
class Customer extends Model
{
}

Retrieving models

Retrieving all models

$customers = Customer::all();

Retrieving first model

If you do not specify index number, primary key will be used.

Retrieving first model by primary key

$customer = Customer::first();

Retrieving first model by index number 1

$customer = Customer::index(1)->first();

Retrieving single model

Find a customer id = 3 by primary key

$customer = Customer::find(3);

Find a customer name = 'John' by name key

$nameKeyNum = 1;
$john = Customer::index($nameKeyNum)->find('John');

Find a customer by multiple segments (fields) key

$customer = Customer::index(2)->find([3, '1567-900-00']);

Retrieving multiple models

Find multiple records at once.

Find records id = 3, id = 4 and id = 6 by primary key

$customers = Customer::findMany([3, 4, 6]);

Find records by multiple segments (fields) key

$customers = Customer::index(2)->findMany([[3, '1567-900-00'],[9, '1467-700-01']]);

Query multiple models

Like QueryExecuter, you can search models with various conditions.

$customers = Customer::keyValue(0)->where('name', 'John')->noBreakReject()->get();

Just replace QueryExecuter instance to the class name. Please see Query conditions.

Advanced model definition

Object-relational impedance mismatch

If the data handling areas are different between database and model, Transactd PHP ORM can transfer the values on database to another object.

For example, customers table has customer's name and address. However, we want to manage the address with Address object in address property of Customer.

Define transferring with static $transferMap, 'field_name' => 'property_name_of_the_transfer_object'.

class Address
{
}

class Customer extends Model
{
    static protected $transferMap = ['zip' => 'address', 'city' => 'address', 'street' => 'address'];
    public $address = null;
    public function __construct()
    {
        parent::__construct();
        $this->address = new Address(); // address must be created before transferring!
    }
}

Then you can access to zip property like bellow:

$customer = find(1);
if ($customer->address->zip === '390-0825') {
    // (omitted)
}

Note: The object which receives the values must be created as a property value of the main object, before transferring. If the property is null, then the values will not be read or wrote.

Access modifiers

If you do not want to publish some members, make it to protected.

Make note property protected

class Customer extends Model
{
    protected $note;
}

When loading models, if the setter method is defined, Transactd PHP ORM sets the value via it.

class Customer extends Model
{
    protected $note;
    public __set($name, $value)
    {
        if ($name === 'note') {
            $this->note = trim($value); // The extension (php_tranasactd.[so|dll]) set value via this code.
        }
    }
}

When saving models, the getter method will not be used, even if it is defined. Transactd PHP ORM reads $note directly, even if it is a protected member.

class Customer extends Model
{
    protected $note;
    public __get($name)
    {
        if ($name === 'note') {
            return $this->note; // The extension (php_tranasactd.[so|dll]) does not use this code.
        }
        return parent::__get($name); // Call Model::__get (It is important for relationship opertions)
    }
}

Access to a property that does not exist

An exception will be thrown when a property that does not exist in the model is read. This makes it easy to detect typos.

If the field which was not selected by select is read, an exception will be thrown too.

$customer = Customer::keyValue(1)->select('id', 'name')->get();
echo $customer->phone; // Error !! \Exception will be thrown

However, the set of values is unconditionally possible.

$customer = new Customer();
// phone123 is not in field members.
$customer->phone123 = '0263-99-9999'; // OK!

CachedQueryExecuter and Objects cache

I wrote that the model inherits QueryExecuter class. In exactly, it inherits CachedQueryExecuter class. When Transactd PHP ORM reads objects, cached objects will be returned if it is in the cache. This cache reduces the number of accesses to the database.

The following methods add object(s) to the cache or update the cache:

The following methods read from cache first:

Clear the cache of the Model

Clear all caches of Customer.

Customer::clear();

Re-read a object and update the cache

$customer->refresh();

Using Custom collection

You can define custom collection.

Define custom collection

sample custom collection

class CCollection extends Transactd\Collection
{
    public function __construct($array, $rel = null, $parent = null)
    {
        parent::__construct($array, $rel, $parent);
    }
}

Using custom collection in ORM

Add newCollection method that generates collection to model.

class Customer extends Model
{
     public function newCollection($ar, $rel, $parent)
     {
         return new CCollection($ar, $rel, $parent);
     }
}

Mixed method chain of Model and QueryExecuter

The methods which are prefixed with scope in model can be called by QueryExecuter instance.

Define method

scope prefixed methods receive and return a QueryExecuter instance.

class Customer extends Model
{
     public function scopeInJapan($query) {
         return $query->where('country', 'japan');
     }
}

Using mixed method chain

To call the method, remove prefix scope from its name. QueryExecuter finds and calls the method which has scope + method name in the model.

$customer = Customer::keyValue(1)->where('id', '<', 11)->inJapan()->get();