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

Mite Bundle Laravel Package

stejac/mite-bundle

Symfony bundle for integrating Mite time tracking into PHP applications. It provides configurable services around the Mite API to manage projects, services, and time entries, making it easier to add time-tracking features to Symfony apps.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first: stejac/mite-bundle is a Symfony bundle, not a Laravel package. composer require stejac/mite-bundle will pull in Symfony DI/Config/HttpClient, and Laravel's package discovery will not register it. There is no service provider, no config file to publish, and no facade. In a Laravel app you have two viable paths:

  1. Use the bundle's typed services directly (recommended). The DTOs, enums, input DTOs and resource services are plain PHP classes — you wire them yourself in a small MiteServiceProvider. You get the typed API surface without the Symfony kernel.
  2. Depend on the underlying mite client and skip the bundle. Only do this if the service layer adds nothing for you; you lose the per-resource typed services and the exception hierarchy.

Minimal steps for path 1:

composer require stejac/mite-bundle
// config/mite.php
return [
    'domain'     => env('MITE_DOMAIN'),      // subdomain only, e.g. "acme"
    'api_key'    => env('MITE_API_KEY'),
    'user_agent' => env('MITE_USER_AGENT', 'myapp/1.0 ([email protected])'),
];
// app/Providers/MiteServiceProvider.php
public function register(): void
{
    $this->app->singleton(MiteClient::class, fn () => new MiteClient(
        domain: config('mite.domain'),
        apiKey: config('mite.api_key'),
        userAgent: config('mite.user_agent'),
        httpClient: new Psr18Client(...), // see Gotchas: Laravel's Http facade is not a PSR-18 client
    ));

    $this->app->bind(TimeEntryService::class, fn ($app) => new TimeEntryService($app->make(MiteClient::class)));
    // ...one binding per resource service you actually use
}

Where to look first: the resource services (Account, TimeEntry, Tracker, Bookmark, Customer, Project, Service, User, Changes) and the DTO/enum directory. First use case: read-only reporting — fetch the account (for timezone and minute-based duration semantics), then list time entries for a date range and render them. That exercises config, auth, DTO hydration and error handling in one pass without touching write paths.

Implementation Patterns

Keep Symfony types out of your app. Don't inject TimeEntryService into controllers. Wrap it:

final class MiteTimeEntries
{
    public function __construct(private TimeEntryService $mite) {}

    /** @return TimeEntry[] */
    public function forRange(CarbonInterface $from, CarbonInterface $to, ?int $projectId = null): array
    {
        return $this->mite->list(from: $from->toDateString(), to: $to->toDateString(), projectId: $projectId);
    }
}

Your controllers, jobs and Blade/Livewire components then depend on your class, and the eventual ETag work or a client swap is a one-file change.

Config mapping. The bundle's four keys map cleanly onto Laravel config: domain is the mite subdomain (not a URL), api_key goes in .env only, user_agent should identify your app and a contact address (mite uses it for API etiquette and may throttle anonymous agents), and http_client is the seam you replace.

Scheduled sync via the changes feed. This is the highest-value pattern. Run an Artisan command on a schedule, persist the cursor, and upsert into local tables:

Schedule::command('mite:sync')->everyFifteenMinutes()->withoutOverlapping();

Inside, walk the changes feed from the stored cursor, map each change to the relevant typed resource service for the full payload, upsert, then advance the cursor after the write transaction commits. Treat the feed as at-least-once: key on the resource ID and make upserts idempotent.

Grouped reports and daily aliases are for dashboards. Prefer the grouped/report endpoint over fetching entries and summing in PHP — one request, less mapping code, and the shape already matches what a chart component wants. Cache the result for a few minutes; there is no ETag support yet, so nothing upstream will save you a round trip.

Input DTOs and partial updates. Build an input DTO per mutation and set only what the user actually changed. This is what the partial-update semantics are for — a form that edits just the note should not send back a reconstructed full payload from a stale read. One DTO per form, populated field-by-field.

Exceptions. Catch MiteExceptionInterface at a single boundary (a middleware or your wrapper class) and translate:

  • 401 → configuration problem; fail loudly, log without the key, alert in non-prod.
  • 404 → wrong domain, or a deleted resource; distinguish "wrong subdomain" in the message, because it's the most common setup mistake.
  • 422 → validation; map back onto the form's field errors where the API supplies them.
  • 429 → retry with backoff; wrap the client in Symfony's RetryableHttpClient at construction so every call inherits it.

Testing. Http::fake() in Laravel does not intercept Symfony HttpClient. Bind a MockHttpClient (or a PSR-18 fake) in MiteServiceProvider under APP_ENV=testing, or in setUp() via $this->app->instance(...). Record real response bodies once for the happy paths of each resource and reuse them as fixtures.

Gotchas and Tips

  • No package discovery, no vendor:publish, no facade. Any tutorial showing php artisan vendor:publish --tag=mite-config is for a different package. Write config/mite.php by hand.
  • The bundle is a Symfony bundle. Registering its Extension in Laravel is unsupported. Don't try to load it into Laravel's container; use the classes, skip the DI plumbing.
  • http_client must be PSR-18. Laravel's Http facade is not. Either use symfony/http-client's Psr18Client over GuzzleHttp\Client, or Http\Discovery\Psr18ClientDiscovery. Getting this wrong produces an interface-not-implemented error rather than an HTTP error, which is confusing on first run.
  • domain is a subdomain, not a URL. acme — not https://acme.mite.yo.lk. Combined with a 404 it looks like a permissions issue; it isn't.
  • Partial-update semantics and nulls. "Field omitted" and "field set to null" are different intents and can mean "leave alone" vs "clear". Verify which the input DTO encodes before letting a form's default-null fields blank out real data. Add a test that patches one field and asserts the rest are unchanged.
  • Read-only DTOs. They're value objects out of the API. Don't mutate them or use them as your form model — map to your own model, then to an input DTO. Trying to reuse a read DTO as a write payload will fight the typing.
  • Durations are minutes, dates are account-local. Read the account's timezone once at boot or in the sync command. Building a report with Carbon::now() in UTC against account-local entries silently shifts boundary days.
  • user_agent is not decoration. Set a real one with contact info; a generic client string is a plausible route to throttling.
  • No caching yet. ETag / If-None-Match is planned, not shipped. Every call is a full fetch, so: cache report output yourself, don't poll tight loops, and don't fan out one request per project. Because the feature is coming, keep all HTTP calls behind your wrapper — adopting conditional requests later should be one change in one file, not a grep across the codebase.
  • Pin the version. v1.0.0 is the first stable tag and it's a fresh API surface; use ^1.0 and read the changelog before minor bumps. Anything under 0.x had no compatibility promise, so don't copy pre-1.0 snippets.
  • Log the request ID and status on every failure, but never log api_key — Symfony's HTTP client will happily dump headers in an exception trace.
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core