NAME
Devel::Ladybug::ExtID - Define inter-object relationships
SYNOPSIS
use Devel::Ladybug qw| :all |;
create "YourApp::ChildObject" => {
parentObjectId => Devel::Ladybug::ExtID->assert(
"YourApp::ParentObject"
)
};
DESCRIPTION
Ladybug's ExtID assertions are a simple and powerful way to define cross-object, cross-table relationships. An ExtID is a column/instance variable which points at the ID of another object.
Extends Devel::Ladybug::Str.
RELATIONSHIP DIRECTION
This is important.
Always use parent id in a child object, rather than child id in a parent object-- or else cascading operations will probably eat your data in unexpected and unwanted ways.
PUBLIC CLASS METHODS
$class->assert([$query], @rules)
ExtID assertions are like pointers defining relationships with other classes, or within a class to define parent/child hierarchy.
Attributes asserted as ExtID must match an id in the specified class's database table.
If using a backing store which supports it, ExtID assertions also enforce database-level foreign key constraints.
EXAMPLES
Self-Referencing Table
Create a class of object which refers to itself by parent ID:
#
# File: YourApp/ParentObject.pm
#
use strict;
use warnings;
use Devel::Ladybug qw| :all |;
create "YourApp::ParentObject" => {
#
# Folders can go in other folders:
#
parentId => Devel::Ladybug::ExtID->assert(
"YourApp::ParentObject",
subtype(
optional => true
)
),
# ...
};
Meanwhile, in caller, create a top-level object and a child object which refers to it:
#
# File: test-selfref.pl
#
use strict;
use warnings;
use YourApp::ParentObject;
my $parent = YourApp::ParentObject->new(
name => "Hello Parent"
);
$parent->save;
my $child = YourApp::ChildObject"->new(
name => "Hello Child",
parentId => $parent->id,
);
$child->save;
Externally Referencing Table
A document class, building on the above example. Documents refer to their parent by ID:
#
# File: YourApp/ChildObject.pm
#
use strict;
use warnings;
use Devel::Ladybug qw| :all |;
use YourApp::ParentObject; # You must "use" any external classes
create "YourApp::ChildObject" => {
parentId => Devel::Ladybug::ExtID->assert(
"YourApp::ParentObject"
)
};
Meanwhile, in caller, create a node which refers to its foreign class parent:
#
# File: test-extref.pl
#
use strict;
use warnings;
use YourApp::ChildObject;
my $parent = YourApp::ParentObject->loadByName("Hello Parent");
my $child = YourApp::ChildObject->new(
name => "Hello Again",
parentId => $parent->id
);
$child->save;
One to Many
Wrap ExtID assertions inside a Devel::Ladybug::Array assertion to create a one-to-many relationship.
#
# File: YourApp/OneToManyExample.pm
#
# ...
create "YourApp::OneToManyExample" => {
parentIds => Devel::Ladybug::Array->assert
Devel::Ladybug::ExtID->assert(
"YourApp::ParentObject"
)
),
# ...
};
Many to One / One to One
ExtID's default behavior is to permit many-to-one relationships (that is, multiple children may refer to the same parent by ID). To restrict this to a one-to-one relationship, include a unique
subtype argument.
#
# File: YourApp/OneToOneExample.pm
#
# ...
create "YourApp::OneToOneExample" => {
parentId => Devel::Ladybug::ExtID->assert(
"YourApp::ParentObject",
subtype(
unique => true
)
),
# ...
};
Dynamic Allowed Values
If a string is specified as a second argument to ExtID, it will be used as a SQL query, which selects a subset of Ids used as allowed values at runtime.
This is entirely an application-level constraint, and is not enforced by the database when manually inserting or updating rows. Careful!
create "YourApp::PickyExample" => {
userId => Devel::Ladybug::ExtID->assert(
"YourApp::Example::User",
"select id from example_user where foo = 1"
),
# ...
};
BUGS AND LIMITATIONS
Same-table non-GUID keys
Self-referential tables whose ID is not of type Devel::Ladybug::ID should assert an appropriate column type. This is needed because at class creation time, Ladybug does not yet know anything about the ID of the class being created. This workaround is only needed for self-referential tables which have overridden their id
column.
You do not need to do this for externally referencing tables, since Ladybug will already know which column type to use. You do not need to do this unless the id
assertion was overridden.
create "YourApp::FunkySelfRef" => {
id => Devel::Ladybug::Serial->assert(),
parentId => Devel::Ladybug::ExtID->assert(
"YourApp::FunkySelfRef",
subtype(
columnType => "INTEGER" # <-- eg
)
),
};
SEE ALSO
This file is part of Devel::Ladybug.