[html-formfu] Accessing a form element which is not in the table

Octavian Râşniţă orasnita at gmail.com
Fri Dec 18 22:26:56 GMT 2009


From: "Carl Franks" <fireartist at gmail.com>
2009/12/17 Octavian Râşniţă <orasnita at gmail.com>:
> Hi,
>
> Is it possible to access (in a DBIx::Class inflator) an HTML::FormFu 
> element
> that doesn't have a corresponding field in the db table?
>
> I need to let the user specify an option in a separate form field
> (checkbox), and depending on that option to replace or not replace another
> field in the database, but the value of that checkbox should not be 
> entered
> in the database because there is no field for it there.

I suspect this logic should be handled by the form - not the database layer.
I'd suggest writing a HTML::FormFu::Transformer subclass to do it.
Carl


I found how to do that, by creating an accessor:

__PACKAGE__->mk_group_accessors(simple => 'file_replace_validator');

where "file_replace_validator" is a column that can be accessed in $row, but 
is not inserted/updated to DB.

So I made a module temporarily named DBIx::Class::InflateColumn::FileUpload 
that can be used with HTML::FormFu::Model::DBIC (with 2 issues).
This module allow file uploads when creating new records, file deletions and 
replacements.

It needs just a column "ID" and a column that holds the file name.

The HTML::FormFu could have one more field named file_replace_validator, 
which could be a checkbox.

If the form doesn't have that checkbox, the file is uploaded and inserted in 
the DB.
If the form has that checkbox and it is not marked, the file previously 
uploaded is not replaced, no matter if a new file is uploaded or not.
If the form has the checkbox and it is marked for confirming the 
replacement, the file from the server is replaced with the new file 
uploaded.
If the form has the checkbox and it is marked for confirming the 
replacement, but no new file is uploaded, the file previously uploaded is 
deleted (and the directory it is stored in also, if it doesn't contain other 
files - from the same recordset).

In order to use this module, the following settings must be done:

In the DBIC Result for the table that holds the files:

__PACKAGE__->mk_group_accessors(simple => 'file_replace_validator');

__PACKAGE__->load_components("InflateColumn::FileUpload");

__PACKAGE__->add_columns(
"id", {
data_type => "INT",
default_value => undef,
is_nullable => 0,
size => 10,
is_auto_increment => 1,
},
"file", {
data_type => "VARCHAR",
default_value => undef,
is_nullable => 1,
size => 255,
is_file_upload => 1,
file_upload_path => '/the/path/to/the/dir',
#or
file_upload_path => MyApp->path_to('the', 'path', 'to', 'dir'),
});

And in the Catalyst controller it should be just:

my $record = $c->model("DB::Records")->find($id);
my $form = $c->stash->{form};

if ($form->submitted_and_valid) {
  $form->model->update($record);
  $c->res->redirect($c->uri_for("/"));
}
else {
  $form->model->default_values($record);
}

But unfortunately if the File element is submitted empty, the "file" field 
gets the value undef, and if the value of a column is undef, 
DBIx::Class::InflateColumn will ignore it in the deflator because it accepts 
only non-scalar references there.
So the file is simply set to null in the DB and not deleted from the hard 
disk of the server.

Because of this, I needed to manually set the "file" field to {} but the 
code becomes much uglier.

And another issue is that the file handle $record->file->{handle} doesn't 
get out of scope after the line:
$form->model->default_values($record);

although it should do it, so I needed to add another line that undef it.

With these 2 fixes, the module works fine, and it is very helpful, but 
unfortunately these 2 fixes look ugly, like in:

my $record = $c->model("DB::Records")->find($id);
my $form = $c->stash->{form};

if ($form->submitted_and_valid) {
  unless ($form->param('file')) {
    $c->req->params->{file} = {};
    $form->process;
  }
  $form->model->update($record);
  $c->res->redirect($c->uri_for("/"));
}
else {
  $form->model->default_values($record);
  undef $record->file->{handle} if $record->file;
}

Even with these few lines of code added it could be very helpful, because it 
shouldn't be named explicitly all the row fields as when we create the row 
with ->create().

I have tried to use DBIx::Class::InflateColumn::FS and 
DBIx::Class::InflateColumnFile for doing file uploads, and even tried to 
modify DBIx::Class::InflateColumn::File, but none of them could do what I 
want (replacing/deleting the file previously uploaded based on a checkbox's 
state.

So I made DBIx::Class::InflateColumn::FileUpload which was inspired by 
DBIx::Class::InflateColumn::File.

Please help me to avoid needing to use those 2 fixes, or at least please 
tell me if what I want is impossible and due to some restrictions other 
modules can't be modified to make it work better, to know if I should 
continue or not working for it.

Thank you very much.

Octavian




More information about the HTML-FormFu mailing list