NAME
PONAPI::DAO - Data Abstraction Object class
VERSION
version 0.003003
SYNOPSIS
use PONAPI::DAO;
my $dao = PONAPI::DAO->new( repository => $repository );
my ($status, $doc) = $dao->retrieve( type => $type, id => $id );
die "retrieve failed; status $status, $doc->{errors}[0]{detail}"
if $doc->{errors};
use Data::Dumper;
say Dumper($doc->{data});
# Fetch all resources of this type
$dao->retrieve_all( type => $type );
# Fetch all the relationships of $rel_type for the requested resource
$dao->retrieve_relationships(
type => $type,
id => $id,
rel_type => $rel_type,
);
# Like the above, but fetches full resources instead of just relationships
$dao->retrieve_by_relationship(
type => $type,
id => $id,
rel_type => $rel_type,
);
# Create a new resource
$dao->create(
type => $type,
data => {
type => $type,
attributes => { ... },
relationships => { ... },
}
);
# *Add* a new entry to the relationships between $type and $rel_type
$dao->create_relationsips(
type => $type,
rel_type => $rel_type,
data => [
{ ... },
]
);
# Update the attributes and/or relationships of a resource
$dao->update(
type => $type,
id => $id,
data => {
type => $type,
id => $id,
attributes => { ... },
relationships => { ... },
},
);
# Update the relationships of a given type for one resource
$dao->update_relationships(
type => $type,
id => $id,
rel_type => $rel_type,
data => $update_data,
);
# Delete a resource
$dao->delete(
type => $type,
id => $id,
);
# Delete the members from the relationship
$dao->delete_relationships(
type => $type,
id => $id,
rel_type => $rel_type,
data => [
{ ... }, ...
],
);
DESCRIPTION
Data Access Object for the JSON API. This sits in between a server and a repository.
All public DAO methods will return a 3-item list of a status, headers, and the response body; this can then be fed directly to a PSGI application:
If present, the data
key of the response will contain either a resource, or an arrayref of resources. Resources are represented as plain hashrefs, and they must include both a type
and id
; they may also contain additional keys. See http://jsonapi.org/format/#document-resource-objects for a more in-depth description.
METHODS
new
Create a new instance of PONAPI::DAO.
my $DAO = PONAPI::DAO->new(
repository => $repository,
);
Where $repository
implements the PONAPI::Repository role.
As expanded below in "Return value of update operations", the JSON API specification requires some update operations returning 200 OK
to also do a retrieve
and include it in the response. By default, PONAPI::DAO
will simply turn those 200 OK
into 202 Accepted
, avoiding the need to do the extra fetch. If needed, the full 200 responses can be re-enabled by passing respond_to_updates_with_200 => 1,
to new
.
API METHODS
With the exception of create
and retrieve_all
, the type and id arguments are mandatory for all operations.
retrieve
Retrieve a resource. Returns both the status of the request and the document to be encoded.
my ( $status, $doc ) = $dao->retrieve( type => "articles", id => 1 );
if ( $doc->{errors} ) {
die "Welp! Got some errors: ", join "\n",
map $_->{detail}, @{ $doc->{errors} };
}
say $doc->{data}{attributes}{title};
This accepts several optional values:
- fields
-
Allows fetching only specific fields of the resource:
# This will fetch the entire resource $dao->retrieve(type => "articles", id => 1); # This will only fetch the title attribute $dao->retrieve( type => "articles", id => 1, fields => { articles => [qw/ title /] }, );
Note how the fields fetched are requested per attribute type. This allows you to request specific fields in resources fetched through
include
. - include
-
Allows including related resources.
# The response will contain a top-level 'include' key with the # article's author $dao->retrieve(type => "articles", id => 1, include => [qw/ author /]); # We can combine include with C<fields> to fetch just the author's name: my $response = $dao->retrieve( id => 1, type => "articles", include => [qw/ author /], fields => { author => [qw/ name /] } );
These will show up in the document in the top-level "included" key.
- page
-
Used to provide pagination information to the underlaying repository. Each implementation may provide a different pagination strategy.
- filter
-
Entirely implementation-specific.
retrieve_all
As you might expect, this is similar to retrieve
. The returned document will contain an arrayref of resource, rather than a single resource.
Depending on the implementation, you may be able to combine this with filter
to retrieve multiple specific resources in a single request.
retrieve_all
takes all the same optional arguments as retrieve
, plus one of its own:
- sort
-
Sorting strategy for the request. Implementation-specific.
retrieve_relationships
This retrieves all relationships of $type
. Will return either an arrayref or a hashref, depending on whether the requested relationship is one-to-one or one-to-many:
# Retrieves all comments made for an article
$doc = $dao->retrieve_relationships(
type => "articles",
id => 1,
rel_type => "comments",
);
# articles-to-comments is one-to-many, so it returns an arrayref
say scalar @{ $doc->{data} };
$doc = $dao->retrieve_relationships(
type => "articles",
id => 1,
rel_type => "author",
);
# articles-to-author is one-to-one, so it returns a hashref
say $doc->{data}{id};
Takes two optional arguments, filter
and page
; both are entirely implementation specific.
retrieve_by_relationships
Like retrieve_relationships
, but fetches full resources, rather than identifier objects.
# One-to-many relationship, this returns an arrayref of resource hashrefs.
$dao->retrieve_by_relationships(
type => "articles",
id => 1,
rel_type => "comments",
);
# Same as:
$doc = $dao->retrieve_relationships(
type => "articles",
id => 1,
rel_type => "comments",
);
$comments = $dao->retrieve_all(
type => $doc->{data}{type},
filter => { id => [ map $_->{id}, @{ $doc->{data} } ] },
);
# One-to-one relationship
$doc = $dao->retrieve_relationships(
type => "articles",
id => 1,
rel_type => "author",
);
# Same as:
$doc = $dao->retrieve_relationships(
type => "articles",
id => 1,
rel_type => "author",
);
$author = $dao->retrieve(
type => $doc->{data}{type},
id => $doc->{data}{id},
);
Takes the same optional arguments as retrieve
and retrieve_all
, whichever is applicable.
delete
Deletes a resource.
$dao->delete( type => "articles", id => 1 );
May or may not return a document with a top-level meta key.
create
Creates a resource.
$dao->create(
type => "articles",
data => {
type => "articles",
attributes => { ... },
relationships => { ... },
},
);
This is one of the few methods where the id
is optional. If provided, the underlaying implementation may choose to use it, instead of generating a new idea for the created resource.
If successful, the response will include both a Location
header specifying where the new resource resides, and a document that includes the newly created resource.
update
Updates a resource. This can be used to either update the resource attributes, or its relationships; for the latter, you may want to consider using update_relationships
, create_relationships
, or delete_relationships
instead.
# Change article's title
$dao->update(
type => "articles",
id => 1,
data => {
type => "articles",
id => 1,
attributes => { title => "Updated title!" },
}
);
# Change the article's author
$dao->update(
type => "articles",
id => 1,
data => {
type => "articles",
id => 1,
relationships => {
author => { type => "people", id => 99 },
},
},
);
# Switch the tags of the article to a new set of tags
$dao->update(
type => "articles",
id => 1,
data => {
type => "articles",
id => 1,
relationships => {
tags => [
{ type => "tag", id => 4 },
{ type => "tag", id => 5 },
],
},
},
);
Missing attributes or relationships will not be modified.
Return value of update operations
update
, delete_relationships
, create_relationships
, and update_relationships
all follow the same rules for their responses.
If successful, they will return with either:
- 200 OK
-
If the update was successful and no extra data was updated, the response will include a top-level
meta
key, with a description of what was updated.Meanwhile, if the update was successful but more data than requested was updated -- Consider
updated-at
columns in a table -- then the request will return both a top-levelmeta
key, and a top-leveldata
key, containing the results of aretrieve
operation on the primary updated resource. Since this behavior can be undesirable, unlessPONAPI::DAO->new
was passedrespond_to_updates_with_200 => 1
, this sort of response is disabled, and the server will instead respond with a "202 Accepted", described below. - 202 Accepted
-
The response will include a top-level
meta
key, with a human-readable description of the success.This is used when the server accepted the operation, but hasn't yet completed it; "Completed" being purposely very ambiguous. In a SQL-based implementation, it might simply mean that the change hasn't fully replicated yet.
- 204 No Content
-
If the operation was successful and nothing beyond the requested was modified, the server may choose to send a 204 with no body, instead of a 200.
delete_relationships
Remove members from a one-to-many relationship.
# Remove two comments from the article
$dao->delete(
type => "articles",
id => 1,
rel_type => "comments',
data => [
{ type => "comment", id => 44 },
{ type => "comment", id => 89 },
],
);
See also "Return value of update operations".
update_relationships
Update the relationships of $rel_type
; this will replace all relationships of the requested type with the ones provided. Note that different semantics are used for one-to-one and one-to-many relationships:
# Replace all comments
$dao->update_relationships(
type => "articles",
id => 1,
rel_type => "comments",
# articles-to-comments is one-to-many, so it gets an arrayref
data => [ { ... }, { ... } ],
);
# Change the author of the article
$dao->update_relationships(
type => "articles",
id => 1,
rel_type => "author",
# articles-to-authors is one-to-one, so it gets a simple hashref
data => { type => "people", id => 42 },
);
# Clear the comments of an article
$dao->update_relationships(
type => "articles",
id => 1,
rel_type => "comments",
# Empty array to clear out a one-to-many
data => [],
);
# Clear the author of the relationship
$dao->update_relationships(
type => "articles",
id => 1,
rel_type => "author",
# undef to clear out one-to-one
data => undef,
);
See also "Return value of update operations".
create_relationships
Adds a new member to the specified one-to-many relationship.
# Add a new, existing comment to the article
$dao->create_relationships(
type => "articles",
id => 1,
rel_type => "comments",
data => [
{ type => "comment", id => 55 },
],
);
See also "Return value of update operations".
AUTHORS
Mickey Nasriachi <mickey@cpan.org>
Stevan Little <stevan@cpan.org>
Brian Fraser <hugmeir@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2019 by Mickey Nasriachi, Stevan Little, Brian Fraser.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.