DBIx::Classで論理削除

DBICで論理削除をしたくなったので調べていたのだが、うまく書く方法がイマイチなかった。

まず、削除フラグを常にチェックするようにするのは簡単で、テーブルクラスに

__PACKAGE__->resultset_attributes({ where => { deleted => undef }});

とか書いてくだけでつねにWHERE句に deleted IS NOT NULL が入るようになる。これはマニュアルに書いてある通り。

問題は削除するときで、テーブルクラスで delete 定義してそこで update({ deleted => 1 }) とかやればいいかなと思いきや、そうすると cascade delete 効かなくなってしまっていやだ。

DBICのrowに対するdeleteチェーンは大まかに

  1. ユーザー定義テーブルクラスでのdelete (定義されてる場合)
  2. DBIx::Class::Relationship::CascadeActions
  3. DBIx::Class::Row

となっていて、2 で cascade delete の処理が入り。3で実際に row が削除される。

(正確には2ではnextよんでからcascade deleteしてるため 1 -> 2 -> 3 -> 2 という感じである)

それでこの場合は実際に削除するのを update({ deleted => 1 }) に置き換えたいので 3 の直前に自分のメソッドを差し込んでそこでチェーンをとめるという実装がしたいと思った。

のだけどしばらく考えたけどいいやり方が見つからなかったので結局 1 のユーザー定義クラスで

sub delete {
    my $self = shift;

    $self->update({ deleted => 1 });

    my $source = $self->result_source;
    $self->search_related($_)->delete_all
        for grep { $source->relationship_info($_)->{attrs}{cascade_delete} } $source->relationships;

    $self;
}

などと全部詰め込む感じでお茶を濁した。

論理削除を今まで使ってなかったのでいまさら感がありますが、DBICで論理削除ってるひとでいいやり方知ってる人いたら教えてください!

by typester / at 2008-01-23T17:31:00 / perl · dbic / Comment

DBIx::Class::AsArrayHash - Hatena::Diary::Neko::kak 500 Internal Server Error

うんうん、DBICつかってるとmapまくりよね。僕ならこう書いてるな。

my @rets = map { $_->get_columns }, $rs->all;

DBIC::AsArrayHashいらないってのは同意。

retrieveとかCDBI的なのもあれ。

by typester / at 2008-01-08T22:40:00 / perl · dbic / Comment

舌足らずすぎた。

Model::DBIC:
  connect_info:
    - dbi:mysql:table
    - root
    - on_connect_do:
        - SET NAMES utf8
      cursor_class: DBIx::Class::Cursor::Cached
  cache_file: __path_to(tmp/query_cache)__

さっきはこんなconfigで使った場合のコードです。

by typester / at 2007-12-20T15:13:00 / catalyst · dbic / Comment

DBIx::Class::Cursor::Cachedつかってみた

すばらしすぎる。もっと早く使えばよかったとおもった。

Catalyst::Model::DBIC::Schema で使う場合はこんな感じでOK。

sub new {
    my $self = shift->NEXT::new(@_);

    my $cache = Cache::FastMmap->new( share_file => $self->{cache_file} );

    $self->schema->default_resultset_attributes({
        cache_object => $cache,
    });

    $self;
}

そんで、あとはsearchのattrとして { cache_for => 300 } とかかいてやればそのクエリは300秒キャッシュされる。

実際にはsearch時ではなくてallとかnextのときにそのクエリをキャッシュしてる。

ちなみにdefault_resultset_attributescache_forを含めるとすべてのクエリがキャッシュされる。

by typester / at 2007-12-20T13:33:00 / catalyst · dbic / Comment

書き忘れたけどさっきの update_schema.pl

./script/myapp_update_schema.pl dbi:mysql:tablename username password

見たいな感じで DSN を渡す必要がある。めんどう。

ここら辺があれでまだヘルパーにはなってない。

by typester / at 2007-05-07T05:16:00 / perl · dbic / Comment

Schema::Loader 使い方

mizzy.org : Re: DBICとDBIx::Class::Schema::Loader 僕のいろいろな勘違い

僕の中で流行ってる使い方があるのでかぶせて書いておいてみる。mizzy さんの二個目の例を自分ルール化させた感じ?

作業は一般的なCatalystアプリのディレクトリ構造上であるとして、そこに新しく schema というディレクトリを作成。

そんで、

  • schema/lib/Schema/{TableName}.pm

に各テーブルのリレーション定義とかメソッドとかを自分で書く。実際にこのライブラリは Catalyst にロードされない。

