Skip to content

vincentauger/datacite-php-sdk

Repository files navigation

DataCite PHP SDK (Unofficial)

A modern PHP SDK for the DataCite REST API, built for maintainability and clarity using Saloon for HTTP integration.

Tests DOI

Features

  • Modern PHP 8.4+ - Typed properties, readonly classes, enums, and constructor property promotion
  • Full DOI Management - Create, read, update, delete, and search DOI records
  • DataCite Events - Query and retrieve usage events for DOIs
  • Advanced Query Builder - Fluent interface for complex searches with boolean logic
  • Typed DTOs - Fully typed data transfer objects for all API responses
  • Built on Saloon - Powerful HTTP client with mocking and testing support
  • Framework Agnostic - Works with any PHP framework or standalone
  • Comprehensive Tests - Full test coverage with Pest PHP

Installation

composer require vincentauger/datacite-php-sdk

Requirements:

  • PHP 8.4+
  • Composer

Quick Start

Initialize the Client

Public API (Read-Only)

use VincentAuger\DataCiteSdk\DataCite;

// No credentials needed for public read-only access
$datacite = new DataCite();

Member API (Full Access)

use VincentAuger\DataCiteSdk\DataCite;

// Credentials required for create/update/delete operations
$datacite = new DataCite(
    username: 'your-repository-id',
    password: 'your-repository-password',
    mailto: '[email protected]'  // Optional but recommended
);

Usage Examples

Get a Single DOI

Quick way (convenience method):

// Simple one-liner for basic operations
$response = $datacite->getDOI('10.5438/4k3m-nyvg');
$doi = $response->dto();

echo $doi->data->attributes->titles[0]->title;
echo $doi->data->attributes->creators[0]->name;
echo $doi->data->attributes->publicationYear;

Advanced way (using request objects):

use VincentAuger\DataCiteSdk\Requests\DOIs\GetDOI;

// Use request objects when you need more control
$request = new GetDOI('10.5438/4k3m-nyvg');
$response = $datacite->send($request);
$doi = $response->dto();

List DOIs

use VincentAuger\DataCiteSdk\Requests\DOIs\ListDOIs;
use VincentAuger\DataCiteSdk\Enums\SortOption;

$request = (new ListDOIs)
    ->withPageSize(25)
    ->withSortDesc(SortOption::CREATED);

$response = $datacite->send($request);
$results = $response->dto();

foreach ($results->data as $doi) {
    echo "{$doi->id}: {$doi->attributes->titles[0]->title}\n";
}

echo "Total: {$results->meta->total}\n";

Search DOIs with Query Builder

use VincentAuger\DataCiteSdk\Query\QueryBuilder;
use VincentAuger\DataCiteSdk\Enums\QueryField;
use VincentAuger\DataCiteSdk\Requests\DOIs\ListDOIs;

$query = (new QueryBuilder)
    ->whereEquals(QueryField::PUBLICATION_YEAR, '2023')
    ->whereContains(QueryField::TITLES_TITLE, 'climate')
    ->whereExists(QueryField::FUNDING_REFERENCES_FUNDER_NAME);

$request = (new ListDOIs)->withQuery($query);
$response = $datacite->send($request);
$results = $response->dto();

Create a DOI

use VincentAuger\DataCiteSdk\Data\CreateDOIInput;
use VincentAuger\DataCiteSdk\Data\Metadata\Creator;
use VincentAuger\DataCiteSdk\Data\Metadata\Title;
use VincentAuger\DataCiteSdk\Data\Metadata\ResourceType;
use VincentAuger\DataCiteSdk\Enums\ResourceTypeGeneral;
use VincentAuger\DataCiteSdk\Enums\DOIEvent;

$input = new CreateDOIInput(
    doi: '10.5438/example-doi',
    url: 'https://example.com/dataset',
    titles: [new Title('My Dataset Title')],
    creators: [new Creator(name: 'Smith, John')],
    publisher: new PublisherData(name: 'Example Publisher'),
    publicationYear: 2025,
    types: new ResourceType(
        resourceTypeGeneral: ResourceTypeGeneral::DATASET,
        resourceType: 'Dataset'
    ),
    event: DOIEvent::PUBLISH  // Optional: publish, register, or hide
);

// Quick way (convenience method)
$response = $datacite->createDOI($input);
$doi = $response->dto();

