モデルの定義

データベースが準備できたところで、モデルを定義します。

この章では以下の内容について学習できます。

アプリケーション開始時の処理

それではいよいよコードを記述していきましょう。 salesdb直下にsalesdb.phpファイルを作成して記述していきます。

はじめにrequireuseとデータベースのURIを定義します。

<?php

require_once(__DIR__ . "/vendor/autoload.php");

use BizStation\Transactd\Transactd;
use BizStation\Transactd\Database;
use BizStation\Transactd\Tabledef;
use BizStation\Transactd\Benchmark;
use BizStation\Transactd\Nstable;
use Transactd\QueryExecuter;
use Transactd\IOException;
use Transactd\Model;
use Transactd\Collection;
use Transactd\JsonSerializable;

// DatabaseManagerのエイリアスをDBにする
class_alias('Transactd\DatabaseManager', 'DB');

// データベースアクセスURI マスターとスレーブは同じホスト
// Transactdのホスト認証であればユーザー名パスワードは不要
CONST masterUri = 'tdap://localhost/salesdb';
CONST slaveUri  = 'tdap://localhost/salesdb';

データベースへの接続

URIが決まれば接続はとても簡単です。

// データベースに接続する
DB::connect(masterUri, slaveUri);

基本的なモデルとリレーションの定義

まずは各テーブルのモデルとリレーション定義します。

Customerモデル

モデルはModelを継承します。

Customerは伝票を持つのでhasManyinvoicesリレーションを定義します。ただ、このリレーションは現実的ではありません。長年使用して伝票が大量になると、すべての伝票を一度に読み取る操作にはほとんど意味がありません。

そこで、取引期間を指定して伝票を取得するtransactionsという関数を定義します。

また、リレーションで読み取られるプロパティは、存在しないとエラーになります。 newしたオブジェクトからも関連が読み取れるよう事前に定義しましょう。

class Customer extends Model
{
    // Modelには$fillableまたは$guardedが必須なので$guardedを定義します。
    protected static $guarded = ['id'];

    // invoices()で使われるので事前定義
    public $id = 0;

    // このメソッドは使用しない。期間を指定したtransactionsを使う。
    public function invoices()
    {
        return $this->hasMany('Invoice');
    }

    public function transactions($startDate, $endDate)
    {
        return Invoice::index(1)->keyValue($this->id, $startDate)
            ->where('customer_id', $this->id)->where('date', '<=', $endDate)->get();
    }
}

Productモデル

Productは在庫を持つのでhasOnestockリレーションを定義します。

class Product extends Model
{
    protected static $guarded = ['id'];

    public function stock()
    {
        // stocks::index 0,  Product::code
        return $this->hasOne('Stock', 0, 'code');
    }
}

Stockモデル

Product::stockリレーションの逆リレーションを定義します。

class Stock extends Model
{
    protected static $guarded = [];

    public function product()
    {
        // Product::index 1,  Stock::code
        return $this->belongsTo('Product', 'code', 1);
    }
}

AmountTraitトレイト

invoicesdaily_summariesは共通のslaes_amounttax_amountpayment_amountフィールドを持つので、計算処理を集約するためにAmountTraitを定義します。

プロパティ名からはamountを省きます。$balanceは残高であり、daily_summariesでは使用しませんが、invoicesで必要なので加えておきます。

Trait AmountTrait
{
    public $sales = 0;
    public $tax = 0;
    public $payment = 0;
    public $balance = 0;

    public function reset()
    {
        $this->sales = 0;
        $this->tax = 0;
        $this->payment = 0;
    }

    public function sum($amount)
    {
        $this->sales += $amount->sales;
        $this->tax += $amount->tax;
        $this->payment += $amount->payment;
    }

    public function total()
    {
       return $this->sales + $this->tax - $this->payment;
    }
}

このトレイトには3つの簡単なメソッドresetsumtotalを追加しておきます。これらは後で使用します。

Invoiceモデル

InvoiceモデルではAmountTraitトレイトを直接useせず、インスタンスを作成して委譲してみましょう。まず、インスタンスを作成するためにトレイトからInvoiceAmountクラスを定義します。さらに、このクラスをシリアライズとデシリアライズに対応させるため、use JsonSerializableを加えます。

