A modern PHP SDK for the DataCite REST API, built for maintainability and clarity using Saloon for HTTP integration.
- ✅ 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
composer require vincentauger/datacite-php-sdkRequirements:
- PHP 8.4+
- Composer
use VincentAuger\DataCiteSdk\DataCite;
// No credentials needed for public read-only access
$datacite = new DataCite();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
);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();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";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();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);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);// 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);// 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);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";
}// 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);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): ResponseWhen 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
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 matchingwhereContains()- Case-insensitive contains searchwhereStartsWith(),whereEndsWith()- Wildcard prefix/suffixwhereExists(),whereNotExists()- Field presence checkswhereIn(),whereNotIn()- Multiple value matchingwhereAnd(),whereOr()- Boolean logic groupingwhereExact()- Quoted exact match for phrases
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 nameCREATED- Creation dateUPDATED- Last updatedPUBLISHED- Publication dateREGISTERED- Registration date
Event Sort Options:
RELEVANCE- Search relevanceOBJ_ID- Object IDTOTAL- Total occurrencesCREATED- Creation dateUPDATED- Last updated
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().
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);| 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;
}| 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) |
The SDK supports two access levels:
- Public API (default) - Read-only access to DOI metadata and events (no credentials needed)
- 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.
- ✅
GET /heartbeat- Check API status
- ✅
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
- ✅
GET /events- List events with filtering - ✅
GET /events/{id}- Get a single event
Contributions are welcome! Please see CONTRIBUTING.md for:
- Development environment setup
- Coding standards and guidelines
- Testing requirements
- Pull request process
- QueryBuilder.md - Complete query builder reference with examples
- sorting-and-filtering.md - Sorting, filtering, and pagination examples
MIT License — see LICENSE.md for details.
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.