A callback in the Controller that talks to the Model

Overview

In this example, we'll update a timestamp and increment a login count when a user logs into our application. The example assumes a user table with last_login and login_count fields. In addition, the Result class for the user table loads the TimeStamp component so we can define the timestamp data type. This petit article focuses on how to trigger the update of login timestamp and count incrementer from the login action.

Pertinent Model (Result Class) Parts

Load the TimeStamp Component

__PACKAGE__->load_components(qw/EncodedColumn TimeStamp Core/);

Here we are loading TimeStamp component which provides a timestamp data_type for the last_login column. As a side note the EncodedColumn is being load since it's a user table with a password field that we want encrypted.

add_columns

The login time and count columns are defined as such:

    "last_login",
    {
        data_type     => "timestamp",
        set_on_create => '0',
        set_on_update => '0',
    },
    "login_count",
    {
        data_type     => "integer",
        default_value => 0,
        is_nullable   => 0,
        size          => 4
    },

Note: last_login uses the data_type of timestamp which the TimeStamp component provides. In addition, we don't set the timestamp when we create or update the record because we don't want edits to the user record to affect the login timestamp and count. We'll update those field via the following:

record_login method

This method does our user record update for us:

sub record_login () {
    my $self = shift;
    $self->update(
        { last_login => DateTime->now, login_count => \'login_count + 1' } );
}

Rename DateTime Type to DateTimeType

My Result class is also a Reacton::Class which orginally had a DateTime type for the last_login attribute. This causes problems when I want to do DateTime->now. The dirty workaround is DateTime::->now, but the preferred way is to rename the type (gracias to Sub::Exporter) like so:

use Reaction::Types::DateTime DateTime => { -as => 'DateTimeType' };
has 'last_login'  => ( isa => DateTimeType, is => 'ro', required => 1 );

Controller Parts

Login Action

Now let's show the pertinent parts of our Auth.pm controller and how we will call the record_login method. Let's focus on the login action:

sub login :Chained('/base') :Args(0) {
  my ($self$c) = @_;
  $self->push_viewport(LoginVP,
    model => LoginAction->new(ctx => $c, target_model => $c),
    on_apply_callback => $self->make_context_closure(
        sub { 
             my $weak_c = shift;
             $self->after_login$weak_c@_ );
        }
    ),
    field_order       => [ qw(username password) ],
    reset_passwd_uri  => $c->uri_for($self->action_for('reset_password')),
  );
}

Callback

The key to hooking into the record_login() method is the on_apply_callback attribute which calls the after_login method Here's what the afer_login method looks like:

sub after_login {
    my ( $self$c) = @_;
    $c->res->redirect($c->uri_for('/'));
    $c->user->record_login;
}

Notice that it's in the after_login method that we do the record_login method.

Summary

In summary, when a user logs in the time will be recorded and their login count will be incremented by one. We do this by using a callback that invokes a model method during the login process. As a side note, in the callback we are also redirecting the user to the root page. We can add as much functionality into the callback as we want.

My tags:
 
Popular tags:
 
Powered by Catalyst
Powered by MojoMojo Hosted by Shadowcat - Managed by Nordaaker