NAME

Yancy::Guides::Model - Building modules for your application's business logic

VERSION

version 1.078

DESCRIPTION

This guide describes how to extend Yancy::Model to add custom business logic.

Model Classes

There are three types of classes in Yancy::Model:

Model

The model class represents the entire database and contains schemas.

Schema

Schema classes represent individual tables, views, or collections and contain items.

Item

Item classes represent individual records from the database.

Using the Default Model

The default Yancy::Model class contains basic CRUD (Create, Retrieve, Update, Delete) methods.

Setup

For now, you must set up the model manually by creating a Yancy::Model object, passing in a Yancy::Backend:

my $model = Yancy::Model->new( backend => $backend );

Schemas

Get a schema using the schema method:

my $users = $model->schema( 'users' );

This returns a Yancy::Model::Schema object (or a custom schema class, see "Schema Classes").

Creating Items

To create an item, use the create method on the schema:

my $user_id = $model->schema( 'users' )->create({
    username => 'bender',
    password => 'shiny',
});

Only the ID of the new item is returned.

Lookup by ID

To look up an item by its ID, use the get method on the schema.

my $bender = $model->schema( 'users' )->get( $bender_id );

This method returns a Yancy::Model::Item object (or a custom item class, see "Item Classes").

To search for items, use the list method on the schema. The list method returns a hash reference with items and total keys. items is an array reference of Yancy::Model::Item objects. total is the total number of items that match the query.

my $result = $model->schema( 'users' )->list({ active => 1 });
if ( $result->{total} > 0 ) {
    my @users = $result->{items}->@*;
    say "Found $result->{total} active users: ",
        map { $_->{username} } @users;
}

Updating Items

To update an item, first get the item object and then call set to update its data.

my $bender = $model->schema( 'users' )->get( $bender_id );
$bender->set({ password => 'daffodil' });

Bulk-updating items is not yet supported, but may be added in a later release (patches welcome!)

Deleting Items

If you already have an item, you can use its delete method to delete it. Otherwise, you can delete an item by its ID.

$model->schema( 'users' )->get( $user_id )->delete;
$model->schema( 'users' )->delete( $user_id );

Bulk-deleting items is not yet supported, but may be added in a later release (patches welcome!)

Relationships

Related data can be prefetched and added to an item using the join option to get or list.

my $user = $model->schema( 'users' )->get( $id, join => 'user_roles' );
my $result = $model->schema( 'users' )->list( {}, join => 'user_roles' );

The resulting data is added in a key named for the join:

my $roles = $user->{user_roles};

TODO: Items could have a related method that gets related data after-the-fact.

Future Development: Queries

In the future, a query method may be added that will return an object that can build up arguments for a call to the list method, as well as act as a cursor for that call.

Starting a Custom Model

When building your own model layer, you only need to create classes when you want to add custom code. By default, the Yancy::Model class will load classes from all of its configured namespaces.

Make sure to add your namespace to the "namespaces" in Yancy::Model array:

unshift @{ $model->namespaces }, 'MyApp';

This adds MyApp::Schema as a namespace for schema classes, and MyApp::Item as a namespace for item classes.

Create a Model Class

If you need it, you can create a model class by extending Yancy::Model.

package MyApp::Model;
use Mojo::Base 'Yancy::Model', -signatures;

Your model class could include methods for the most common data lookups:

sub get_user( $self, $id ) {
    return $self->schema( 'user' )->get( $id );
}

Your model class should also store configuration needed by the other classes:

# Invitation e-mails come from this address
has email_from => 'no-reply@example.com';

Schema Classes

Schema classes contain code for working with the entire schema or a subset of the schema. Any code that affects more than one item should live in the schema class.

To create a schema class, extend Yancy::Model::Schema. The name of the schema class should be the camel-cased version of the schema's name.

package MyApp::Schema::User; # schema: 'user'
use Mojo::Base 'Yancy::Model::Schema', -signatures;

The schema class should contain methods that work on the collection: Creating new items, searching for items.

# Invite a new user
sub invite( $self, $email ) {
    my $id = $self->create({ email => $email });
    $self->get( $id )->send_invite_mail;
    return $id;
}

Item Classes

Item classes contain code for working on a single item.

To create an item class, extend Yancy::Model::Item. The name of the item class should be the camel-cased version of the schema's name.

package MyApp::Item::User;
use Mojo::Base 'Yancy::Model::Item', -signatures;

# Send the invite mail to this user
sub send_invite_mail( $self ) {
    my $to = $self->data->{email};
    my $from = $self->model->email_from;
    # TODO: Send e-mail
}

SEE ALSO

Yancy::Model, Yancy::Guides::Schema, Yancy::Backend, Yancy::Guides

AUTHOR

Doug Bell <preaction@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2021 by Doug Bell.

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