# PODNAME: Yancy::Guides::Model # ABSTRACT: Building modules for your application's business logic __END__ =pod =head1 NAME Yancy::Guides::Model - Building modules for your application's business logic =head1 VERSION version 1.081 =head1 DESCRIPTION This guide describes how to extend L to add custom business logic. =head1 Model Classes There are three types of classes in C: =over =item Model The model class represents the entire database and contains schemas. =item Schema Schema classes represent individual tables, views, or collections and contain items. =item Item Item classes represent individual records from the database. =back =head1 Using the Default Model The default L class contains basic CRUD (Create, Retrieve, Update, Delete) methods. =head2 Setup For now, you must set up the model manually by creating a L object, passing in a L: my $model = Yancy::Model->new( backend => $backend ); =head2 Schemas Get a schema using the C method: my $users = $model->schema( 'users' ); This returns a L object (or a custom schema class, see L). =head2 Creating Items To create an item, use the C method on the schema: my $user_id = $model->schema( 'users' )->create({ username => 'bender', password => 'shiny', }); Only the ID of the new item is returned. =head2 Lookup by ID To look up an item by its ID, use the C method on the schema. my $bender = $model->schema( 'users' )->get( $bender_id ); This method returns a L object (or a custom item class, see L). =head2 Search To search for items, use the C method on the schema. The C method returns a hash reference with C and C keys. C is an array reference of L objects. C 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; } =head2 Updating Items To update an item, first get the item object and then call C 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!) =head2 Deleting Items If you already have an item, you can use its C 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!) =head2 Relationships Related data can be prefetched and added to an item using the C option to C or C. 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}; B: Items could have a C method that gets related data after-the-fact. =head2 Future Development: Queries In the future, a C method may be added that will return an object that can build up arguments for a call to the C method, as well as act as a cursor for that call. =head1 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 L class will load classes from all of its configured L. Make sure to add your namespace to the L array: unshift @{ $model->namespaces }, 'MyApp'; This adds C as a namespace for schema classes, and C as a namespace for item classes. =for comment XXX: Create `yancy generate model`, schema, and item =head2 Create a Model Class If you need it, you can create a model class by extending L. 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'; =head2 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 L. 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; } =head2 Item Classes Item classes contain code for working on a single item. To create an item class, extend L. 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 } =head2 Adding Custom Validation To add custom validation to a schema, override the L. This method is called by both Schema and Item classes. =head2 Adding Input / Output Transforms (Filters) To transform a model's input before creation or update, override the L and the L. The C method of the Schema class is called by both Schema and Item classes. You cannot modify data in the C method. To transform a model's data after being read from the database, override the L. =head1 SEE ALSO L, L, L, L =head1 AUTHOR Doug Bell =head1 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. =cut