Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Float Sdk Laravel Package

spatie/laravel-float-sdk

A Laravel-friendly SDK for the Float.com API v3, with config publishing, API token and user-agent setup, and a FloatClient for interacting with the API. Not a full Float API implementation, but extensible via PRs.

View on GitHub
Deep Wiki
Context7

Getting Started

Three commands and two env vars, and you're talking to Float:

composer require spatie/laravel-float-sdk
FLOAT_API_TOKEN=your_api_token_here
FLOAT_USER_AGENT="YourAppName ([email protected])"
php artisan vendor:publish --tag="float-sdk-config"

The published config is deliberately tiny — just api_token and user_agent read from env(). There is no base URL, timeout, retry, or cache setting to configure, which is the first hint about the package's scope: it is a thin, typed wrapper, not an integration platform.

Where to look first. Not the README alone — open src/QueryParameters/. That namespace is the real map of what's supported. Each resource group has a matching Get*Params object, and the constructor's named arguments tell you exactly which filters Float's v3 API exposes for that endpoint. If a filter you need isn't a property there, the package doesn't send it. Cross-reference against developer.float.com before you promise a feature.

First use case. The one that pays for the package in an afternoon is an allocation/capacity view. Inject the client, ask for allocations in a window, render them:

use Spatie\FloatSdk\FloatClient;
use Spatie\FloatSdk\QueryParameters\GetAllocationsParams;

public function __construct(protected FloatClient $float) {}

public function capacity()
{
    return $this->float->allocations()->all(
        new GetAllocationsParams(
            startDate: now()->startOfMonth()->toDateString(),
            endDate: now()->endOfMonth()->toDateString(),
            fields: ['id', 'project_id', 'people_id', 'start_date', 'end_date', 'hours'],
        )
    );
}

Verify the shape in php artisan tinker before writing any transformation code — the return types are arrays of arrays, not DTOs, and the shape differs between all() and get() (see Gotchas).

Implementation Patterns

Wrap the client, don't sprinkle it. FloatClient is container-bound and injectable, which makes it tempting to call $this->float->projects()->all() from a dozen controllers. Don't. Put a FloatService or per-domain repository in front of it so that when you hit a missing endpoint, change tokens, or swap the package out, you have one seam to touch. It also gives you one place to normalize dates and field names.

Sync into local tables; query locally. The highest-leverage pattern for anything beyond a single screen is a scheduled command that pulls Float data into Eloquent models keyed on Float's IDs, then does reporting against your own database:

// app/Console/Commands/SyncFloatProjects.php
foreach ($this->paginate(fn ($page) => $this->float->projects()->all(
    new GetProjectsParams(page: $page, perPage: 200)
)) as $project) {
    Project::updateOrCreate(['float_id' => $project['project_id']], [...]);
}

This buys you joins, indexes, whereBetween on dates, and no per-request API latency. It also means your dashboard doesn't break when Float is slow. Treat Float as system of record, your tables as a read replica.

Loop pagination explicitly. all() returns one page (default perPage: 50) — it does not auto-paginate. Every sync helper needs a loop that stops when a page returns fewer than perPage rows (or zero). Bump perPage on bulk syncs; the default of 50 is fine for interactive lookups and wasteful for backfills.

Prune payloads with fields, then expand only what you need. For a capacity board you need six fields, not the full project graph. Then use expand sparingly: expand: ['client'] on projects is genuinely useful for building a client→project tree in one pass, but each expansion inflates the response and the mapping work.

Combine the three read streams for the actual insight. Allocations tell you someone is booked; timeOff()->all($from, $to) tells you they're away; publicHolidays()->all($year) tells you the day doesn't count. The genuinely useful feature — a "this person is overallocated or booked over leave" alert — is a merge of those three arrays in PHP. Note the signature inconsistency: time off and holidays take positional args, everything else takes a params object.

$timeOff    = $float->timeOff()->all('2025-01-01', '2025-12-31');
$holidays   = $float->publicHolidays()->all(2025);
$timeOffTypes = $float->timeOff()->types(); // map type_id -> human label

