cardtechie/tradingcardapi-sdk-php

Official PHP SDK for Trading Card API - comprehensive tools for accessing trading card data, players, sets, and market information with enhanced error handling and Laravel integration

Maintainers

Package info

github.com/cardtechie/tradingcardapi-sdk-php

Documentation

pkg:composer/cardtechie/tradingcardapi-sdk-php

Transparency log

Statistics

Installs: 175

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 15

0.2.26 2026-06-30 01:20 UTC

README

Latest Version on Packagist GitHub Tests Total Downloads License: MIT

A modern PHP SDK for integrating with the Trading Card API. This Laravel package provides a clean, type-safe interface for accessing trading card data including cards, sets, players, teams, and more.

โœจ Features

  • ๐Ÿ”ง Laravel Integration - Built specifically for Laravel applications
  • ๐Ÿ›ก๏ธ Type Safety - Full PHPStan Level 4 compliance with strict typing
  • ๐Ÿงช Well Tested - Comprehensive test suite (Pest) covering the SDK's public surface
  • ๐Ÿ“ฆ Easy Installation - Simple Composer installation and configuration
  • ๐Ÿ”„ OAuth2 Authentication - Automatic token management and renewal
  • ๐Ÿšจ Enhanced Error Handling - Specific exception classes for different error types
  • ๐Ÿ“– Rich Documentation - Clear examples and comprehensive API coverage
  • โšก Built on Guzzle - HTTP transport via the battle-tested GuzzleHTTP client

๐Ÿ“‹ Requirements

  • PHP 8.2 or higher
  • Laravel 10.0 or higher
  • GuzzleHTTP 7.5 or higher

๐Ÿš€ Installation

Install the package via Composer:

composer require cardtechie/tradingcardapi-sdk-php

Publish the configuration file:

php artisan vendor:publish --tag="tradingcardapi-config"

Add your API credentials to your .env file:

TRADINGCARDAPI_URL=https://api.tradingcardapi.com
TRADINGCARDAPI_CLIENT_ID=your_client_id
TRADINGCARDAPI_CLIENT_SECRET=your_client_secret
TRADINGCARDAPI_SSL_VERIFY=true

# HTTP timeouts (seconds) โ€” defaults shown; 0 disables a timeout
TRADINGCARDAPI_TIMEOUT=10
TRADINGCARDAPI_CONNECT_TIMEOUT=5

# Opt-in retry/backoff for 429 and 5xx responses (and connection errors)
TRADINGCARDAPI_RETRY_ENABLED=false
TRADINGCARDAPI_RETRY_MAX_ATTEMPTS=3
TRADINGCARDAPI_RETRY_BASE_DELAY_MS=1000

๐ŸŽฏ Quick Start

Using the Facade

use CardTechie\TradingCardApiSdk\Facades\TradingCardApiSdk;

// Get a specific card
$card = TradingCardApiSdk::card()->get('card-id');

// Search for cards
$cards = TradingCardApiSdk::card()->list(['name' => 'Pikachu']);

// Get player information
$player = TradingCardApiSdk::player()->get('player-id');

// Get paginated list of players
$players = TradingCardApiSdk::player()->list(['limit' => 25, 'page' => 1]);

// Search for players (returns Collection)
$players = TradingCardApiSdk::player()->all(['full_name' => 'Michael Jordan']);

Using the Helper Function

// Get a set with related data
$set = tradingcardapi()->set()->get('set-id', ['include' => 'cards,genre']);

// Create a new team
$team = tradingcardapi()->team()->create([
    'name' => 'New Team',
    'location' => 'City Name'
]);

Error Handling

The SDK provides comprehensive error handling with specific exception classes:

use CardTechie\TradingCardApiSdk\Exceptions\{
    CardNotFoundException,
    ConflictException,
    ValidationException,
    RateLimitException,
    AuthenticationException
};

