html-formhandler-model-dbic icon indicating copy to clipboard operation
html-formhandler-model-dbic copied to clipboard

Unique constraints do not work properly across a belongs_to relationship

Open dracos opened this issue 5 years ago • 0 comments

If you have a DB model, something like:

__PACKAGE__->add_columns(
  "id", { data_type => "integer", is_auto_increment => 1, is_nullable => 0, sequence => "roles_id_seq" },
  "body_id", { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
  "name", { data_type => "text", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->add_unique_constraint("roles_body_id_name_key", ["body_id", "name"]);
__PACKAGE__->belongs_to(
  "body", "App::DB::Result::Body", { id => "body_id" },
  { is_deferrable => 0, on_delete => "CASCADE,", on_update => "NO ACTION" },
);

And a matching form (which needs body to work, body_id does not):

has_field 'name' => ( required => 1 );
has_field 'body' => ( type => 'Select', empty_select => 'Select a body', required => 1 );

And your database has three rows:

  1. Body A, name "123"
  2. Body A, name "456"
  3. Body B, name "789"

Then the following happens:

  • Editing row 1 to Body A, name "456" – works fine, you get the "Duplicate value for [_1] unique constraint" error message.
  • Editing row 1 to Body B, name "789" – validation incorrectly passes, it tries to update and dies with a database error.
  • Creating a new entry, Body A, name "123" – validation incorrectly passes, it tries to insert and dies with a database error.

This is because the form column is body but the database field is body_id. Here is the bit of code responsible:

https://github.com/gshank/html-formhandler-model-dbic/blob/e580d8aa7803ff0f14df8f588a663cde0b8bd5fb/lib/HTML/FormHandler/TraitFor/Model/DBIC.pm#L492-L495

$value will have a body entry, not a body_id entry, from the submitted form. So it always falls back to the item lookup, so on creation that's nothing and on editing the body is the original body not the new one. In both those cases it therefore doesn't find the $value->{body} entry and incorrectly passes validation.

It needs to know how to turn either body_id into body or vice-versa, at the accessor for those columns, not the actual column names, but I'm not sure how best to do that.

dracos avatar May 23 '19 19:05 dracos