PHPackages                             timatic/php-sdk - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. timatic/php-sdk

ActiveLibrary[API Development](/categories/api)

timatic/php-sdk
===============

PHP SDK for Timatic API built with Saloon

0.1.0(5mo ago)01Elastic-2.0PHPPHP ^8.2CI passing

Since Dec 9Pushed 3mo agoCompare

[ Source](https://github.com/Timatic/timatic-php-sdk)[ Packagist](https://packagist.org/packages/timatic/php-sdk)[ RSS](/packages/timatic-php-sdk/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (11)Versions (4)Used By (0)

Timatic PHP SDK
===============

[](#timatic-php-sdk)

[![Tests](https://github.com/Timatic/timatic-php-sdk/actions/workflows/tests.yml/badge.svg)](https://github.com/Timatic/timatic-php-sdk/actions/workflows/tests.yml)[![codecov](https://camo.githubusercontent.com/a0d6ec44f0d2a4cde04186747d48413ed22b4155ffd61dd0b7726dcb0fc88905/68747470733a2f2f636f6465636f762e696f2f67682f54696d617469632f74696d617469632d7068702d73646b2f6272616e63682f6d61696e2f67726170682f62616467652e737667)](https://codecov.io/gh/Timatic/timatic-php-sdk)[![Code Style](https://github.com/Timatic/timatic-php-sdk/actions/workflows/code-style.yml/badge.svg)](https://github.com/Timatic/timatic-php-sdk/actions/workflows/code-style.yml)[![Static Analysis](https://github.com/Timatic/timatic-php-sdk/actions/workflows/static-analysis.yml/badge.svg)](https://github.com/Timatic/timatic-php-sdk/actions/workflows/static-analysis.yml)

A Laravel package for the Timatic API, built with [Saloon](https://docs.saloon.dev/) and automatically generated from OpenAPI specifications.

Requirements
------------

[](#requirements)

- PHP 8.2 or higher
- Laravel 10.x or higher

Installation
------------

[](#installation)

```
composer require timatic/php-sdk
```

Configuration
-------------

[](#configuration)

The package automatically registers itself via Laravel auto-discovery.

Publish the config file:

```
php artisan vendor:publish --tag=timatic-config
```

Add your API credentials to `.env`:

```
TIMATIC_BASE_URL=https://api.app.timatic.test
TIMATIC_API_TOKEN=your-api-token-here
```

Usage
-----

[](#usage)

### Using Dependency Injection

[](#using-dependency-injection)

The SDK connector is automatically registered in Laravel's service container, making it easy to inject into your controllers, commands, and other classes:

```
use Timatic\Requests\Budget\GetBudgetsCollectionRequest;
use Timatic\Requests\Budget\GetBudgetRequest;
use Timatic\Requests\Budget\PostBudgetsRequest;
use Timatic\Requests\BudgetType\GetBudgetTypesCollectionRequest;

use Timatic\TimaticConnector;

class BudgetController extends Controller
{
    public function __construct(
        protected TimaticConnector $timatic
    ) {}

    public function index()
    {
        // fetch one or more items, limited by the default page size from the api
        $budgetTypes = $this->timatic
            ->send(new GetBudgetTypesCollectionRequest())
            ->dto();

        $defaultBudget = $this->timatic->send(
            new \Timatic\Requests\Budget\GetBudgetRequest(id: '1337')
        )->dtoOrFail();

        // fetch all DTO's
        $budgets = $this->timatic->paginate(new GetBudgetsCollectionRequest())
            ->dtoCollection();

        return view('budgets.index', compact('budgets', 'budgetTypes', 'defaultBudget'));
    }

    public function store(Request $request)
    {
        $budget = new \Timatic\Dto\Budget([
            'title' => $request->input('title'),
            'totalPrice' => $request->input('total_price'),
        ]);

        $created = $this->timatic
            ->send(new PostBudgetsRequest($budget))
            ->dtoOrFail();

        return redirect()->route('budgets.show', $created->id);
    }
}
```

**In Console Commands:**

```
use \Timatic\Requests\Budget\GetBudgetsCollectionRequest;
use Timatic\TimaticConnector;

class SyncBudgetsCommand extends Command
{
    public function handle(TimaticConnector $timatic): int
    {
        $budgets = $timatic->paginate(
            new GetBudgetsCollectionRequest()
        )->dtoCollection()

        foreach ($budgets as $budget) {
            // Process budgets
        }

        return Command::SUCCESS;
    }
}
```

### Testing

[](#testing)

When testing code that uses the Timatic SDK, you can mock the connector and its responses using factories. The SDK includes factory classes for all DTOs that make it easy to generate test data.

Here's an example of testing the `BudgetController` from the example above:

```
use Timatic\TimaticConnector;
use Timatic\Dto\Budget;
use Timatic\Dto\BudgetType;
use Timatic\Requests\Budget\GetBudgetsRequest;
use Timatic\Requests\Budget\PostBudgetsRequest;
use Timatic\Requests\BudgetType\GetBudgetTypesRequest;
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;

test('it displays budgets and budget types', function () {
    // Generate test data using factories
    $budget = Budget::factory()->state(['id' => '1'])->make();
    $budgetType = BudgetType::factory()->state(['id' => '1'])->make();

    // Create mock responses using factory-generated data
    $mockClient = MockClient::global([
        GetBudgetsRequest::class => MockResponse::make([
            'data' => [$budget->toJsonApi()],
        ], 200),
        GetBudgetTypesRequest::class => MockResponse::make([
            'data' => [$budgetType->toJsonApi()],
        ], 200),
    ]);

    // Make request
    $response = $this->get(route('budgets.index'));

    // Assert
    $response->assertOk();
    $response->assertViewHas('budgets');
    $response->assertViewHas('budgetTypes');
});

test('it creates a new budget', function () {
    // Generate test data with specific attributes
    $budget = Budget::factory()->state([
        'id' => '2',
        'title' => 'New Budget',
        'totalPrice' => '5000.00',
    ])->make();

    $mockClient = MockClient::global([
        PostBudgetsRequest::class => MockResponse::make([
            'data' => $budget->toJsonApi(),
        ], 201),
    ]);

    $response = $this->post(route('budgets.store'), [
        'title' => 'New Budget',
        'total_price' => 5000.00,
    ]);

    $response->assertRedirect(route('budgets.show', '2'));
});

test('it sends a POST request to create a budget using the SDK', function () {
    $budgetToCreate = Budget::factory()->state([
        'title' => 'New Budget',
        'totalPrice' => '5000.00',
        'customerId' => 'customer-123',
    ])->make();

    $createdBudget = Budget::factory()->state([
        'id' => 'created-456',
        'title' => 'New Budget',
        'totalPrice' => '5000.00',
        'customerId' => 'customer-123',
    ])->make();

    $mockClient = MockClient::global([
        PostBudgetsRequest::class => MockResponse::make([
            'data' => $createdBudget->toJsonApi(),
        ], 201),
    ]);

    artisan('sync:budgets')->assertOk();

    // Assert the request body was sent correctly
    $mockClient->assertSent(function (PostBudgetsRequest $request) {
        $body = $request->body()->all();

        return $body['data']['attributes']['title'] === 'New Budget'
            && $body['data']['attributes']['totalPrice'] === '5000.00'
            && $body['data']['attributes']['customerId'] === 'customer-123';
    });
});
```

#### Factory Methods

[](#factory-methods)

Every DTO in the SDK has a corresponding factory class with the following methods:

```
// Create a single model with random data, without an ID
$budget = Budget::factory()->make();

// Create multiple models with unique UUID IDs
$budgets = Budget::factory()->withId()->count(3)->make(); // Returns Collection

// Override specific attributes
$budget = Budget::factory()->state([
    'title' => 'Q1 Budget',
    'totalPrice' => '10000.00',
])->make();

// Chain state calls for complex scenarios
$budget = Budget::factory()
    ->state(['customerId' => $customerId])
    ->state(['budgetTypeId' => $budgetTypeId])
    ->make();
```

For more information on mocking Saloon requests, see the [Saloon Mocking Documentation](https://docs.saloon.dev/testing/faking-responses).

### Pagination

[](#pagination)

The SDK supports automatic pagination for all collection endpoints using Saloon's pagination plugin:

```
use Timatic\TimaticConnector;
use Timatic\Requests\Budget\GetBudgets;

class BudgetController extends Controller
{
    public function index(TimaticConnector $timatic)
    {
        // Create a paginator
        $paginator = $timatic->paginate(new GetBudgets());

        // Optionally set items per page (default is API's default)
        $paginator->setPerPageLimit(50);

        // Iterate through all pages automatically
        foreach ($paginator->items() as $budget) {
            // Process each budget across all pages
            // The paginator handles pagination automatically
        }

        // Or collect all items at once
        $allBudgets = $paginator->dtoCollection();
    }
}
```

The paginator:

- Automatically handles JSON:API pagination (`page[number]` and `page[size]`)
- Detects the last page via `links.next`
- Works with all GET collection requests (GetBudgets, GetCustomers, GetUsers, etc.)

### Custom Response Methods

[](#custom-response-methods)

All responses are instances of `TimaticResponse` which extends Saloon's Response with JSON:API convenience methods:

```
$response = $timatic->send(new GetBudgetsCollectionRequest());

// Get the first item from a collection
$firstBudget = $response->firstItem();

// Check for errors
if ($response->hasErrors()) {
    $errors = $response->errors();
    // Handle errors...
}

// Access JSON:API meta information
$meta = $response->meta();
$total = $meta['total'] ?? 0;

// Access pagination links
$links = $response->links();
$nextPage = $links['next'] ?? null;

// Access included resources
$included = $response->included();
foreach ($included as $resource) {
    // Process related resources
}
```

HTTP Methods
------------

[](#http-methods)

This SDK follows REST best practices and **does not support PUT requests**. Instead:

- **POST** - Create new resources
- **PATCH** - Partially update existing resources
- **GET** - Retrieve resources
- **DELETE** - Remove resources

PUT is intentionally excluded because resources are never completely replaced by Timatic.

Available Resources
-------------------

[](#available-resources)

The SDK provides access to the following resources:

- **Budgets** - Manage budgets and budget entries
- **Customers** - Customer management
- **Users** - User management
- **Teams** - Team management
- **Entries** - Time entry management
- **Incidents** - Incident tracking
- **Changes** - Change tracking
- **Overtimes** - Overtime management
- **Events** - Event logging
- And more...

JSON:API Support
----------------

[](#jsonapi-support)

This SDK uses a custom **JSON:API DTO Generator** that automatically flattens JSON:API attributes into proper Model properties. Instead of having generic `$attributes`, `$type`, and `$relationships` objects, each model has specific typed properties.

### Example

[](#example)

Instead of:

```
$budget->attributes->title; // ❌ Generic structure
```

You get:

```
$budget->title; // ✅ Proper typed property
$budget->budgetTypeId;
$budget->startedAt; // Carbon instance for datetime fields
```

### Model Features

[](#model-features)

- **Extends `Model` base class** with JSON:API support
- **Property attributes** via `#[Property]` for serialization
- **DateTime handling** with Carbon instances
- **Type safety** with PHP 8.1+ type hints
- **HasAttributes trait** for easy attribute manipulation

Regenerating the SDK
--------------------

[](#regenerating-the-sdk)

This SDK is automatically generated from the Timatic API OpenAPI specification using a custom JSON:API generator. To regenerate the SDK with the latest API changes:

```
composer regenerate
```

This will:

1. Download the latest OpenAPI specification from the API
2. Generate Models with flattened JSON:API attributes
3. Update the autoloader
4. Format the code with Laravel Pint

### How It Works

[](#how-it-works)

The SDK uses a custom `JsonApiDtoGenerator` that:

1. Detects JSON:API schemas in the OpenAPI specification
2. Extracts properties from the `attributes` object
3. Generates proper Model classes with specific properties
4. Adds `#[Property]` and `#[DateTime]` attributes
5. Uses Carbon for datetime fields

Development
-----------

[](#development)

### Running Tests

[](#running-tests)

```
# Run tests
composer test

# Run tests with code coverage
composer coverage
```

License
-------

[](#license)

This package is licensed under the Elastic License 2.0 (ELv2).

Credits
-------

[](#credits)

- Built with [Saloon](https://docs.saloon.dev/)
- Generated using [Saloon SDK Generator](https://docs.saloon.dev/installable-plugins/sdk-generator)

###  Health Score

32

—

LowBetter than 72% of packages

Maintenance76

Regular maintenance activity

Popularity1

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

160d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/22033bf3bc370af7632beaa0c40b05a672d56b835444d71c304d67c73634ed6b?d=identicon)[tomasvanrijsse](/maintainers/tomasvanrijsse)

---

Top Contributors

[![tomasvanrijsse](https://avatars.githubusercontent.com/u/1403466?v=4)](https://github.com/tomasvanrijsse "tomasvanrijsse (80 commits)")

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/timatic-php-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/timatic-php-sdk/health.svg)](https://phpackages.com/packages/timatic-php-sdk)
```

###  Alternatives

[statamic/cms

The Statamic CMS Core Package

4.8k3.2M720](/packages/statamic-cms)[team-reflex/discord-php

An unofficial API to interact with the voice and text service Discord.

1.1k379.4k24](/packages/team-reflex-discord-php)[temporal/sdk

Temporal SDK

4002.2M18](/packages/temporal-sdk)[codebar-ag/laravel-docuware

DocuWare integration with Laravel

1221.1k](/packages/codebar-ag-laravel-docuware)[myoutdeskllc/salesforce-php

salesforce library for php8+

1560.8k](/packages/myoutdeskllc-salesforce-php)[sandorian/moneybird-api-php

Moneybird API client for PHP

127.3k](/packages/sandorian-moneybird-api-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
