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 / Comments(0)