#$self->{subform} = [];
#my %formatters_db;
#my %formatters_f;
# $self->{dates_formatted} = \(keys %{$self->{date_formatters}});
foreach my $v ( keys %{ $self->{date_formatters} } ) {
$log{ $id }->debug( "** " . $v . " **" );
push @dates, $v;
}
$self->{dates_formatted} = \@dates;
my %hdates = map { $_ => 1 } @dates;
$self->{hdates_formatted} = \%hdates;
$self->{dates_formatters} = {};
$self->{inserting} = 0;
$self->{pos2del} = [];
#parameter $in_db is 0 or 1 : # 0 we are reading from the db, and the format to use are at the pos 0 and 1 in the array of format for the field # 1 we are writing to the db and the format are to use in a revers order # $id is the field id # $v the date string from the form (if in_db is 1) or from the db (if in_db is 0) sub _format_date { my ( $self, $in_db, $id, $v ) = @_; my $id = id $self; $log{ $id }->debug( "format_date received date: " . $v ); my ( $pos1, $pos2 ) = ( $in_db ? ( 1, 0 ) : ( 0, 1 ) ); my $format = $self->{date_formatters}->{$id}->[$pos1]; my $f = $self->_get_dateformatter($format); my $dt = $f->parse_datetime($v) or croak( $f->errmsg ); $log{ $id }->debug( "format_date: date time object ymd: " . $dt->ymd ); $format = $self->{date_formatters}->{$id}->[$pos2]; $f = $self->_get_dateformatter($format); my $r = $f->format_datetime($dt) or croak( $f->errmsg ); $log{ $id }->debug( "format_date formatted date: " . $r );
return $r;
}
NAME
Gtk2::Ex::DbLinker::Form - a module that display data from a database in glade generated Gtk2 interface
VERSION
See Version in Gtk2::Ex::DbLinker
SYNOPSIS
use Rdb::Coll::Manager;
use Rdb::Biblio::Manager;
use Gtk2::Ex::DbLinker::RdbDataManager;
use Gtk2::Ex::DbLinker::Form;
use Gtk2 -init;
use Gtk2::GladeXML;
my $builder = Gtk2::Builder->new();
$builder->add_from_file($path_to_glade_file);
$builder->connect_signals($self);
This gets the Rose::DB::Object::Manager (we could have use plain sql command, or DBIx::Class object instead), and the DataManager object we pass to the form constructor.
my $data = Rdb::Mytable::Manager->get_mytable(query => [pk_field => {eq => $value]);
my $dman = Gtk2::Ex::DbLinker::RdbDataManager->new(data=> $data, meta => Rdb::Mytable->meta );
This create the form.
$self->{form_coll} = Gtk2::Ex::DbLinker::Form->new(
data_manager => $dman,
meta => Rdb::Mytable->meta,
builder => $builder,
rec_spinner => $self->{dnav}->get_object('RecordSpinner'),
status_label=> $self->{dnav}->get_object('lbl_RecordStatus'),
rec_count_label => $self->{dnav}->get_object("lbl_recordCount"),
on_current => sub {on_current($self)},
date_formatters => {
field_id1 => ["%Y-%m-%d", "%d-%m-%Y"],
field_id2 => ["%Y-%m-%d", "%d-%m-%Y"], },
time_zone => 'Europe/Zurich',
locale => 'fr_CH',
);
rec_spinner
, status_label
, rec_count_label
are Gtk2 widget used to display the position of the current record. See one of the example 2 files in the examples folder for more details. date_formatters
receives a hash of id for the Gtk2::Entries in the Glade file (keys) and an arrays (values) of formating strings.
In this array
pos 0 is the date format of the database.
pos 1 is the format to display the date in the form.
time_zone
and locale
are needed by Date::Time::Strptime.
To display new rows on a bound subform, connect the on_change event to the field of the primary key in the main form. In this sub, call a sub to synchonize the form:
In the main form:
sub on_nofm_changed {
my $widget = shift;
my $self = shift;
my $pk_value = $widget->get_text();
$self->{subform_a}->synchronize_with($pk_value);
...
}
In the subform_a module
sub synchronize_with {
my ($self,$value) = @_;
my $data = Rdb::Product::Manager->get_product(with_objects => ['seller_product'], query => ['seller_product.no_seller' => {eq => $value}]);
$self->{subform_a}->get_data_manager->query($data);
$self->{subform_a}->update;
}
Dealing with many to many relationship
It's the sellers and products situation where a seller sells many products and a product is selled by many sellers. One way is to have a insert statement that insert a new row in the linking table (named transaction for example) each time a new row is added in the product table.
An other way is to create a data manager for the transaction table
With DBI
$dman = Gtk2::Ex::DbLinker::DbiDataManager->new( dbh => $self->{dbh}, sql =>{select =>"no_seller, no_product", from => "transaction", where => ""});
With Rose::DB::Object
$data = Rdb::Transaction::Manager->get_transaction(query=> [no_seller => {eq => $current_seller }]);
$dman = Gtk2::Ex::DbLinker::RdbDataManager->new(data => $data, meta=> Rdb::Transaction->meta);
And keep a reference of this for latter
$self->{linking_data} = $dman;
If you want to link a new row in the table product with the current seller, create a method that is passed and array of primary key values for the current seller and the new product.
sub update_linking_table {
my ( $self, $keysref) = @_;
my @keys = keys %{$keysref};
my $f = $self->{main_form};
my $dman = $self->{main_abo}->{linking_data};
$dman->new_row;
foreach my $k (@keys){
my $value = ${$keysref}{$k};
$dman->set_field($k, $value );
}
$dman->save;
}
This method is to be called when a new row has been added to the product table:
sub on_newproduct_applied_clicked {
my $button = shift;
my $self = shift;
my $main = $f->{main_form};
$self->{product}->apply;
my %h;
$h{no_seller}= $main->{no_seller};
$h{no_product}= $self->{abo}->get_widget_value("no_product");
$self->update_linking_table(\%h);
}
You may use the same method to delete a row from the linking table
my $data = Rdb::Transaction::Manager->get_transaction(query=> [no_seller => {eq => $seller }, no_product=>{eq => $product } ] );
$f->{linking_data}->query($data);
$f->{linking_data}->delete;
DESCRIPTION
This module automates the process of tying data from a database to widgets on a Glade-generated form. All that is required is that you name your widgets the same as the fields in your data source.
Steps for use:
Create a xxxDataManager object that contains the rows to display
Create a Gtk2::GladeXML object (the form widget)
Create a Gtk2::Ex::DbLinker::Form object that links the data and your form
You would then typically connect the buttons to the methods below to handle common actions such as inserting, moving, deleting, etc.
METHODS
constructor
The new();
method expects a list or a hash reference of parameters name => value pairs
data_manager
a instance of a xxxDataManager objectbuilder
a Gtk2::GladeXML builder
The following parameters are optional:
datawidgets
a reference to an array of id in the glade file that will display the fieldsrec_spinner
the name of a GtkSpinButton to use as the record spinner or a reference to this widget. The default is to use a widget called RecordSpinner.rec_count_label
name (default to "lbl_RecordCount") or a reference to a label that indicate the position of the current row in the rowsetstatus_label
name (default to "lbl_RecordStatus") or a reference to a label that indicate the changed or syncronized flag of the current rowon_current
a reference to sub that will be called when moving to a new recorddate_formatters
a reference to an hash of Gtk2Entries id (keys), and format strings that follow Rose::DateTime::Util (value) to display formatted Dateauto_apply
defaults to 1, meaning that apply will be called if a changed has been made in a widget before moving to another record. Set this to 0 if you don't want this feature
add_combo_values( $widget, $array_ref);
Populates a Gtk2::ComboBox or Gtk2::ComboBoxEntry widget with a static list of values. The array whose reference is stored in $array_ref is a list of array that described the rows: [[return value1, displayed value1], [...], ...]
add_combo( {data_manager => $dman, id => 'noed', fields => ["id", "nom"], );
Once the constructor has been called, combo designed in the glade file received their rows with this method. The parameter is a list of parameters name => value, or a hash reference of the same.
The paramaters are:
data_manager
a dataManager instance that holds the rows of the comboid
the id of the widget in the glade filefields
an array reference holdings the names of fields in the combo (this parameter is needed with RdbDataManager only)
Gtk2::Ex::DbLinker::Form-
add_combo({ data_manager => $combodata, id => 'countryid', builder => $builder, }); >
This method can also be called as a class method, when the underlying form is not bound to any table. You need to pass the Gtk2::Builder object as a supplemental parameter.
get_widget_value ( $widget_id );
Returns the value of a data widget from its id
set_widget_value ( $widget_id, $value )
;
Sets the value of a data widget from its id
Methods applied to a row of data
insert()
See "insert()" in Gtk2::Ex::DbLinker::AbFormdelete()
See "delete()" in Gtk2::Ex::DbLinker::AbFormapply()
See "apply()" in Gtk2::Ex::DbLinker::AbFormundo()
See "undo()" in Gtk2::Ex::DbLinker::AbFormnext()
See "Moving between rows" in Gtk2::Ex::DbLinker::AbFormprevious()
See "Moving between rows" in Gtk2::Ex::DbLinker::AbFormfirst()
See "Moving between rows" in Gtk2::Ex::DbLinker::AbFormlast()
See "Moving between rows" in Gtk2::Ex::DbLinker::AbFormadd_childform( $childform )
See "add_childform( $childform )" in Gtk2::Ex::DbLinker::AbFormhas_changed()
See "has_changed()" in Gtk2::Ex::DbLinker::AbForm
SUPPORT
Any Gk2::Ex::DbLinker questions or problems can be posted to me (rappazf) on my gmail account.
The current state of the source can be extract using Mercurial from http://sourceforge.net/projects/gtk2-ex-dblinker/.
AUTHOR
François Rappaz <rappazf@gmail.com>
COPYRIGHT
Copyright (c) 2014-2017 by F. Rappaz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
SEE ALSO
CREDIT
Daniel Kasak, whose modules initiate this work.