// Or use request object for more control
$request = new CreateDOI($input);
$response = $datacite->send($request);

Update a DOI

use VincentAuger\DataCiteSdk\Data\UpdateDOIInput;

$input = new UpdateDOIInput(
    doi: '10.5438/example-doi',
    titles: [new Title('Updated Title')],
    // ... other updated fields
);

// Quick way (convenience method)
$response = $datacite->updateDOI('10.5438/example-doi', $input);

// Or use request object
$request = new UpdateDOI('10.5438/example-doi', $input);
$response = $datacite->send($request);

Delete a DOI

// Only works for draft DOIs
$response = $datacite->deleteDOI('10.5438/draft-doi');

// Or use request object
$request = new DeleteDOI('10.5438/draft-doi');
$response = $datacite->send($request);

Get DOI Activities

// Quick way (convenience method)
$response = $datacite->getDOIActivities('10.5438/4k3m-nyvg');
$activities = $response->dto();

foreach ($activities->data as $activity) {
    echo "{$activity->attributes->action} at {$activity->attributes->createdAt}\n";
}

// Or use request object
use VincentAuger\DataCiteSdk\Requests\DOIs\GetDOIActivities;

$request = new GetDOIActivities('10.5438/4k3m-nyvg');
$response = $datacite->send($request);

List Events

use VincentAuger\DataCiteSdk\Requests\Events\ListEvents;
use VincentAuger\DataCiteSdk\Enums\EventSortOption;
use VincentAuger\DataCiteSdk\Enums\EventSource;

$request = (new ListEvents)
    ->withDoiId('10.5438/4k3m-nyvg')
    ->withSource(EventSource::CROSSREF)
    ->withYearMonth(2023, 6)
    ->withSortDesc(EventSortOption::TOTAL)
    ->withPageSize(25);

$response = $datacite->send($request);
$events = $response->dto();

foreach ($events->data as $event) {
    echo "{$event->id}: {$event->attributes->total} occurrences\n";
}

Get a Single Event

// Quick way (convenience method)
$response = $datacite->getEvent('event-id-here');
$event = $response->dto();

// Or use request object
use VincentAuger\DataCiteSdk\Requests\Events\GetEvent;

$request = new GetEvent('event-id-here');
$response = $datacite->send($request);

Convenience Methods

The SDK provides convenient shorthand methods for common operations:

// System
$datacite->heartbeat(): bool

// DOIs
$datacite->getDOI(string $doi): Response
$datacite->getDOIActivities(string $doi): Response
$datacite->createDOI(CreateDOIInput $input): Response
$datacite->updateDOI(string $doi, UpdateDOIInput $input): Response
$datacite->deleteDOI(string $doi): Response

// Events
$datacite->getEvent(string $eventId): Response

When to use convenience methods:

  • ✅ Quick, simple operations
  • ✅ Getting started with the SDK
  • ✅ Straightforward single-resource retrieval

When to use request objects:

  • ✅ Complex queries with filters
  • ✅ Pagination and sorting
  • ✅ Using the QueryBuilder
  • ✅ Need to customize request behavior

Advanced Features

Query Builder

Build complex search queries with a fluent interface. See docs/QueryBuilder.md for full documentation.

use VincentAuger\DataCiteSdk\Query\QueryBuilder;
use VincentAuger\DataCiteSdk\Enums\QueryField;

$query = (new QueryBuilder)
    ->whereEquals(QueryField::PUBLICATION_YEAR, '2023')
    ->whereOr(function (QueryBuilder $builder) {
        $builder
            ->whereContains(QueryField::TITLES_TITLE, 'climate')
            ->whereContains(QueryField::TITLES_TITLE, 'weather');
    })
    ->whereExists(QueryField::CREATORS_NAME_IDENTIFIER);

Available Methods:

  • whereEquals(), whereNotEquals() - Exact matching
  • whereContains() - Case-insensitive contains search
  • whereStartsWith(), whereEndsWith() - Wildcard prefix/suffix
  • whereExists(), whereNotExists() - Field presence checks
  • whereIn(), whereNotIn() - Multiple value matching
  • whereAnd(), whereOr() - Boolean logic grouping
  • whereExact() - Quoted exact match for phrases

Sorting and Filtering

Apply sorting, filtering, and pagination to list requests. See docs/sorting-and-filtering.md for examples.

use VincentAuger\DataCiteSdk\Enums\SortOption;

$request = (new ListDOIs)
    ->withProviderId('cern')
    ->withClientId('cern.zenodo')
    ->withCreated('2023-01-01,2023-12-31')  // Date range
    ->withHasPerson(true)
    ->withHasFundingReference(true)
    ->withSortDesc(SortOption::CREATED)
    ->withPageSize(50)
    ->withPage(2)
    ->withAffiliation()  // Include additional data
    ->withPublisher();

DOI Sort Options:

  • RELEVANCE - Search relevance (default)
  • NAME - DOI name
  • CREATED - Creation date
  • UPDATED - Last updated
  • PUBLISHED - Publication date
  • REGISTERED - Registration date

Event Sort Options:

  • RELEVANCE - Search relevance
  • OBJ_ID - Object ID
  • TOTAL - Total occurrences
  • CREATED - Creation date
  • UPDATED - Last updated

Working with DTOs and Type Hints

All API responses return fully typed Data Transfer Objects (DTOs) for type safety and IDE autocomplete. However, due to how Saloon creates DTOs, you may lose type information when calling ->dto().

Getting Proper Type Hints

Option 1: Use PHPDoc annotation (Recommended)

use VincentAuger\DataCiteSdk\Data\DOIData;

/** @var DOIData $doi */
$doi = $response->dto();

// Now you get full IDE autocomplete and type checking
$title = $doi->data->attributes->titles[0]->title;

Option 2: Use createDtoFromResponse() directly

$request = new GetDOI('10.5438/4k3m-nyvg');
$response = $datacite->send($request);

// This method returns the correct type automatically
$doi = $request->createDtoFromResponse($response);

Available DTOs

Request Type DTO Class Description
GetDOI DOIData Single DOI response
ListDOIs ListDOIData Paginated DOI list
CreateDOI DOIData Created DOI response
UpdateDOI DOIData Updated DOI response
GetDOIActivities DOIActivitiesData DOI activity log
GetEvent EventData Single event response
ListEvents ListEventData Paginated event list

Example with proper typing:

use VincentAuger\DataCiteSdk\Data\DOIData;
use VincentAuger\DataCiteSdk\Data\ListDOIData;

// Single DOI
/** @var DOIData $doi */
$doi = $datacite->getDOI('10.5438/0012')->dto();

// List of DOIs
/** @var ListDOIData $results */
$results = $datacite->send(new ListDOIs)->dto();

foreach ($results->data as $doiItem) {
    // Full autocomplete available
    echo $doiItem->attributes->titles[0]->title;
}

Configuration

Client Options

Parameter Type Default Description
baseUrl string https://api.datacite.org DataCite API base URL
username string|null null Repository ID (required for member API)
password string|null null Repository password (required for member API)
mailto string|null null Email for User-Agent header (optional)

Authentication

The SDK supports two access levels:

  1. Public API (default) - Read-only access to DOI metadata and events (no credentials needed)
  2. Member API - Full access including create/update/delete (requires credentials)
// Public API - read-only
$datacite = new DataCite();

// Member API - full access
$datacite = new DataCite(
    apiVersion: ApiVersion::MEMBER,
    username: 'your-repository-id',
    password: 'your-repository-password'
);

Endpoints that modify data (POST, PUT, DELETE) use the RequiresMemberAuth trait and will throw a RuntimeException if you attempt these operations without proper authentication.


API Coverage

System

  • GET /heartbeat - Check API status

DOIs

  • GET /dois - List DOIs with advanced filtering
  • GET /dois/{id} - Get a single DOI
  • POST /dois - Create a new DOI (member only)
  • PUT /dois/{id} - Update a DOI (member only)
  • DELETE /dois/{id} - Delete a draft DOI (member only)
  • GET /dois/{id}/activities - Get DOI activity log

Events

  • GET /events - List events with filtering
  • GET /events/{id} - Get a single event

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for:

  • Development environment setup
  • Coding standards and guidelines
  • Testing requirements
  • Pull request process

Documentation


License

MIT License — see LICENSE.md for details.


Disclaimer

This SDK is unofficial and not affiliated with DataCite.

  • Use at your own risk
  • Respect DataCite API rate limits and terms of service
  • Ensure proper authentication and security for member API credentials
  • Always test in a non-production environment first

For official DataCite documentation, visit support.datacite.org/docs.

About

A modern PHP SDK for the DataCite REST API platform

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Languages