上記ファイルを元に Schema::Loadermake_schema_at を使い

  • lib/MyApp/Schema.pm
  • lib/MyApp/Schema/*.pm

にコードを自動生成して、そちらをロードするという感じ。こちらのコードは自分では書き換えない。

コード自動生成は script/myapp_update_schema.pl にこんなのを書いてそれを実行している。

#!/usr/bin/env perl

use strict;
use warnings;

use FindBin;
use File::Spec;
use lib File::Spec->catfile( $FindBin::Bin, qw/.. schema lib/ );

use DBIx::Class::Schema::Loader qw/make_schema_at/;

die unless @ARGV;

make_schema_at(
    'MyApp::Schema',
    {   components     => ['ResultSetManager', 'UTF8Columns'],
        dump_directory => File::Spec->catfile( $FindBin::Bin, '..', 'lib' ),
        dump_overwrite => 1,
        debug => 1,
    },
    \@ARGV,
);

@INCschema/lib をくっつけてから make_schema_at してるだけ。ワンライナーでもできるけど見通しが悪いので。

これで普通の make_schema_at でつくられる Schema ファイルに自分で schema/lib/Schema/ 以下に書いた定義がくっついて出力されるという寸法。

Schema::Loader でダイナミックロードを使っていると各テーブルクラスにはリレーション定義やメソッド拡張のコードだけをかけばいいのでシンプルになって好きなんだけど、そうすると起動時のオーバーヘッドがあるし、ResultSetManagerがつかえなくていやんというときに、これだとまぁイイとこどりのような感じにできる。

実際にいじるファイルは schema/lib 以下のファイルで、それらのファイルを更新するたびに update.pl を実行するというのがあれだけどまぁ自動化できる。してないけど。

難点は、schema/lib 以下のファイルでsyntax errorがあっても何も言われないこと。

何も言われないというか、syntax error があるファイルは

# Loaded external class definition for 'MyApp::Schema::TableName'

ってのが出ないだけという。どこがエラってるのかとかがわからないので、がーーって書いてどこか typo してたりするとはまるかも。

by typester / at 2007-05-07T04:57:00 / perl · dbic / Comment

inflate_column + time_zone

んー

for my $date_column (qw/created_date modified_date/) {
    __PACKAGE__->inflate_column(
        $date_column => {
            inflate => sub { DateTime::Format::MySQL->parse_datetime(shift)->set_time_zone('UTC') },
            deflate => sub { DateTime::Format::MySQL->format_datetime(shift->set_time_zone('UTC')) },
        }
    );

    {
        no strict 'refs';
        *{"$date_column\_for"} = sub {
            my ($self, $user) = @_;

            ($user && $user->info && $user->info->time_zone)
                ? $self->$date_column->set_time_zone( $user->info->time_zone )
                : $self->$date_column;
        }
    }
}
by typester / at 2007-05-01T08:12:00 / perl · dbic / Comment

あけましておめでとうございます!

今年初良いことは、DBICのinflate_result上書きによるサブクラス化を知ったことです!

うおおやっべぇ便利すぎる。

詳しくは

DBIx::Class::Manual::Cookbook - Miscellaneous recipes - search.cpan.org

参照のこと。

知らなかったなぁ。いつから使えたのか。

by typester / at 2007-01-01T03:54:00 / perl · dbic / Comment

make_schema_at が external クラスも読むようになっている

既存のDBに接続してテーブル情報などを取得して、DBIx::Class::Schema のテーブルクラスを作成してくれる Schema::Loader の make_schema_at が自分で定義した外部クラスファイルも見てくれるようになっていた。

perl -Ilib -MDBIx::Class::Schema::Loader=make_schema_at,dump_to_dir:tmp -e 'make_schema_at("MyApp::Schema", { components => [qw/ResultSetManager UTF8Columns/]}, ["dbi:mysql:myapp","root"])'

などとすると、./tmp にスキーマクラスをダンプしてくれるのだけど、-Ilib している中にすでに MyApp::Schema::User とかがかいてあると、それも作成するクラスにくっつけてくれる。

便利だ。

by typester / at 2006-11-24T13:35:00 / perl · dbic / Comment

テーブル定義はSchema::Loaderにまかせてメソッドやリレーション定義だけをテーブルクラスに書いていくのが好き。

でもさっきResultSetManagerとSchema::Loaderって併用できないことが判明。

不貞昼寝。

by typester / at 2006-11-24T12:04:00 / life · dbic / Comment

« Prev 1 2 3 4

(Page 1 of 4)