Extending a DBIx::Class model

A common occurrence is a situation in which your application has free reign over most of the database, but needs to use a few stored procedure calls to get to certain pieces of data. In that case, you'll want to create a normal DBIC schema, and then add methods for accessing the unusual data.

As an example, let's look back at the AddressBook application and imagine that for some reason we couldn't use DBIx::Class to access the user table, and instead need to write the raw SQL to return an array containing everyone's username. In AddressBook::Model::AddressDB, we just need to write a subroutine to do our work as follows:

package AddressBook::Model::AddressDB;
// other code in the package
sub get_users {
my $self = shift;
my $storage = $self->storage;
return $storage->dbh_do(
sub {
my $self = shift;
my $dbh = shift;
my $sth = $dbh->prepare('SELECT username FROM user'),
$sth->execute();
my @rows = @{$sth->fetchall_arrayref()};
return map { $_->[0] } @rows;
});
}

Here's how the code works. On the first line, we get our DBIC::Schema object and then obtain the schema's storage object. The storage object is what DBIC uses to execute its generated SQL on the database, and is usually an instance of DBIx::Class::Storage::DBI. This class contains a method called dbh_do that will execute a piece of code, passed to dbh_do as a coderef (or "anonymous subroutine"), and provide the code with a standard DBI database handle (usually called $dbh). dbh_do will make sure that the database handle is valid before it calls your code, so you don't need to worry about things like the database connection timing out. DBIC will reconnect if necessary and will then call your code. dbh_do will also handle exceptions raised within your code in a standard way, so that errors can be caught normally.

The rest of the code deals with actually executing our query. When the database handle is ready, it's passed as the second argument to our coderef (the first is the storage object itself, in case you happen to need that). Once we have the database handle, the rest of the code is exactly the same as if we were using plain DBI instead of DBIx::Class. We first prepare our query (which need not be a SELECT; it could be EXEC or anything else), execute it and, finally, process the result. The map statement converts the returned data to the form we expect it in, a list of names (instead of a list of rows each containing a single name). Note that the return statement in the coderef returns to dbh_do, not to the caller of get_users. This means that you can execute dbh_do as many times as required and then further process the results before returning from the get_users subroutine.

Once you've written this subroutine, you can easily call it from elsewhere in your application as follows:

my @users = $c->model('AddressDB')->get_users;
$c->response->body('All Users' join ', ', @users);

Custom methods without raw SQL

As the last example doesn't use any features of the database that DBIC doesn't explicitly expose in its resultset interface, let us see how we can implement the get_users function without using dbh_do. Although the preconditions of the example indicated that we couldn't use DBIC, it's good to compare the two approaches so that you can decide which way to do things in your application. Here's another way to implement the last example:

sub get_users {
# version 2
my $self = shift;
my $users = $self->resultset('User'),
my @result;
while(my $user = $users->next){
push @result, $user->username;
}
return @result;
}

This looks like the usual DBIC manipulation that we're used to. Usually, we call $c->model('AddressDB::User') to get the User resultset, but under the hood this is the same as $c->model('AddressDB')->resultset('User'). In this example, $self is the same as $c->model('AddressDB').

The last code snippet is cleaner and more portable (across database systems) than the dbh_do method, so it's best to prefer resultsets over dbh_do unless there's absolutely no other way to achieve the functionality you desire.

Calling database functions

Another common problem is the need to call database functions on tables that you're accessing with DBIC. Fortunately, DBIC provides syntax for this case, so we won't need to write any SQL manually and run it with dbh_do. All that's required is a second argument to search. For example, if we want to get the count of all users in the user table, we could write (in a Controller) the following:

$users = $c->model('AddressDB::User'),
$users->search({}, { select => [ { COUNT => 'id' } ],
as => [ 'count' ],});
$count = $users->first->get_column('count'),

This is the same as executing SELECT COUNT(id) FROM user, fetching the first row, and then setting $count to the first column of that row.

Note

Note that we didn't specify a WHERE clause, but if we wanted to, we could replace the first {} with the WHERE expression, and then get the count of matching rows.

Here's a function that we can place in the User ResultSet class to get easy access to the user count:

sub count_users_where {
my $self = shift;
my $condition = shift;
$self->search($condition,
{ select => [ { COUNT => 'id' } ], as => [ 'count' ], });
my $first = $users->first;
return $first->get_column('count') if $first;
return 0; # if there is no "first" row, return 0
}

Now, we can write something like the following:

$jons = $c->model('AddressDB::User')->
count_users_where([ username => {-like => '%jon%'}]);

to get the number of jons in the database, without having to fetch every record and count them.

If you need to work only with a single column, you can also use the DBIx::Class::ResultSetColumn interface.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.166.131