第9章 新しい機能を追加する(その2)

前回は設定ファイルを用意して実行環境ごとに実行条件を切り替える方法を紹介しました。

今回はオブジェクトリレーショナルマッピング(ORM)を拡張する方法やってみたいと思います。

ORMとはなんであるのかの詳細な説明はここではしませんが、 具体的にwebアプリケーションのフレームワークのなかからどのようにして ORMを拡張するのかを事例を紹介します。

9.2 データベースロジック(ORM 拡張ロジック)

今回は teng モジュールでロジックを拡張するやり方を紹介します。

9.2.1 ファイル構成

例として今回は下記のようなファイル構成としたい

beginning_mojo            # Application directory
...
|- lib                    # 読み込みファイル各種
|  |- BeginningMojo       # アプリケーションファイル各種
|  |  +- Controller       # アプリケーションコントローラー
|  |  +- DB               # データベースオブジェクトロジック各種
|  |  |  +- Teng                # Teng 拡張ロジック
|  |  |  |  +- Row              # Row オブジェクト拡張ロジック
|  |  |  |     +- Bulletin.pm   # 各ロジック
|  |  |  +- Base.pm             # Teng 共通ロジック
|  |  +- Model            # コントローラーロジック
|  |  +- DB.pm            # データベースオブジェクトロジック呼び出し
|  |  +- Model.pm         # コントローラーロジック呼び出し
... 

9.2.2 追加のファイルを作成

作業を開始するまえにテストコードが確実に実行できるか確認しておきましょう

(テストコード実行、アプリケーションが実行されている状態で別のウインドウから)
% docker-compose exec --env MOJO_MODE=testing web carton exec -- prove --lib 

下記のように新しくファイルを追加してゆきます。

各ファイルの内容の詳細については github のサンプルコードを参考にしてください。

% mkdir -p lib/BeginningMojo/DB/Teng/Row
% touch lib/BeginningMojo/DB.pm
% touch lib/BeginningMojo/DB/Base.pm
% touch lib/BeginningMojo/DB/Teng/Row/Bulletin.pm 

データベースオブジェクトの呼び出し用

lib/BeginningMojo/DB.pm

package BeginningMojo::DB;
use Mojo::Base 'BeginningMojo::DB::Base';

1;

lib/BeginningMojo/DB/Base.pm

データベースオブジェクトの共通ロジック

package BeginningMojo::DB::Base;
use Mojo::Base -base;
use Teng;
use Teng::Schema::Loader;
use Time::Piece;

has [qw{conf}];

sub teng {
    my $self    = shift;
    my $dsn_str = 'dbi:SQLite:' . $self->conf->{db_file};

    # ...
    return $teng;
}

# 共通のDB用メソッドを定義する
# ... 

lib/BeginningMojo/DB/Teng/Row/Bulletin.pm

teng Row 各オブジェクトを定期、今回は削除ロジック

package BeginningMojo::DB::Teng::Row::Bulletin;
use Mojo::Base 'Teng::Row';
use Time::Piece;

sub soft_delete {
    my $self = shift;
    my $opt  = shift || +{};
    # ...
    return;
}
# ... 

9.2.3 既存のファイルを修正

データベース用のロジックは移動された

lib/BeginningMojo/Model/Base.pm

package BeginningMojo::Model::Base;
use Mojo::Base -base;
use BeginningMojo::DB;

has [qw{req_params conf}];

has db => sub {
    BeginningMojo::DB->new( +{ conf => shift->conf } );
};

1; 

teng の呼び出し方法を変更

lib/BeginningMojo/Model/Bulletin.pm

# ...
sub to_template_list {
    # ...
    my $teng          = $self->db->teng;
    # ...
}

sub store {
    # ...
    my $teng = $self->db->teng;
    # ...
}

sub remove {
    # ...
    my $teng   = $self->db->teng;
    # ...
}
# ...

テストコードを修正

t/remove.t

# ...
subtest 'top -> list -> create -> store -> list -> remove -> list' => sub {
    # ...
    my $row     = $t->app->model->db->teng->single( 'bulletin', +{ deleted => 0 } );
    # ...
};
# ... 

ヘルパーメソッドを変更

lib/BeginningMojo.pm

# ...
sub startup {

    # ...
    $self->helper(
        model => sub { BeginningMojo::Model->new( conf => $config ); } );
    # ...
}
# ... 

新規作成と削除のロジックを置き換える

lib/BeginningMojo/Model/Bulletin.pm

# ...
sub store {
    # ...
    my $db        = $self->db;
    my $table     = 'bulletin';
    my $insert_id = $db->teng_fast_insert( $table, $params );
    return;
}

sub remove {
    # ...

    my $params = $self->req_params;
    my $teng   = $self->db->teng;
    my $row    = $teng->single( 'bulletin', $params );
    return if !$row;

    $row->soft_delete();
    # ...
}
# ... 

最後にテストコードを実行して成功することを確認しておきましょう。

(テストコード実行、アプリケーションが実行されている状態で別のウインドウから)
% docker-compose exec --env MOJO_MODE=testing web carton exec -- prove --lib

9.2.4 まとめ

9.2.5 次回

今回はデーターベースロジックまわりをととのえました。

次回はその他のwebアプリを開発するときに必要なよくありがちな機能をみていきたいと思います。