try {
    $card = TradingCardApiSdk::card()->get('invalid-id');
} catch (CardNotFoundException $e) {
    // Handle missing card
    echo "Card not found: " . $e->getMessage();
} catch (ConflictException $e) {
    // Handle duplicate resource (HTTP 409)
    echo "Conflict: " . $e->getMessage();
} catch (ValidationException $e) {
    // Handle validation errors
    foreach ($e->getValidationErrors() as $field => $errors) {
        echo "Field $field: " . implode(', ', $errors);
    }
} catch (RateLimitException $e) {
    // Handle rate limiting
    echo "Rate limited. Retry after: " . $e->getRetryAfter() . " seconds";
}

Direct Class Usage

use CardTechie\TradingCardApiSdk\TradingCardApi;

$api = new TradingCardApi();
$genres = $api->genre()->list();

Meta and links

Top-level JSON:API meta and links are document-scoped โ€” they describe the whole response (pagination totals, self/next/prev links). The SDK attaches them to the main parsed object only, and you read them with getMeta() / getLinks():

$cards = TradingCardApi::card()->list();

$cards->getMeta()->total;        // e.g. 1500
$cards->getLinks()->next;        // e.g. https://api.example.com/cards?page=2

Included relationship models do not carry the top-level meta/links โ€” calling getMeta() / getLinks() on an included model returns an empty stdClass by design. Document-scoped links describe the main collection, so copying them onto an included resource (e.g. a sideloaded Player) would be misleading.

$player = $cards->getRelationships()['players'][0];
(array) $player->getMeta();       // []  โ€” empty by design

๐Ÿ‘ฅ Working with Players

The Player resource provides comprehensive CRUD operations and relationship management:

Basic Operations

use CardTechie\TradingCardApiSdk\Facades\TradingCardApiSdk;

// Get a specific player
$player = TradingCardApiSdk::player()->get('player-id');

// Create a new player
$player = TradingCardApiSdk::player()->create([
    'first_name' => 'Michael',
    'last_name' => 'Jordan'
]);

// Update player information
$player = TradingCardApiSdk::player()->update('player-id', [
    'first_name' => 'Michael Jeffrey',
    'last_name' => 'Jordan'
]);

// Delete a player
TradingCardApiSdk::player()->delete('player-id');

Listing and Searching

// Get paginated list of players
$players = TradingCardApiSdk::player()->list([
    'limit' => 50,
    'page' => 1,
    'sort' => 'last_name'
]);

// Search for players (returns Collection)
$players = TradingCardApiSdk::player()->all([
    'full_name' => 'Michael Jordan',
    'parent_id' => null  // Only parent players, not aliases
]);

// Find players by partial name
$players = TradingCardApiSdk::player()->all([
    'first_name' => 'Michael'
]);

Player Relationships

// Working with player relationships
$player = TradingCardApiSdk::player()->get('player-id');

// Get parent player (if this is an alias)
$parent = $player->getParent();

// Get all aliases of this player
$aliases = $player->getAliases();

// Get teams this player has been associated with
$teams = $player->getTeams();

// Get all player-team relationships
$playerteams = $player->getPlayerteams();

// Check if player is an alias
if ($player->isAlias()) {
    echo "This is an alias of: " . $player->getParent()->full_name;
}

// Check if player has aliases
if ($player->hasAliases()) {
    echo "This player has " . $player->getAliases()->count() . " aliases";
}

Creating Player Hierarchies

// Create a parent player
$parent = TradingCardApiSdk::player()->create([
    'first_name' => 'Michael',
    'last_name' => 'Jordan'
]);

// Create an alias player with parent relationship
$alias = TradingCardApiSdk::player()->create(
    ['first_name' => 'Mike', 'last_name' => 'Jordan'],
    ['parent' => ['data' => ['type' => 'players', 'id' => $parent->id]]]
);

Working with Deleted Players

// List deleted players
$deletedPlayers = TradingCardApiSdk::player()->listDeleted();

// Get a specific deleted player
$deletedPlayer = TradingCardApiSdk::player()->deleted('player-id');

Player Model Attributes

$player = TradingCardApiSdk::player()->get('player-id');