class InvoiceAmount
{
    use AmountTrait;
    use JsonSerializable;

    public function __construct()
    {
        $this->className = get_class($this); // シリアライズ用のクラス名をセット
    }
}

JsonSerializableを使う際は、コンストラクタでシリアライズ用のクラス名をセットします。

これで、シリアライズに対応したInvoiceAmountの原型が出来上がりました。

InvoiceのコンストラクタでこのInvoiceAmountのインスタンスを生成します。

class Invoice extends Model
{
    public $amount = null;

    public function __construct()
    {
        parent::__construct();
        $this->amount = new InvoiceAmount;
    }
}

次にORMのための委譲マップを定義しますが、その前に名前の違いを吸収するエイリアスを定義します。

    public static  $aliases  = ['sales_amount' => 'sales', 'tax_amount' => 'tax',
                                'payment_amount' => 'payment', 'balance_amount' => 'balance'];

$this->amountのプロパティに値が読み書きされるよう、$transferMapを定義します。

    public static  $transferMap = ['sales' => 'amount', 'tax' => 'amount',
                                    'payment' => 'amount', 'balance' => 'amount'];

Invoiceは明細を持つのでhasManyitemsリレーションを定義します。また、Customer::invoiceの逆リレーションなどを定義します。

    public static $guarded = ['id'];

    public function items()
    {
        return $this->hasMany('InvoiceItem');
    }

    public function customer()
    {
        return $this->belongsTo('Customer');
    }

ここまででInvoiceモデルは以下のようになります。

class Invoice extends Model
{
    public static  $aliases  = ['sales_amount' => 'sales', 'tax_amount' => 'tax', 'payment_amount' => 'payment', 'balance_amount' => 'balance'];
    public static  $transferMap = ['sales' => 'amount', 'tax' => 'amount', 'payment' => 'amount', 'balance' => 'amount'];
    public static $guarded = ['id'];

    public $id = 0;  // リレーションのために事前定義
    public $amount = null;

    public function __construct()
    {
        parent::__construct();
        $this->amount = new InvoiceAmount;
    }

    public function items()
    {
        return $this->hasMany('InvoiceItem');
    }

    public function customer()
    {
        return $this->belongsTo('Customer');
    }
}

InvoiceItemモデル

Transactd PHP ORMはモデル名からテーブル名への変換でsnake_caseをサポートしないため、テーブル名を指定します。

class InvoiceItem extends Model
{
    protected static $table = 'invoice_items';
}

InvoiceItemproduct_codeを持つので、そこからProductStockの関連を定義しましょう。 InvoiceItemにない、商品の詳細や在庫数などを簡単に取得できます。

Invoice::itemsの逆リレーションも定義します。

class InvoiceItem extends Model
{
    protected static $table = 'invoice_items';
    protected static $guarded = [];

    public function invoice()
    {
        return $this->belongsTo('Invoice');
    }

    public function stock()
    {
        // Stock index 0, InvoiceItem::product_code
        return $this->hasOne('Stock', 0, 'product_code');
    }

    public function product()
    {
        // Product index 1, InvoiceItem::product_code
        return $this->hasOne('Product', 1, 'product_code');
    }
}

DailySummaryモデル

DailySummaryモデルは、先ほどのAmountTraitトレイトをuseして使用します。

DailySummarydateinvoiceを取得できるように、hasManyinvoicesリレーションを定義します。また、テーブル名とエイリアスも定義します。

class DailySummary extends Model
{
    use AmountTrait;
    protected static $guarded = [];
    protected static $table = 'daily_summaries';
    static protected $aliases  = ['slaes_amount' => 'sales', 'tax_amount' => 'tax', 'payment_amount' => 'payment'];

    public function invoices()
    {
        // Invoice index 2, DailySummary::date
        return $this->hasMany('Invoice', 2, 'date');
    }
}

ソースコード

ここまでのソースコードはGitHub Gistからダウンロードできます。

  1. データベースとテーブルの準備
  2. マスターデータの作成