The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Catalyst::TraitFor::Model::DBIC::Schema::Result - PerRequest Result from Catalyst Request

SYNOPSIS

In your configuration, set the trait:

    MyApp->config(
      'Model::Schema' => {
        traits => ['Result'],
        schema_class => 'MyApp::Schema',
        connect_info => [ ... ],
      },
    );

Now in your actions you can call the generated models, which get their ->find($id) from $c->request->args.

    sub user :Local Args(1) {
      my ($self, $c, $id) = @_;

      ## Like: $c->model('Schema::User')->find($id)
      my $user = $c->model('Schema::User::Result');
    }

You can also control how the 'find' on the Resultset works via an action attribute ('ResultModelFrom') or via arguments passed to the 'model' call.

    sub user_with_attr :Local Args(1) ResultModelFrom(first_name=>$args[0]) {
      my ($self, $c, @args) = @_;

      ## Like: $c->model('Schema::User')->find({first_name=>$args[0]})
      my $user = $c->model('Schema::User::Result');
    }

If you want to use a resultset that is 'prepared' you can pass it as the first argument:

    sub from_rs :Local Args(1) {
      my ($self, $c, $id) = @_;
      my $rs = $c->model("Schema::User")->search({first_name=>['john','joe']});
      my $user = $c->model('Schema::User::Result', $rs);
    }

(This feature is probably most useful in a chaining setup.)

Lastly, if you invoke this method on an action the explicitly defines no arguments you get a new result rather than a database lookup

    sub new_user_result :Local Args(0) {
      my ($self, $c) = @_;

      ## Like: $c->model('Schema::User')->new_result(+{});
      my $new_user_result = $c->model('Schema::User::Result');
    }

DESCRIPTION

Its a common case to get the result of a DBIx::Class '->find' based on the current Catalyst request (typically from the Args attribute). This is an experimental trait to see if we can usefully encapsulate that common task in a way that is not easily broken.

If you can't read the source code and figure out what is going on, might want to stay away for now!

When you compose this trait into your MyApp::Model::Schema (subclass of Catalyst::Model::DBIC::Schema) it automatically creates a second PerRequest model for each ResultSource in your Schema. This new Model is named by taking the name of the resultsource (for example 'Schema::User') and adding '::Result' to it (or in the example case 'Schema::User::Result'). When you request an instance of this model, it will automatically assume the arguments of the current action is intended to be the index by which the ->find locates your database row. So basically the two following actions are the same in effect:

With trait:

    sub user :Local Args(1) {
      my ($self, $c, $id) = @_;
      my $user = $c->model('Schema::User::Result');
    }

Without trait:

    sub user :Local Args(1) {
      my ($self, $c, $id) = @_;
      my $user = $c->model('Schema::User')->find($id);
    }

I recommend that if you can use Catalyst 5.9009x+ that you use a type constraint to make sure the argument is the correct type (otherwise you risk generating a database error if the user tries to submit a string arg and the database is expecting an integer:

    use Types::Standard 'Int';

    sub user :Local Args(Int) {
      my ($self, $c) = @_;
      my $user = $c->model('Schema::User::Result');
    }

SUBROUTINE ATTRIBUTES

For the case when a result is complex (requires more than one argument) or you want to use a key other then the PK, you may add a subroutine argument to describe the pattern:

  sub user_with_attr :Local Args(1) ResultModelFrom(first_name=>$args[0]) {
    my ($self, $c) = @_;
  }

This is experimental and may change as needed. Basically this get converted to a hashref and submitted to ->find.

Actions with no arguments

If you current action has no arguments, we instead return a new result, which is a DBIC result that is not yet in storage. You can use this to make a new row in the database and save it:

    sub new_result :Local Args(0) {
        my ($self, $c) = @_;
        my $new_user = $c->model('Schema::User::Result');
        $new_user->name('Fido');
        $new_user->insert; # Save the new user.
    }

You might find this useful in some common patterns for validating POST parameters and using them to create a new object in the database.

Please note that you should be quite explicit in setting Args(0) since in many cases leaving the Args attribute off defaults to 'as many args as you care to send!

Passing arguments to ->model

Lastly, you may passing arguments to find via the ->model call. The following are examples

With trait:

    sub user :Local Args(1) {
      my ($self, $c) = @_;
      my $user = $c->model('Schema::User::Result', $some_other_id);
    }

Without trait:

    sub user :Local Args(1) {
      my ($self, $c, $id) = @_;
      my $user = $c->model('Schema::User')->find($some_other_id);
    }

With trait:

    sub user :Local Args(1) {
      my ($self, $c) = @_;
      my $user = $c->model('Schema::User::Result', +{first_name=>'john'});
    }

Without trait:

    sub user :Local Args(1) {
      my ($self, $c, $id) = @_;
      my $user = $c->model('Schema::User')->find({first_name=>'john'});
    }

If you choose to pass arguments this way, each call will 'reset' the current model (changing PerRequest into a Factory type). This behavior is still subject to change).

SEE ALSO

Catalyst, Catalyst::Model::DBIC::Schema.

AUTHOR

John Napiorkowski email:jjnapiork@cpan.org

COPYRIGHT & LICENSE

Copyright 2017, John Napiorkowski email:jjnapiork@cpan.org

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.