// Access player data
echo $player->first_name;        // "Michael"
echo $player->last_name;         // "Jordan"
echo $player->full_name;         // "Michael Jordan" (computed attribute)
echo $player->last_name_first;   // "Jordan, Michael" (computed attribute)

// Check relationships
echo $player->parent_id;         // UUID of parent player (if alias)

๐Ÿ“š Available Resources

The SDK provides access to the following Trading Card API resources:

Resource Description Methods
Cards Individual trading cards get(), create(), update(), delete()
Sets Card sets and collections get(), list(), create(), update(), delete(), checklist($id), workflow($id), addMissingCards($id), addChecklist($request, $id)
Players Player information get(), list(), all(), getList() (deprecated โ€” use all()), create(), update(), delete(), listDeleted(), deleted($id)
Teams Team data get(), list(), all(), getList() (deprecated โ€” use all()), create(), update(), delete(), listDeleted(), deleted($id)
Genres Card categories/types get(), list(), create(), update(), delete(), listDeleted(), deleted($id)
Brands Trading card brands get(), list(), create(), update(), delete()
Manufacturers Trading card manufacturers get(), list(), create(), update(), delete()
Years Trading card years get(), list(), create(), update(), delete()
ObjectAttributes Object attributes get(), list(), create(), update(), delete()
SetSources Set data sources get(), list(), create(), update(), delete(), forSet($setId)
Stats Entity statistics and analytics get($type), getCounts(), getSnapshots(), getGrowth()
Attributes Card attributes get(), list(), all(), create(), update(), delete()
CardImages Card image upload and management list(), get($id), upload($file, $cardId, $imageType), update($id, $attributes), delete($id), getDownloadUrl($id, $size)
Internal\Workflow (internal only) Set workflow management and bulk operations actionableSets(), updateSetTodo($todoId, $attributes), bulkInitializeWorkflow(), getBulkInitializeStatus($jobId), getSetTodos($setId), getReviewQueue($step?, $params?), flagForReview($todoId, $reason), resolveReview($todoId, $notes?)
Internal\AuditLog (internal only) Audit log tracking and creation getAuditLogs($params?), createAuditEvent($attributes?)

Stats Resource

The Stats resource provides analytics and tracking capabilities for entity counts:

// Get current counts for all entity types
$counts = $api->stats()->getCounts();

// Access counts for a specific entity type
$setsCount = $counts->getByEntityType('sets');
echo $setsCount->total;      // Total count
echo $setsCount->published;  // Published count
echo $setsCount->draft;      // Draft count
echo $setsCount->archived;   // Archived count

// Get growth metrics (default: 7 days)
$growth = $api->stats()->getGrowth();
// Or specify a period: '7d', '30d', '90d', 'week', 'month'
$growth = $api->stats()->getGrowth('30d');

$setsGrowth = $growth->getByEntityType('sets');
echo $setsGrowth->current;          // Current count
echo $setsGrowth->previous;         // Previous period count
echo $setsGrowth->change;           // Absolute change
echo $setsGrowth->percentageChange; // Percentage change

// Get historical snapshots
$snapshots = $api->stats()->getSnapshots();

// With filters
$snapshots = $api->stats()->getSnapshots([
    'entity_type' => 'sets',
    'from' => '2024-11-01',
    'to' => '2024-11-30',
]);

foreach ($snapshots->snapshots as $snapshot) {
    echo $snapshot->date;        // Snapshot date
    echo $snapshot->entityType;  // Entity type
    echo $snapshot->total;       // Total at that point
}

SetSource Resource

The SetSource resource manages data sources for trading card sets (checklists, metadata, images):

// Get all sources for a specific set
$sources = $api->setSource()->forSet('set-id');

// Get a specific source
$source = $api->setSource()->get('source-id');

// Create a new set source
$source = $api->setSource()->create([
    'set_id' => 'set-uuid',
    'source_url' => 'https://example.com/checklist',
    'source_name' => 'Example Source',
    'source_type' => 'checklist',  // checklist, metadata, or images
]);

