Weave Code
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 Harvest Sdk Laravel Package

spatie/laravel-harvest-sdk

Laravel-friendly SDK for the Harvest.com API. Configure account ID, access token, and user agent, then resolve the Harvest client from the container or facade to call API endpoints. Not a complete implementation; PRs welcome.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require spatie/laravel-harvest-sdk
    

    Publish the config file:

    php artisan vendor:publish --provider="Spatie\HarvestSdk\HarvestServiceProvider"
    
  2. Configuration: Update .env with your Harvest API credentials:

    HARVEST_ACCOUNT_ID=your_account_id
    HARVEST_TOKEN=your_api_token
    
  3. First Use Case: Fetch a user’s time entries for today:

    use Spatie\HarvestSdk\Facades\Harvest;
    
    $timeEntries = Harvest::time()->entries()->today()->get();
    

Key Entry Points

  • Facades: Harvest::time(), Harvest::projects(), Harvest::invoices(), etc.
  • Config: .env or config/harvest.php for API credentials and defaults.
  • Documentation: Check the Harvest API docs for endpoint specifics.

Implementation Patterns

Common Workflows

1. Time Tracking

  • Create/Update Entries:
    Harvest::time()->entries()->create([
        'project_id' => 12345,
        'task_id' => 67890,
        'hours' => 2.5,
        'notes' => 'Fixed bug in auth module',
        'started_at' => now()->format('Y-m-d H:i:s'),
        'ended_at' => now()->addHours(2.5)->format('Y-m-d H:i:s'),
    ]);
    
  • Bulk Operations:
    $entries = Harvest::time()->entries()->all();
    foreach ($entries as $entry) {
        if ($entry->hours > 8) {
            Harvest::time()->entries()->update($entry->id, ['hours' => 8]);
        }
    }
    

2. Project Management

  • Fetch Projects:
    $projects = Harvest::projects()->all();
    $activeProjects = Harvest::projects()->active()->get();
    
  • Create Projects:
    Harvest::projects()->create([
        'name' => 'New Client Project',
        'code' => 'NCP',
        'is_active' => true,
    ]);
    

3. Invoicing

  • Generate Invoices:
    $invoice = Harvest::invoices()->create([
        'client_id' => 1,
        'number' => 'INV-2023-001',
        'due_date' => now()->addDays(14),
        'line_items' => [
            ['kind' => 'Time', 'quantity' => 10, 'rate' => 75.00],
        ],
    ]);
    

4. Webhooks

  • Register Webhook:
    Harvest::webhooks()->register(
        url: route('harvest.webhook'),
        events: ['time_entry.created', 'invoice.created']
    );
    
  • Handle Incoming Webhooks (in routes/web.php):
    Route::post('/harvest/webhook', [HarvestWebhookController::class, 'handle']);
    

Integration Tips

  • Laravel Models: Attach Harvest IDs to Eloquent models for quick lookups:
    class Project extends Model {
        protected $casts = ['harvest_id' => 'integer'];
    }
    
  • Jobs/Queues: Offload API calls to background jobs for performance:
    Harvest::time()->entries()->create([...])->later();
    
  • Caching: Cache frequent API responses (e.g., projects) using Laravel’s cache:
    $projects = Cache::remember('harvest.projects', now()->addHours(1), function () {
        return Harvest::projects()->all();
    });
    

Gotchas and Tips

Pitfalls

  1. Rate Limiting:

    • Harvest API enforces rate limits. Handle 429 Too Many Requests gracefully:
      try {
          $response = Harvest::time()->entries()->get();
      } catch (\Spatie\HarvestSdk\Exceptions\RateLimitExceeded $e) {
          sleep($e->retryAfter);
          retry();
      }
      
  2. ID Mismatches:

    • Harvest uses account_id (not user ID) for scoping. Ensure your .env has the correct HARVEST_ACCOUNT_ID.
  3. Webhook Verification:

    • Harvest sends a X-Harvest-Signature header. Validate it in your webhook handler:
      use Spatie\HarvestSdk\Webhook;
      
      public function handle(Request $request) {
          if (!Webhook::verify($request->header('X-Harvest-Signature'), $request->getContent())) {
              abort(403);
          }
      }
      
  4. Time Zone Handling:

    • Harvest uses UTC for timestamps. Convert to your local time zone when displaying:
      $entry->started_at->setTimezone('America/New_York');
      

Debugging

  • Enable Debugging: Add to config/harvest.php:

    'debug' => env('HARVEST_DEBUG', false),
    

    This logs API requests/responses to storage/logs/harvest.log.

  • Common Errors:

    • 401 Unauthorized: Check HARVEST_TOKEN in .env.
    • 404 Not Found: Verify HARVEST_ACCOUNT_ID and resource IDs.
    • 422 Unprocessable Entity: Validate payloads against Harvest’s API specs.

Extension Points

  1. Custom API Clients: Override the default Guzzle client in config/harvest.php:

    'client' => function () {
        return new \GuzzleHttp\Client([
            'timeout' => 30,
            'headers' => ['User-Agent' => 'MyApp/1.0'],
        ]);
    },
    
  2. Missing Endpoints: Extend the SDK by creating a custom facade or service:

    class CustomHarvest extends \Spatie\HarvestSdk\Facades\Harvest {
        public function customEndpoint() {
            return $this->client->get('/custom/endpoint');
        }
    }
    
  3. Testing: Use the HarvestFake class for unit tests:

    use Spatie\HarvestSdk\Testing\HarvestFake;
    
    public function test_time_entries() {
        HarvestFake::fake();
        HarvestFake::timeEntries()->shouldReceive('get')->andReturn([]);
    
        $entries = Harvest::time()->entries()->get();
        $this->assertEmpty($entries);
    }
    
  4. Pagination: Handle paginated responses manually:

    $entries = [];
    $page = 1;
    do {
        $response = Harvest::time()->entries()->page($page)->get();
        $entries = array_merge($entries, $response);
        $page++;
    } while (count($response) > 0);
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport