spatie/laravel-float-sdk
Laravel-friendly SDK for interacting with the Float.com API (v3). Configure your API token and user agent via .env/config and use the provided FloatClient to access Float endpoints. Not a full API implementation; contributions welcome.
composer require spatie/laravel-float-sdk
.env:
FLOAT_API_TOKEN=your_float_api_token_here
FLOAT_USER_AGENT="YourAppName (your-email@example.com)"
php artisan vendor:publish --tag="float-sdk-config"
use Spatie\FloatSdk\FloatClient;
class UserController extends Controller
{
public function __construct(protected FloatClient $float) {}
public function show($id)
{
$user = $this->float->users()->get($id);
return response()->json($user);
}
}
FloatClient is auto-bound to Laravel’s container.users(), projects(), or allocations() for common workflows.GetUsersParams, GetProjectsParams, etc., for filtering (e.g., active: true).Best Practice: Inject FloatClient into controllers/services.
public function __construct(protected FloatClient $float) {}
$project = $this->float->projects()->get(10);
$users = $this->float->users()->all();
$activeUsers = $this->float->users()->all(
new GetUsersParams(active: true)
);
$users = $this->float->users()->all(
new GetUsersParams(
page: 2,
perPage: 20,
sort: 'name'
)
);
$projects = $this->float->projects()->all(
new GetProjectsParams(
fields: ['id', 'name', 'client_id'],
expand: ['client']
)
);
// Fetch pending allocations for approval
$allocations = $this->float->allocations()->all(
new GetAllocationsParams(
status: 'pending',
startDate: now()->subDays(7)->format('Y-m-d'),
endDate: now()->format('Y-m-d')
)
);
// Approve via Float’s API (extend SDK if needed)
foreach ($allocations as $allocation) {
$this->float->allocations()->approve($allocation->id);
}
// Get project tasks with client details
$tasks = $this->float->projectTasks()->all(
new GetProjectTasksParams(
projectId: 42,
expand: ['client']
)
);
// Aggregate by client
$report = $tasks->groupBy('client.name');
// Fetch time off types for a dropdown
$timeOffTypes = $this->float->timeOff()->types();
// Validate time off requests
$requests = $this->float->timeOff()->all(
'2023-12-01',
'2023-12-31',
new GetTimeOffParams(
status: 'pending',
userId: auth()->id()
)
);
Error Handling:
Wrap SDK calls in try-catch blocks to handle API errors gracefully:
try {
$user = $this->float->users()->get(1);
} catch (\Spatie\FloatSdk\Exceptions\FloatException $e) {
Log::error("Float API Error: " . $e->getMessage());
return response()->json(['error' => 'Service unavailable'], 503);
}
Caching: Cache frequent queries (e.g., client lists) using Laravel’s cache:
$clients = Cache::remember('float.clients', now()->addHours(1), function () {
return $this->float->clients()->all();
});
Testing:
Use Laravel’s MockFloatClient (if provided) or mock the FloatClient interface:
$this->mock(FloatClient::class, function ($mock) {
$mock->shouldReceive('users')
->andReturnSelf()
->shouldReceive('get')
->with(1)
->andReturn(new UserVO(['id' => 1, 'name' => 'Test User']));
});
Extending the SDK: Create a facade or decorator for custom logic:
class FloatService
{
public function __construct(protected FloatClient $float) {}
public function getUserAllocations($userId)
{
return $this->float->allocations()->all(
new GetAllocationsParams(userId: $userId)
);
}
}
Single Object Response Handling:
allocations()->get($id)) might return inconsistent data structures.$allocation = $this->float->allocations()->get(1);
if (!is_array($allocation)) {
$allocation = [$allocation]; // Normalize if needed
}
Pagination Limits:
perPage is 50. For large datasets, paginate manually:
$page = 1;
do {
$users = $this->float->users()->all(
new GetUsersParams(page: $page, perPage: 100)
);
$page++;
} while (!empty($users));
API Rate Limits:
use Spatie\FloatSdk\Exceptions\RateLimitExceeded;
try {
$data = $this->float->users()->all();
} catch (RateLimitExceeded $e) {
sleep($e->getRetryAfter());
retry();
}
Field Expansion:
expand. Check the Float API docs for valid relationships.Time Off Date Ranges:
timeOff()->all() method expects ISO-8601 formatted dates (e.g., '2023-12-01'). Invalid formats may return empty results.Enable API Logging:
Configure the underlying Saloon HTTP client to log requests:
$this->float->withOptions(['debug' => true]);
Logs will appear in storage/logs/laravel.log.
Inspect Raw Responses: Access the raw API response for debugging:
$response = $this->float->users()->get(1);
\Log::debug($response->originalResponse->body());
Query Parameter Validation:
Use dd() to inspect generated query strings:
$params = new GetUsersParams(active: true);
dd($params->toArray()); // Verify parameters
Custom Query Parameters:
Extend existing Params classes or create new ones:
namespace App\QueryParameters;
use Spatie\FloatSdk\QueryParameters\QueryParams;
class GetActiveProjectsParams extends QueryParams
{
public function __construct(
public ?int $clientId = null,
public ?string $status = 'active'
) {}
}
Response Transformers: Override response handling for specific endpoints:
$this->float->extend('users', function ($float) {
$float->getTransformer()->setCustomTransformer('users', function ($response) {
return collect($response)->map(function ($user) {
return (object) array_merge($user, ['formatted_name' => ucfirst($user->name)]);
});
});
});
Webhook Integration: Use the SDK to validate incoming Float webhooks:
public function handleWebhook(Request $request)
{
$payload = $request->json();
$user = $this->float->users()->get($payload['user_id']);
// Validate payload against Float’s data
}
Testing:
Mock the FloatClient for unit tests:
$this->partialMock(FloatClient::class, function ($mock) {
$mock
How can I help you explore Laravel packages today?