NAME
DBIx::Class::Async::Row - Asynchronous row object for DBIx::Class::Async
VERSION
Version 0.40
SYNOPSIS
use DBIx::Class::Async::Row;
# Typically created by DBIx::Class::Async, not directly
my $row = DBIx::Class::Async::Row->new(
schema => $schema,
async_db => $async_db,
source_name => 'User',
row_data => { id => 1, name => 'John', email => 'john@example.com' },
);
# Access columns
my $name = $row->name; # Returns 'John'
my $email = $row->get_column('email'); # Returns 'john@example.com'
# Get all columns
my %columns = $row->get_columns;
# Update asynchronously
$row->update({ name => 'John Doe' })->then(sub {
my ($updated_row) = @_;
say "Updated: " . $updated_row->name;
});
# Delete asynchronously
$row->delete->then(sub {
my ($success) = @_;
say "Deleted: " . ($success ? 'yes' : 'no');
});
# Discard changes and refetch from database
$row->discard_changes->then(sub {
my ($fresh_row) = @_;
# $fresh_row contains latest data from database
});
DESCRIPTION
DBIx::Class::Async::Row provides an asynchronous row object that represents a single database row in a DBIx::Class::Async application. It mimics the interface of DBIx::Class::Row but returns Future objects for asynchronous database operations.
This class is typically instantiated by DBIx::Class::Async and not directly by users. It provides both synchronous column access and asynchronous methods for database operations.
CONSTRUCTOR
new
my $row = DBIx::Class::Async::Row->new(
schema => $schema, # DBIx::Class::Schema instance
async_db => $async_db, # DBIx::Class::Async instance
source_name => $source_name, # Result source name
row_data => \%data, # Hashref of row data
);
Creates a new asynchronous row object.
- Parameters
-
schema-
A DBIx::Class::Schema instance. Required.
async_db-
A DBIx::Class::Async instance. Required.
source_name-
The name of the result source (table). Required.
row_data-
Hash reference containing the row's column data. Required.
- Throws
-
Croaks if any required parameter is missing.
METHODS
copy
my $future = $row->copy(\%changes);
$future->then(sub {
my ($new_row) = @_;
# use $new_row
});
Creates a copy of the current row object with optional modifications.
This method performs an asynchronous copy operation that:
Creates a new database record with the same column values as the current row
Automatically excludes auto-increment columns (primary keys) from the copy
Allows overriding specific column values through the
\%changesparameterReturns a Future that resolves to a new row object of the same class
Parameters:
$changes-
Optional hash reference containing column-value pairs to override in the copied row. The changes are applied after excluding auto-increment columns, so you can use this to set a different primary key if needed.
If not provided or
undef, an empty hash reference will be used.
Returns: Future that resolves to a new DBIx::Class::Async::Row object (or subclass) representing the copied database record.
Example:
# Simple copy
$product->copy->then(sub {
my ($copy) = @_;
say "Copied product ID: " . $copy->id;
});
# Copy with modifications
$product->copy({
name => $product->name . ' (Copy)',
sku => 'NEW-SKU-123'
})->then(sub {
my ($modified_copy) = @_;
# New row with changed name and SKU
});
Notes:
Auto-increment columns are automatically detected and excluded from the copy
The method performs the copy at the database level, not just object duplication
The returned row object will be of the appropriate subclass if one exists (e.g.,
DBIx::Class::Async::Row::Productfor a Product source)All database constraints and triggers will apply during the copy operation
The original row object remains unchanged
Throws: Exceptions from the underlying database operations will be propagated through the returned Future.
create_related
my $post_future = $user->create_related('posts', {
title => 'My First Post',
content => 'Hello World!'
});
my $post = await $post_future;
A convenience method that creates a new record in the specified relationship.
It internally calls related_resultset to identify the correct foreign key mapping (e.g., setting user_id to the current user's ID) and then invokes create on the resulting ResultSet.
This method returns a Future that resolves to a new Row object of the related type.
Note: Just like "create" in DBIx::Class::Async::ResultSet, this method automatically merges the relationship's foreign key constraints into the provided hashref, ensuring that NOT NULL constraints on the foreign key columns are satisfied.
delete
$row->delete
->then(sub {
my ($success) = @_;
if ($success) {
say "Row deleted successfully";
}
})
->catch(sub {
my ($error) = @_;
# Handle error
});
Asynchronously deletes the row from the database.
- Returns
-
A Future that resolves to true if the row was deleted, false otherwise.
- Throws
-
Croaks if the row doesn't have a primary key.
discard_changes
$row->discard_changes->then(sub {
my ($refreshed_row) = @_;
# Row data reloaded from database
});
Reloads the row data from the database, discarding any changes.
- Returns
-
A Future that resolves to the refreshed row object.
- Notes
-
This refetches the row from the database and clears the dirty columns hash.
get_column
my $value = $row->get_column($column_name);
Synchronously retrieves a column value from the row.
- Parameters
- Returns
-
The column value. If the column has an inflator defined, returns the inflated value.
- Throws
-
Croaks if the column doesn't exist.
get_columns
my %columns = $row->get_columns;
Returns all columns as a hash.
- Returns
-
Hash containing all column names and values.
get_dirty_columns
my %dirty = $row->get_dirty_columns;
my $dirty_ref = $row->get_dirty_columns;
Returns columns that have been modified but not yet saved.
- Returns
-
In list context: Hash of column-value pairs for dirty columns.
In scalar context: Hashref of column-value pairs for dirty columns.
- Examples
-
$row->set_column('name' => 'Alice'); $row->set_column('email' => 'alice@example.com'); my %dirty = $row->get_dirty_columns; # %dirty = (name => 'Alice', email => 'alice@example.com') my @dirty_cols = keys %dirty; say "Modified: " . join(', ', @dirty_cols);
get_inflated_columns
my %inflated_columns = $row->get_inflated_columns;
Returns all columns with inflated values where applicable.
- Returns
-
Hash containing all column names and inflated values.
id
my $id = $row->id; # Single primary key
my @ids = $row->id; # Composite primary key (multiple values)
Returns the primary key value(s) for a row.
- Arguments
-
None
- Returns
-
In list context: List of primary key values.
In scalar context: Single primary key value (for single-column primary keys) or arrayref of values (for composite primary keys).
- Throws
-
Dies if: - Called as a class method - No primary key defined for the source - Row is not in storage and primary key value is undefined
- Examples
-
# Single primary key my $user = $rs->find(1)->get; my $id = $user->id; # Returns: 1 # Composite primary key my $record = $rs->find({ key1 => 1, key2 => 2 })->get; my @ids = $record->id; # Returns: (1, 2) # Arrayref in scalar context (composite key) my $ids = $record->id; # Returns: [1, 2]
insert
$row->insert
->then(sub {
my ($inserted_row) = @_;
# Row has been inserted
});
Asynchronously inserts the row into the database.
Note: This method is typically called automatically by "create" in DBIx::Class::Async. For existing rows, it returns an already-resolved Future.
- Returns
-
A Future that resolves to the row object.
insert_or_update
await $row->insert_or_update;
Arguments: \%fallback_data? Return Value: Future resolving to $row
An alias for "update_or_insert". Provided for compatibility with standard DBIx::Class::Row method naming.
in_storage
if ($row->in_storage) {
# Row exists in database
}
Checks whether the row exists in the database.
- Returns
-
True if the row is in storage (has a primary key and hasn't been deleted), false otherwise.
is_column_changed
if ($row->is_column_changed('name')) {
say "Name was modified";
}
Checks if a specific column has been modified but not yet saved.
- Arguments
- Returns
-
True if the column is dirty (modified), false otherwise.
is_column_dirty
if ($row->is_column_dirty('email')) {
print "Email has been changed but not saved yet!";
}
Arguments: $column_name Return Value: 1|0
Returns a boolean value indicating whether the specified column has been modified since the row was last fetched from or saved to the database.
make_column_dirty
$row->make_column_dirty('name');
Marks a column as dirty even if its value hasn't changed.
related_resultset
my $rs = $row->related_resultset($relationship_name);
Returns a resultset for a related table.
- Parameters
- Returns
-
A DBIx::Class::ResultSet for the related table, filtered by the relationship condition.
- Throws
-
Croaks if the relationship doesn't exist.
Croaks if the relationship condition cannot be parsed.
result_source
my $source = $row->result_source;
Returns the DBIx::Class::ResultSource for this row.
- Returns
-
The result source object, or undef if not available.
set_column
$row->set_column('name' => 'Alice');
$row->set_column('email' => 'alice@example.com');
Sets a raw column value. If the new value is different from the old one, the column is marked as dirty for when you next call update.
- Arguments
- Returns
-
The value that was set.
- Notes
-
- If the new value differs from the old value, the column is marked as dirty - If passed an object or reference, it will be stored as-is - Use
set_inflated_columnsfor proper inflation/deflation - Better yet, use column accessors:$row->name('Alice') - Examples
-
# Set a simple value $row->set_column('name' => 'Bob'); # Set to undef $row->set_column('email' => undef); # Mark as dirty and update $row->set_column('active' => 0); $row->update->get;
set_columns
$row->set_columns({
name => 'Alice',
email => 'alice@example.com',
active => 1,
});
Sets multiple column, raw value pairs at once.
- Arguments
- Returns
-
The row object itself (for chaining).
- Examples
-
# Set multiple columns $row->set_columns({ name => 'Bob', email => 'bob@example.com', active => 1, }); # Chain with update $row->set_columns({ name => 'Carol' })->update->get;
update
# Update with explicit values
$row->update({ name => 'Bob', email => 'bob@example.com' })->get;
# Update using dirty columns (after set_column/set_columns)
$row->set_column('name', 'Bob');
$row->update()->get; # Updates only dirty columns
Updates the row in the database.
- Arguments
- Returns
-
A Future that resolves to the updated row object.
update_or_insert
my $future = $row->update_or_insert({ name => 'New Name' });
$future->on_done(sub {
my $row = shift;
print "Row saved successfully";
});
Arguments: \%fallback_data? Return Value: Future resolving to $row
Provides a "save" mechanism that intelligently decides whether to perform an INSERT or an UPDATE based on the current state of the row object.
If the row is already
in_storage, it performs anupdate. Only columns marked as "dirty" (changed) will be sent to the database. If no columns are dirty, the Future resolves immediately with the current row object.If the row is not in storage, it performs a
create. The entire contents of the row's data are sent to the database.
You may optionally pass a hashref of data to this method. These values will be passed to set_column before the save operation begins, effectively merging ad-hoc changes into the row.
Upon success, the row's internal "dirty" tracking is reset, in_storage is set to true, and any database-generated values (like auto-increment IDs) are synchronised back into the object.
AUTOLOAD METHODS
Called automatically for column and relationship access
my $value = $row->column_name;
my $related = $row->relationship_name;
Handles dynamic method dispatch for columns and relationships.
The class uses AUTOLOAD to provide dynamic accessors for:
Column values (e.g.,
$row->namefor column 'name')Relationship accessors (e.g.,
$row->ordersfor 'orders' relationship)
Relationship results are cached in the object after first access.
DESTROY
# Called automatically when object is destroyed
Destructor method.
INTERNAL METHODS
These methods are for internal use and are documented for completeness.
_build_relationship_accessor
my $coderef = $row->_build_relationship_accessor($method, $rel_info);
Builds an accessor for a relationship that checks for prefetched data first, then falls back to lazy loading if needed. For has_many relationships, the ResultSet object is cached in the row.
_ensure_accessors
$row->_ensure_accessors;
Creates accessor methods for all columns in the result source.
_extract_foreign_key
Extracts foreign key mapping from a relationship condition.
_get_primary_key_info
my $pk_info = $row->_get_primary_key_info;
Returns information about the primary key(s) for this row.
- Returns
-
Hash reference with keys: -
columns: Array reference of primary key column names -count: Number of primary key columns -is_composite: Boolean indicating composite primary key
_get_source
my $source = $row->_get_source;
Returns the result source for this row, loading it lazily if needed.
SEE ALSO
DBIx::Class::Async - Asynchronous DBIx::Class interface
DBIx::Class::Row - Synchronous DBIx::Class row interface
Future - Asynchronous programming abstraction
AUTHOR
Mohammad Sajid Anwar, <mohammad.anwar at yahoo.com>
REPOSITORY
https://github.com/manwar/DBIx-Class-Async
BUGS
Please report any bugs or feature requests through the web interface at https://github.com/manwar/DBIx-Class-Async/issues. I will be notified and then you'll automatically be notified of progress on your bug as I make changes.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc DBIx::Class::Async::Row
You can also look for information at:
BUG Report
CPAN Ratings
Search MetaCPAN
LICENSE AND COPYRIGHT
Copyright (C) 2026 Mohammad Sajid Anwar.
This program is free software; you can redistribute it and / or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:
http://www.perlfoundation.org/artistic_license_2_0
Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License.By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license.
If your Modified Version has been derived from a Modified Version made by someone other than you,you are nevertheless required to ensure that your Modified Version complies with the requirements of this license.
This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.
This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement,then this Artistic License to you shall terminate on the date that such litigation is filed.
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.