1 package DBIx::Class::Admin;
5 use Carp::Clan qw/^DBIx::Class/;
7 croak('The following modules are required for DBIx::Class::Admin ' . DBIx::Class::Optional::Dependencies->req_missing_for ('admin') )
8 unless DBIx::Class::Optional::Dependencies->req_ok_for ('admin');
12 use MooseX::Types::Moose qw/Int Str Any Bool/;
13 use DBIx::Class::Admin::Types qw/DBICConnectInfo DBICHashRef/;
14 use MooseX::Types::JSON qw(JSON);
15 use MooseX::Types::Path::Class qw(Dir File);
17 use JSON::Any qw(DWIW XS JSON);
18 use namespace::autoclean;
22 DBIx::Class::Admin - Administration object for schemas
28 $ dbicadmin --schema=MyApp::Schema \
29 --connect='["dbi:SQLite:my.db", "", ""]' \
32 $ dbicadmin --schema=MyApp::Schema --class=Employee \
33 --connect='["dbi:SQLite:my.db", "", ""]' \
34 --op=update --set='{ "name": "New_Employee" }'
36 use DBIx::Class::Admin;
39 my $admin = DBIx::Class::Admin->new(
40 schema_class=> 'MY::Schema',
42 connect_info => { dsn => $dsn, user => $user, password => $pass },
46 $admin->create('SQLite');
48 # create SQL diff for an upgrade
49 $admin->create('SQLite', {} , "1.0");
54 # install a version for an unversioned schema
55 $admin->install("3.0");
59 The Admin interface has additional requirements not currently part of
60 L<DBIx::Class>. See L<DBIx::Class::Optional::Dependencies> for more details.
66 the class of the schema to load
70 has 'schema_class' => (
78 A pre-connected schema object can be provided for manipulation
84 isa => 'DBIx::Class::Schema',
91 Class::MOP::load_class($self->schema_class);
93 $self->connect_info->[3]->{ignore_version} =1;
94 return $self->schema_class->connect(@{$self->connect_info()} ); # , $self->connect_info->[3], { ignore_version => 1} );
100 a resultset from the schema to operate on
112 a hash ref or json string to be used for identifying data to manipulate
125 a hash ref or json string to be used for inserting or updating data
138 a hash ref or json string to be used for passing additonal info to the ->search call
151 connect_info the arguments to provide to the connect call of the schema_class
155 has 'connect_info' => (
157 isa => DBICConnectInfo,
162 sub _build_connect_info {
164 return $self->_find_stanza($self->config, $self->config_stanza);
170 config_file provide a config_file to read connect_info from, if this is provided
171 config_stanze should also be provided to locate where the connect_info is in the config
172 The config file should be in a format readable by Config::General
185 config_stanza for use with config_file should be a '::' deliminated 'path' to the connection information
186 designed for use with catalyst config files
190 has 'config_stanza' => (
198 Instead of loading from a file the configuration can be provided directly as a hash ref. Please note
199 config_stanza will still be required.
212 eval { require Config::Any }
213 or die ("Config::Any is required to parse the config file.\n");
215 my $cfg = Config::Any->load_files ( {files => [$self->config_file], use_ext =>1, flatten_to_hash=>1});
217 # just grab the config from the config file
218 $cfg = $cfg->{$self->config_file};
225 The location where sql ddl files should be created or found for an upgrade.
238 Used for install, the version which will be 'installed' in the schema
250 Previouse version of the schema to create an upgrade diff for, the full sql for that version of the sql must be in the sql_dir
262 Try and force certain operations.
274 Be less verbose about actions
295 =item Arguments: $sqlt_type, \%sqlt_args, $preversion
299 L<create> will generate sql for the supplied schema_class in sql_dir. The flavour of sql to
300 generate can be controlled by suppling a sqlt_type which should be a L<SQL::Translator> name.
302 Arguments for L<SQL::Translator> can be supplied in the sqlt_args hashref.
304 Optional preversion can be supplied to generate a diff to be used by upgrade.
309 my ($self, $sqlt_type, $sqlt_args, $preversion) = @_;
311 $preversion ||= $self->preversion();
313 my $schema = $self->schema();
314 # create the dir if does not exist
315 $self->sql_dir->mkpath() if ( ! -d $self->sql_dir);
317 $schema->create_ddl_dir( $sqlt_type, (defined $schema->schema_version ? $schema->schema_version : ""), $self->sql_dir->stringify, $preversion, $sqlt_args );
325 =item Arguments: <none>
329 upgrade will attempt to upgrade the connected database to the same version as the schema_class.
330 B<MAKE SURE YOU BACKUP YOUR DB FIRST>
336 my $schema = $self->schema();
337 if (!$schema->get_db_version()) {
338 # schema is unversioned
339 $schema->throw_exception ("Could not determin current schema version, please either install() or deploy().\n");
341 my $ret = $schema->upgrade();
351 =item Arguments: $version
355 install is here to help when you want to move to L<DBIx::Class::Schema::Versioned> and have an existing
356 database. install will take a version and add the version tracking tables and 'install' the version. No
357 further ddl modification takes place. Setting the force attribute to a true value will allow overriding of
358 already versioned databases.
363 my ($self, $version) = @_;
365 my $schema = $self->schema();
366 $version ||= $self->version();
367 if (!$schema->get_db_version() ) {
368 # schema is unversioned
369 print "Going to install schema version\n";
370 my $ret = $schema->install($version);
371 print "retun is $ret\n";
373 elsif ($schema->get_db_version() and $self->force ) {
374 carp "Forcing install may not be a good idea";
375 if($self->_confirm() ) {
376 $self->schema->_set_db_version({ version => $version});
380 $schema->throw_exception ("Schema already has a version. Try upgrade instead.\n");
390 =item Arguments: $args
394 deploy will create the schema at the connected database. C<$args> are passed straight to
395 L<DBIx::Class::Schema/deploy>.
400 my ($self, $args) = @_;
401 my $schema = $self->schema();
402 if (!$schema->get_db_version() ) {
403 # schema is unversioned
404 $schema->deploy( $args, $self->sql_dir)
405 or $schema->throw_exception ("Could not deploy schema.\n"); # FIXME deploy() does not return 1/0 on success/fail
407 $schema->throw_exception("A versioned schema has already been deployed, try upgrade instead.\n");
415 =item Arguments: $rs, $set
419 insert takes the name of a resultset from the schema_class and a hashref of data to insert
425 my ($self, $rs, $set) = @_;
427 $rs ||= $self->resultset();
428 $set ||= $self->set();
429 my $resultset = $self->schema->resultset($rs);
430 my $obj = $resultset->create( $set );
431 print ''.ref($resultset).' ID: '.join(',',$obj->id())."\n" if (!$self->quiet);
439 =item Arguments: $rs, $set, $where
443 update takes the name of a resultset from the schema_class, a hashref of data to update and
444 a where hash used to form the search for the rows to update.
449 my ($self, $rs, $set, $where) = @_;
451 $rs ||= $self->resultset();
452 $where ||= $self->where();
453 $set ||= $self->set();
454 my $resultset = $self->schema->resultset($rs);
455 $resultset = $resultset->search( ($where||{}) );
457 my $count = $resultset->count();
458 print "This action will modify $count ".ref($resultset)." records.\n" if (!$self->quiet);
460 if ( $self->force || $self->_confirm() ) {
461 $resultset->update_all( $set );
470 =item Arguments: $rs, $where, $attrs
474 delete takes the name of a resultset from the schema_class, a where hashref and a attrs to pass to ->search.
475 The found data is deleted and cannot be recovered.
480 my ($self, $rs, $where, $attrs) = @_;
482 $rs ||= $self->resultset();
483 $where ||= $self->where();
484 $attrs ||= $self->attrs();
485 my $resultset = $self->schema->resultset($rs);
486 $resultset = $resultset->search( ($where||{}), ($attrs||()) );
488 my $count = $resultset->count();
489 print "This action will delete $count ".ref($resultset)." records.\n" if (!$self->quiet);
491 if ( $self->force || $self->_confirm() ) {
492 $resultset->delete_all();
501 =item Arguments: $rs, $where, $attrs
505 select takes the name of a resultset from the schema_class, a where hashref and a attrs to pass to ->search.
506 The found data is returned in a array ref where the first row will be the columns list.
511 my ($self, $rs, $where, $attrs) = @_;
513 $rs ||= $self->resultset();
514 $where ||= $self->where();
515 $attrs ||= $self->attrs();
516 my $resultset = $self->schema->resultset($rs);
517 $resultset = $resultset->search( ($where||{}), ($attrs||()) );
520 my @columns = $resultset->result_source->columns();
521 push @data, [@columns];#
523 while (my $row = $resultset->next()) {
525 foreach my $column (@columns) {
526 push( @fields, $row->get_column($column) );
528 push @data, [@fields];
536 print "Are you sure you want to do this? (type YES to confirm) \n";
537 # mainly here for testing
538 return 1 if ($self->meta->get_attribute('_confirm')->get_value($self));
539 my $response = <STDIN>;
540 return 1 if ($response=~/^YES/);
545 my ($self, $cfg, $stanza) = @_;
546 my @path = split /::/, $stanza;
547 while (my $path = shift @path) {
548 if (exists $cfg->{$path}) {
549 $cfg = $cfg->{$path};
552 die ("Could not find $stanza in config, $path does not seem to exist.\n");
560 See L<DBIx::Class/CONTRIBUTORS>.
564 You may distribute this code under the same terms as Perl itself