innobrain/laravel-onoffice-adapter
Laravel adapter for the onOffice API that makes it easy to connect your PHP application to onOffice CRM data, including real estate listings, contacts, and addresses, through a clean, framework-friendly integration layer.
Install and let auto-discovery do its job.
composer require innobrain/laravel-onoffice-adapter
php artisan vendor:publish --tag=onoffice-config
Laravel's package discovery should register the service provider; if you're on an older skeleton, add it manually to config/app.php.
Fill in credentials before touching any code. Open the published config/onoffice.php first — this is the single most informative file in the package. You'll typically need the onOffice API URL, an API token, and a secret (exact env key names vary; copy them from the config rather than guessing). Put them in .env and never commit them.
Read the source in this order when the README is thin: config/onoffice.php → the service provider (what gets bound into the container) → the main client class → any facade. The public surface of a package this small is usually 1–3 classes; you can read all of it in fifteen minutes.
First use case: read a page of estates.
$response = app(OnOfficeClient::class)->call('read', [
'resource' => 'estate',
'listlimit' => 10,
]);
Verify the exact method signature against the source — the point is to confirm credentials, connectivity, and response shape with a single read before building anything on top. Log the raw response once, then never again.
Wrap the adapter, don't sprinkle it. Create your own OnOfficeRepository (or per-domain services like EstateRepository, AddressRepository) that injects the package client. Your app code should never see onOffice's request/response arrays. This is the difference between a one-week integration and a one-year liability.
Map responses to DTOs, not arrays. The onOffice API returns stringly-typed data with German-flavoured field names. Run responses through a mapper (EstateData::fromArray($row)) so nullability, casting, and field renames live in one place. When onOffice renames a field, you change one mapper instead of forty call sites.
Treat filters and sorting as first-class objects. The API expects structured filter arrays ([['op' => '=', 'field' => 'status', 'value' => 1]]-style). Build a small query builder or value objects around this rather than hand-writing nested arrays at every call site — it makes filters testable and reusable across list views and exports.
Batch activity reads instead of looping. Since v2.8.0 the activity read accepts multiple estate IDs via estateIds(), so you can pull the activity stream for a whole set of estates in one request rather than issuing one call per estate:
// prefer this over a foreach of single-estate activity reads
$activities = $client->call('read', [
'resource' => 'activity',
'estateIds' => $estateIds,
]);
Chunk large ID lists (the underlying API still returns a bounded result set per request) and treat this as your default for dashboards, timelines, and "recent activity" widgets. It is additive — nothing to migrate — but existing loop-based code is now the slow path.
Cache reference data aggressively. Field lists, filter options (getfilter), and lookup tables change rarely and are expensive to fetch. Cache them for hours with Cache::remember, and key the cache by credential/tenant if you support more than one onOffice account.
Move writes and bulk syncs to queues. Creates, modifies, and multi-page estate/address syncs belong in jobs with $tries, backoff, and failed() handlers. onOffice is a CRM, not your primary datastore — a slow or failed write must never block a web request.
Test with Http::fake(). Because the adapter ultimately speaks HTTP, you can fake at the boundary and assert on the outgoing XML/JSON payload without hitting the real API. Bind a fake client into the container in your TestCase and write contract tests for your repository layer. Include a fixture for the multi-estate activity request so a future release doesn't quietly change its shape.
Isolate multi-tenant credentials. If different sites use different onOffice accounts, resolve credentials per-tenant (a config repository or a scoped binding) rather than reading config() inside the client. Otherwise you'll leak estates between customers — the worst possible bug in this domain.
$response->successful().listlimit/listoffset (or the package's equivalent). Silent truncation is the classic "why are only 20 estates showing?" bug — this applies to multi-estate activity reads too, so don't assume estateIds() returns an unbounded stream."" or absent. Normalise in your mapper, not in Blade templates.veroeffentlichen or objektnr_extern to your frontend.modify, forget the relevant cache keys. Stale reference data is easy to introduce and hard to notice.How can I help you explore Laravel packages today?