Merge 'trunk' into 'pod_fixes'
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / InflateColumn / File.pm
1 package DBIx::Class::InflateColumn::File;
2
3 use strict;
4 use warnings;
5 use base 'DBIx::Class';
6 use File::Path;
7 use File::Copy;
8 use Path::Class;
9
10 __PACKAGE__->load_components(qw/InflateColumn/);
11
12 sub register_column {
13     my ($self, $column, $info, @rest) = @_;
14     $self->next::method($column, $info, @rest);
15     return unless defined($info->{is_file_column});
16
17     $self->inflate_column($column => {
18         inflate => sub { 
19             my ($value, $obj) = @_;
20             $obj->_inflate_file_column($column, $value);
21         },
22         deflate => sub {
23             my ($value, $obj) = @_;
24             $obj->_save_file_column($column, $value);
25         },
26     });
27 }
28
29 sub _file_column_file {
30     my ($self, $column, $filename) = @_;
31
32     my $column_info = $self->column_info($column);
33
34     return unless $column_info->{is_file_column};
35
36     my $id = $self->id || $self->throw_exception(
37         'id required for filename generation'
38     );
39
40     $filename ||= $self->$column->{filename};
41     return Path::Class::file(
42         $column_info->{file_column_path}, $id, $filename,
43     );
44 }
45
46 sub delete {
47     my ( $self, @rest ) = @_;
48
49     for ( $self->columns ) {
50         if ( $self->column_info($_)->{is_file_column} ) {
51             rmtree( [$self->_file_column_file($_)->dir], 0, 0 );
52             last; # if we've deleted one, we've deleted them all
53         }
54     }
55
56     return $self->next::method(@rest);
57 }
58
59 sub insert {
60     my $self = shift;
61
62     # cache our file columns so we can write them to the fs
63     # -after- we have a PK
64     my %file_column;
65     for ( $self->columns ) {
66         if ( $self->column_info($_)->{is_file_column} ) {
67             $file_column{$_} = $self->$_;
68             $self->store_column($_ => $self->$_->{filename});
69         }
70     }
71
72     $self->next::method(@_);
73
74     # write the files to the fs
75     while ( my ($col, $file) = each %file_column ) {
76         $self->_save_file_column($col, $file);
77     }
78
79     return $self;
80 }
81
82
83 sub _inflate_file_column {
84     my ( $self, $column, $value ) = @_;
85
86     my $fs_file = $self->_file_column_file($column, $value);
87
88     return { handle => $fs_file->open('r'), filename => $value };
89 }
90
91 sub _save_file_column {
92     my ( $self, $column, $value ) = @_;
93
94     return unless ref $value;
95
96     my $fs_file = $self->_file_column_file($column, $value->{filename});
97     mkpath [$fs_file->dir];
98
99     # File::Copy doesn't like Path::Class (or any for that matter) objects,
100     # thus ->stringify (http://rt.perl.org/rt3/Public/Bug/Display.html?id=59650)
101     File::Copy::copy($value->{handle}, $fs_file->stringify);
102
103     $self->_file_column_callback($value, $self, $column);
104
105     return $value->{filename};
106 }
107
108 =head1 NAME
109
110 DBIx::Class::InflateColumn::File -  DEPRECATED (superseded by DBIx::Class::InflateColumn::FS)
111
112 =head2 Deprecation Notice
113
114  This component has a number of architectural deficiencies and is not
115  recommended for further use. It will be retained for backwards
116  compatibility, but no new functionality patches will be accepted.
117  Please consider using the much more mature and actively supported
118  DBIx::Class::InflateColumn::FS
119
120 =head1 SYNOPSIS
121
122 In your L<DBIx::Class> table class:
123
124     use base 'DBIx::Class::Core';
125
126     __PACKAGE__->load_components(qw/InflateColumn::File/);
127
128     # define your columns
129     __PACKAGE__->add_columns(
130         "id",
131         {
132             data_type         => "integer",
133             is_auto_increment => 1,
134             is_nullable       => 0,
135             size              => 4,
136         },
137         "filename",
138         {
139             data_type           => "varchar",
140             is_file_column      => 1,
141             file_column_path    =>'/tmp/uploaded_files',
142             # or for a Catalyst application 
143             # file_column_path  => MyApp->path_to('root','static','files'),
144             default_value       => undef,
145             is_nullable         => 1,
146             size                => 255,
147         },
148     );
149
150
151 In your L<Catalyst::Controller> class:
152
153 FileColumn requires a hash that contains L<IO::File> as handle and the file's
154 name as name.
155
156     my $entry = $c->model('MyAppDB::Articles')->create({ 
157         subject => 'blah',
158         filename => { 
159             handle => $c->req->upload('myupload')->fh, 
160             filename => $c->req->upload('myupload')->basename 
161         },
162         body => '....'
163     });
164     $c->stash->{entry}=$entry;
165
166
167 And Place the following in your TT template
168
169     Article Subject: [% entry.subject %]
170     Uploaded File: 
171     <a href="/static/files/[% entry.id %]/[% entry.filename.filename %]">File</a>
172     Body: [% entry.body %]
173
174 The file will be stored on the filesystem for later retrieval.  Calling delete
175 on your resultset will delete the file from the filesystem.  Retrevial of the
176 record automatically inflates the column back to the set hash with the
177 IO::File handle and filename.
178
179 =head1 DESCRIPTION
180
181 InflateColumn::File
182
183 =head1 METHODS
184
185 =head2 _file_column_callback ($file,$ret,$target)
186
187 Method made to be overridden for callback purposes.
188
189 =cut
190
191 sub _file_column_callback {}
192
193 =head1 AUTHOR
194
195 Victor Igumnov
196
197 =head1 LICENSE
198
199 This library is free software, you can redistribute it and/or modify
200 it under the same terms as Perl itself.
201
202 =cut
203
204 1;