Push bulk pulls into queued jobs. A full allocation backfill is dozens of HTTP round-trips. Run it in a job with a chunked loop so a timeout doesn't force you to restart from page 1, and so a slow Float doesn't hold a web request open.

Test by binding a fake, not by faking HTTP. The package is built on Saloon (the changelog's first entry: "Add fetch endpoints with Saloon"), so you can register a Saloon mock client or, more simply for application tests, bind a stub FloatClient subclass into the container. Prefer the stub: most of your tests are about what your app does with the data, not about whether the package serializes params correctly.

Cache the cheap, stable stuff. clients()->all(), users()->all(), and timeOff()->types() change rarely and are read on every dashboard render. Cache::remember with a 15–60 minute TTL on the raw arrays gets you most of the way to a fast app without touching the package.

Multi-tenant / per-workspace tokens. The config reads a single FLOAT_API_TOKEN. If you need several, don't fight the config — construct additional FloatClient instances yourself in a factory and leave the container-bound one for the default account.

Gotchas and Tips

It is read-only and partial, by design. The README says so plainly: "not a full implementation." Seven resource groups, all get/all. No creating allocations, no logging time, no webhooks, no change feeds. If your roadmap includes pushing data into Float or reacting to Float events, this package covers none of it — scope that separately before you commit to it in a plan. Also note 0 dependents: you are an early adopter, and the endpoint you need next may not exist yet.

get() and all() do not return the same shape, and this has already bitten someone. Release 1.0.1 carries "Fix single allocation object response" — a single-record response was wrapped differently from what callers expected. Never assume; dump the raw array for each endpoint you touch and write a small assertion. If you build DTOs, write them against observed payloads, and keep one test per endpoint you depend on so a patch release that changes shape fails loudly in CI rather than on a dashboard.

FloatClient is named that way for a reason. float is a reserved word in PHP, so you cannot have a class named Float. Every Stack Overflow answer and IDE import you find will be slightly wrong. Import Spatie\FloatSdk\FloatClient.

The user agent is not decorative. Float's API expects an identifiable UA with a contact address. A generic or missing UA is the kind of thing that gets you throttled or blocked with an unhelpful error. Put a real email in there, and set it per-environment so sandbox traffic is distinguishable in Float's logs.

env() in a published config file plus config:cache is a classic trap. After php artisan config:cache, changes to FLOAT_API_TOKEN in .env have no effect until you re-cache. If auth suddenly fails in a deployed environment and works locally, check the cached config before you check the token.

Errors surface as exceptions, not false. The package does not swallow failures — a bad token, a 429, or a 5xx will throw (Saloon's RequestException family). Wrap external calls in your service layer, catch and log with the endpoint context, and decide per call site whether to degrade gracefully or fail. There's no built-in retry or backoff configured; if you need it, add it at the Saloon connector level or in your wrapper.

Rate limits are yours to respect. Nothing in the config throttles you. A naive pagination loop with perPage: 50 over a year of allocations will fire a lot of requests in a burst. Use larger page sizes for bulk work, add a small sleep or a job-level rate limiter, and run backfills off-peak.

Date filters are strings, not Carbon. startDate: '2025-01-01' — pass ->toDateString() and be consistent about timezone. Float's allocation and time-off windows are date-based; a stray toDateTimeString() will either be ignored or error confusingly.

perPage has an upper bound you'll discover the hard way. The package passes it through; if Float rejects or silently clamps an absurd value, your pagination termination condition (rows < perPage) can mis-fire. Log the row count per page during the first run of any sync and sanity-check it against the total.

Extending it is genuinely cheap. Every resource is a small class and the params objects are plain VOs. Adding a missing endpoint is usually a new resource class plus a params object plus a FloatClient accessor — an hour's work, and the project explicitly welcomes PRs. If you find yourself reaching for raw Http:: calls, a local fork or an upstream PR will serve you better than a second integration path.

Pin the version loosely but watch releases. The package tracks Laravel versions (2.1.0 adds Laravel 13), so it won't block your upgrade cycle — but with a small contributor base and few releases, read the changelog on every bump. The 1.0.1 response-shape fix is exactly the kind of change that is invisible in a semver patch.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log