// Update a source
$source = $api->setSource()->update('source-id', [
    'source_url' => 'https://example.com/updated-checklist',
    'verified_at' => '2024-01-15T10:30:00Z',
]);

// Delete a source
$api->setSource()->delete('source-id');

// Include sources when fetching a set
$set = $api->set()->get('set-id', ['include' => 'sources']);

// Returns an Illuminate\Support\Collection of SetSource models
$sources = $set->sources();

if ($sources->isEmpty()) {
    echo 'No sources found for this set.';
} else {
    $firstSource = $sources->first();
    echo $firstSource->source_name;
}

Internal Namespace

Internal use only โ€” not part of the public API contract; may change without semver guarantees.

The Internal\ namespace is intended for internal callers (admin tooling, tradingcardapi-mcp, tradingcardapi-tools). Credentials must carry the internal OAuth scope; calls will fail with a 403 if this scope is absent.

Access the internal client via $api->internal():

$internal = $api->internal();

// workflow and audit-log resources are now under internal()
$actionable = $internal->workflow()->actionableSets();
$logs       = $internal->auditLog()->getAuditLogs();

Internal\Workflow Resource

The internal Workflow resource manages set workflow steps (todos) and bulk initialization via /internal/* routes:

$workflow = $api->internal()->workflow();

// Get sets that have actionable workflow steps
// Returns a typed ActionableSetsResponse (->sets is an array of ActionableSet)
$actionable = $workflow->actionableSets();
foreach ($actionable->sets as $set) {
    echo $set->attributes->name;
}

// Filter actionable sets
$actionable = $workflow->actionableSets(['filter[sport]' => 'baseball']);

// Get workflow status for a specific set (via Set resource โ€” still public)
$workflowStatus = $api->set()->workflow('set-id');

// Update a workflow step (set-todo) status
$result = $workflow->updateSetTodo('todo-id', [
    'status' => 'completed',
]);

// Bulk initialize workflow todos for all existing sets
$job = $workflow->bulkInitializeWorkflow();
echo $job->data->job_id;   // Job ID to poll for status
echo $job->data->status;   // 'queued'

// Initialize workflow todos for specific sets only
$job = $workflow->bulkInitializeWorkflow([
    'set_ids' => ['set-id-1', 'set-id-2'],
]);

// Poll bulk initialization job status
$status = $workflow->getBulkInitializeStatus($job->data->job_id);
echo $status->data->status;     // 'queued', 'processing', or 'completed'
echo $status->data->processed;  // Sets processed so far
echo $status->data->total;      // Total sets to process

// Get workflow todos for a specific set
$result = $workflow->getSetTodos('set-id');
foreach ($result->todos as $todo) {
    echo $todo->step;    // e.g. 'discover_sources'
    echo $todo->status;  // e.g. 'completed'
}

// Get all sets blocked for human review
$reviewQueue = $workflow->getReviewQueue();

// Filter review queue by workflow step
$parseReview = $workflow->getReviewQueue('parse');

// Flag a workflow step for human review
$workflow->flagForReview('todo-id', 'Data quality issue detected');

// Resolve a review (resets to pending)
$workflow->resolveReview('todo-id');
$workflow->resolveReview('todo-id', 'Verified card data is correct');

// Use WorkflowStatus and WorkflowStep enums instead of magic strings
$workflow->updateSetTodo('todo-id', [
    'status' => \CardTechie\TradingCardApiSdk\Enums\WorkflowStatus::COMPLETED->value,
]);

Internal\AuditLog Resource

The internal AuditLog resource provides access to audit logging endpoints via /internal/* routes:

$auditLog = $api->internal()->auditLog();

// Get audit logs with pagination
$logs = $auditLog->getAuditLogs();

// Filter audit logs
$logs = $auditLog->getAuditLogs([
    'auditable_type' => 'Set',
    'auditable_id' => 'set-uuid',
    'agent_id' => 'agent-uuid',
    'event_type' => 'created',
    'start_date' => '2026-01-01',
    'end_date' => '2026-04-13',
    'per_page' => 25,
    'page' => 1,
]);

// Create an audit event
$event = $auditLog->createAuditEvent([
    'auditable_type' => 'Set',
    'auditable_id' => 'set-uuid',
    'event_type' => 'manual_review',
    'description' => 'Manual review completed',
]);

CardImage Resource

The CardImage resource handles card image uploads and management:

// Upload a card image
$image = $api->cardImage()->upload(
    $request->file('image'),  // UploadedFile or file path
    'card-id',
    'front'  // 'front' or 'back'
);

// Upload with additional attributes
$image = $api->cardImage()->upload(
    '/path/to/image.jpg',
    'card-id',
    'front',
    ['is_primary' => true]
);

// Get a card image
$image = $api->cardImage()->get('image-id');

// Get download URL
$url = $api->cardImage()->getDownloadUrl('image-id');           // original size
$url = $api->cardImage()->getDownloadUrl('image-id', 'small');  // small variant

// Update image metadata
$image = $api->cardImage()->update('image-id', ['is_primary' => true]);

// List card images with filtering
$images = $api->cardImage()->list(['filter[card_id]' => 'card-id']);

// Delete a card image
$api->cardImage()->delete('image-id');

๐Ÿ”ง Configuration

The configuration file (config/tradingcardapi.php) supports:

return [
    'url' => env('TRADINGCARDAPI_URL', ''),
    'ssl_verify' => (bool) env('TRADINGCARDAPI_SSL_VERIFY', true),
    'client_id' => env('TRADINGCARDAPI_CLIENT_ID', ''),
    'client_secret' => env('TRADINGCARDAPI_CLIENT_SECRET', ''),
    'scope' => env('TRADINGCARDAPI_SCOPE', 'read:published'),
    'timeout' => (float) env('TRADINGCARDAPI_TIMEOUT', 10),
    'connect_timeout' => (float) env('TRADINGCARDAPI_CONNECT_TIMEOUT', 5),
    'retry' => [
        'enabled' => (bool) env('TRADINGCARDAPI_RETRY_ENABLED', false),
        'max_attempts' => (int) env('TRADINGCARDAPI_RETRY_MAX_ATTEMPTS', 3),
        'base_delay' => (int) env('TRADINGCARDAPI_RETRY_BASE_DELAY_MS', 1000),
    ],
];

HTTP Timeouts

By default the SDK applies a 10-second request timeout and a 5-second connect timeout to every request. Guzzle ships with no timeout at all, so without these a hung API would block the calling PHP-FPM worker indefinitely.

Env var Default Meaning
TRADINGCARDAPI_TIMEOUT 10 Total seconds to wait for a response. 0 disables.
TRADINGCARDAPI_CONNECT_TIMEOUT 5 Seconds to wait while establishing the connection. 0 disables.

A connect timeout makes NetworkException::connectionTimeout reachable โ€” it could never fire previously because no timeout was ever set.

Retry / Backoff

Retrying transient failures is opt-in (disabled by default to preserve existing behavior). When enabled, the SDK retries 429 and 5xx responses and connection errors with exponential backoff (base_delay * 2^(attempt-1) ms). If a 429 carries a numeric Retry-After header, that value is honored in preference to the computed backoff.

Env var Default Meaning
TRADINGCARDAPI_RETRY_ENABLED false Enable automatic retries.
TRADINGCARDAPI_RETRY_MAX_ATTEMPTS 3 Max retries after the initial request.
TRADINGCARDAPI_RETRY_BASE_DELAY_MS 1000 Base backoff delay in milliseconds.

OAuth Scopes

Configure the OAuth scopes to request when authenticating. Available scopes:

  • read:published (default) - Access published content only
  • read:draft - Access published and draft content
  • read:all-status - Access all content regardless of status
  • write - Create and update resources
  • delete - Delete resources

Examples

Read-only access to published content:

TRADINGCARDAPI_SCOPE="read:published"

Admin access with full permissions:

TRADINGCARDAPI_SCOPE="read:all-status write delete"

Content management (no delete):

TRADINGCARDAPI_SCOPE="read:draft write"

Multiple scopes should be separated by spaces. If not specified, the default scope (read:published) is used.

๐Ÿงช Development & Testing

This project uses modern PHP development tools and practices:

Prerequisites

  • Docker and Docker Compose
  • Make (optional, for convenience commands)

Getting Started

# Clone the repository
git clone https://github.com/cardtechie/tradingcardapi-sdk-php.git
cd tradingcardapi-sdk-php

# Start development environment
make up

# Install dependencies
make install

# Run tests
make test

# Run code quality checks
make check

Available Commands

make test              # Run test suite
make analyse           # Run PHPStan static analysis
make format            # Format code with Laravel Pint
make check             # Run all quality checks
make quality           # Run comprehensive quality checks with coverage
make ci                # Run CI pipeline locally

# Release management commands
make version           # Show current version
make changelog-update  # Update changelog for current version
make release-notes-preview  # Generate release notes preview

Code Quality Standards

This project maintains high code quality standards:

  • โœ… PHPStan Level 4 - Strict static analysis
  • โœ… PSR-12 - Code style compliance via Laravel Pint
  • โœ… Comprehensive Test Suite - Extensive coverage using Pest (run make test-coverage for a local report)
  • โœ… Automated CI/CD - Quality checks on all PRs

๐Ÿ“– Documentation

Upgrade Notes (0.3.0)

The 0.3.0 Resource-layer standardization introduces two consumer-visible changes:

  • getList() is deprecated in favor of all() on the Player, Team, and Playerteam resources. getList() still works (it delegates to all() with identical behavior) but is marked @deprecated, so static analysis (e.g. PHPStan method.deprecated) will flag remaining call sites. Migrate ->getList(...) to ->all(...).
  • RateLimitException::__construct positional slots were realigned with the base TradingCardApiException: $httpStatusCode (default 429) is now positional slot 6 and $context slot 7. Construct RateLimitException with named arguments to be safe against the slot change โ€” see the Error Handling Guide.

๐Ÿค Contributing

We welcome contributions! Open an issue or start a thread in GitHub Discussions before submitting a pull request. See CONTRIBUTING.md for the full contributor guide โ€” development setup, coding standards, testing requirements, the changelog-fragment and pull-request process, and issue reporting.

Development Workflow

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run quality checks: make check
  5. Submit a pull request

๐Ÿ› Bug Reports & Feature Requests

Please use the GitHub Issues to report bugs or request features.

๐Ÿ”’ Security

Please review our Security Policy for reporting security vulnerabilities.

๐Ÿš€ Release Process

This project uses a sophisticated, automated release management system adapted from the main Trading Card API repository.

Version Management

The SDK uses intelligent, branch-aware semantic versioning:

  • Production releases (1.2.3) - Created from main branch
  • Beta releases (1.3.0.beta-5) - Created from develop branch
  • Release candidates (1.3.0.rc-2) - Created from release/* branches
  • Development versions (1.2.3-alpha.4) - Feature branches

Development Commands

# Check current version
make version

# Preview version for different branches
make version-preview --branch=main
make version-preview --branch=develop

# Update changelog for current version
make changelog-update

# Generate release notes preview
make release-notes-preview

Automated Release Process

  1. Development: Features are developed on feature branches
  2. Integration: Changes are merged to develop for testing
  3. Release Preparation: Release branches are created for final testing
  4. Production Release: Stable releases are merged to main
  5. Automation: GitHub Actions handles versioning, changelog updates, and Packagist publishing

Cutting a Release

Maintainers: see the Release Runbook for the concrete, step-by-step procedure โ€” how a release is cut, the required repository secrets, how to trigger a release manually via workflow_dispatch, how to verify the GitHub Release and Packagist update, and rollback notes.

See docs/VERSION-MANAGEMENT.md for the underlying versioning model.

๐Ÿ“„ Changelog

See CHANGELOG.md for recent changes.

๐Ÿ‘ฅ Credits

๐Ÿ“œ License

This project is licensed under the MIT License. See LICENSE.md for details.

Made with โค๏ธ by CardTechie