1 package SL::Helper::Csv;
9 use Params::Validate qw(:all);
10 use Rose::Object::MakeMethods::Generic scalar => [ qw(
11 file encoding sep_char quote_char header header_acc class numberformat
12 dateformat _io _csv _objects _parsed _data
20 my %params = validate(@_, {
21 sep_char => { default => ';' },
22 quote_char => { default => '"' },
23 header => { type => ARRAYREF, optional => 1 },
24 header_acc => { type => HASHREF, optional => 1 },
31 my $self = bless {}, $class;
33 $self->$_($params{$_}) for keys %params;
35 $self->_io(IO::File->new);
36 $self->_csv(Text::CSV->new({
38 sep_char => $self->sep_char,
39 quote_char => $self->quote_char,
47 my ($self, %params) = @_;
50 return unless $self->_check_header;
51 return unless $self->_parse_data;
62 my ($self, %params) = @_;
63 croak 'no class given' unless $self->class;
64 croak 'must parse first' unless $self->_parsed;
66 $self->_make_objects unless $self->_objects;
67 return wantarray ? @{ $self->_objects } : $self->_objects;
73 my ($self, %params) = @_;
75 $self->encoding($self->_guess_encoding) if !$self->encoding;
77 $self->_io->open($self->file, '<' . $self->_encode_layer)
78 or die "could not open file " . $self->file;
84 my ($self, %params) = @_;
85 return $self->header if $self->header;
87 my $header = $self->_csv->getline($self->_io);
89 $self->header($header);
93 my ($self, %params) = @_;
96 $self->_csv->column_names(@{ $self->header });
98 push @data, $self->_csv->getline_hr($self->_io)
99 while !$self->_csv->eof;
101 $self->_data(\@data);
105 ':encoding(' . $_[0]->encoding . ')';
109 my ($self, %params) = @_;
112 eval "require " . $self->class;
113 local $::myconfig{numberformat} = $self->numberformat if $self->numberformat;
114 local $::myconfig{dateformat} = $self->dateformat if $self->dateformat;
116 for my $line (@{ $self->_data }) {
117 push @objs, $self->class->new(
119 ($self->header_acc && $self->header_acc->{$_}) || $_ => $line->{$_}
120 } grep { $_ } keys %$line
124 $self->_objects(\@objs);
127 sub _guess_encoding {
139 SL::Helper::Csv - take care of csv file uploads
145 my $csv = SL::Helper::Csv->new(
146 file => \$::form->{upload_file},
147 encoding => 'utf-8', # undef means utf8
148 sep_char => ',', # default ';'
149 quote_char => ''', # default '"'
150 header => [qw(id text sellprice word)] # see later
151 header_acc => { sellprice => 'sellprice_as_number' }
152 class => 'SL::DB::CsvLine', # if present, map lines to this
155 my $status = $csv->parse;
156 my @hrefs = $csv->get_data;
157 my @objects = $scv->get_objects;
163 Text::CSV offeres already good functions to get lines out of a csv file, but in
164 most cases you will want those line to be parsed into hashes or even objects,
165 so this model just skips ahead and gives you objects.
167 Encoding autodetection is not easy, and should not be trusted. Try to avoid it if possible.
175 Standard constructor. You can use this to set most of the data.
179 Do the actual work. Will return true ($self actually) if success, undef if not.
183 Parse the data into objects and return those.
187 Returns an arrayref of the raw lines as hashrefs.
191 The file which contents are to be read. Can be a name of a physical file or a
192 scalar ref for memory data.
196 Encoding of the CSV file. Note that this module does not do any encoding guessing.
197 Know what your data ist. Defaults to utf-8.
203 Same as in L<Text::CSV>
205 =item C<header> \@FIELDS
207 can be an array of columns, in this case the first line is not used as a
208 header. Empty header fields will be ignored in objects.
210 =item C<header_acc> \%ACCESSORS
212 May be used to map header fields to custom accessors. Example:
214 { listprice => listprice_as_number }
216 In this case C<listprice_as_number> will be used to read in values from the
221 If present, the line will be handed to the new sub of this class,
222 and the return value used instead of the line itself.