justbetter/laravel-dynamics-client
A Laravel package that provides a fluent client for Microsoft Dynamics, making it easy to query, create and update records from your app. Includes configurable connections, authentication and a clean, Eloquent-like interface for working with Dynamics entities.
composer require justbetter/laravel-dynamics-client and publish the config: php artisan vendor:publish --tag=dynamics-config. Everything is driven from config/dynamics.php, so open that file first — it defines the named connections (client id/secret, tenant, environment, company, base URL) that every other call hangs off..env rather than in the config file. You need an Azure AD app registration with client-credentials access to the Dynamics 365 Business Central API, plus the target environment and company name. If the company name has spaces or punctuation, verify early that it resolves — that is a common first-run failure.php artisan tinker is a parameterless read against a well-known endpoint (e.g. customers or companies). If you get a token and a JSON collection back, auth and routing are correct; if you get a 401 the credentials/tenant are wrong, if you get a 404 the environment or company segment is wrong.DynamicsRepository/service per resource (CustomerRepository, ItemRepository) that owns the endpoint name and the field mapping. Call sites then depend on your class, which you can fake in tests without booting HTTP.production, sandbox, and per-entity-company connections explicitly rather than relying on the default. This makes it impossible for a queued job to write to the wrong environment because an env var was stale.php artisan dynamics:sync-customers on a schedule, upsert into Eloquent models keyed by the Dynamics primary key, and keep a synced_at column. Reconcile with incremental filters (modifiedOn gt <last sync>) instead of full pulls once volumes grow.$tries, backoff(), and failed() handling. Dynamics will rate-limit and occasionally 5xx; a synchronous write in an HTTP request is a bad experience waiting to happen. Log the Dynamics record id/etag on success and store it on your local record for future updates.@odata.nextLink means "get all" is a loop. Encapsulate it in one cursor()/each() helper in your repository and use $top/$skip deliberately; never let a controller call an unbounded fetch.Http::fake() (or the package's provided fake/binding) with captured fixtures for a list response, an empty response, and an OData error payload. Assert your mapping and your error handling separately — the second is what breaks in production.{}) as the body. Some endpoints, proxies, and gateways reject that. The fix is a patch-level change with no API impact, so upgrading from 2.0.0 is safe — but note it changes what goes over the wire, so if you have HTTP-level tests asserting on an empty body, update the fixtures.name eq 'O''Brien'). Dates want ISO-8601 without timezone surprises. Build filters through a small helper rather than string concatenation at call sites — this is the number-one source of silent "zero results" bugs.My Company Ltd must be encoded in the path segment. When something returns 404 that works in Postman, check encoding before checking permissions.Retry-After, keep concurrency low, and don't parallelise blindly — sequential with a small delay often finishes faster than a burst that gets throttled.Illuminate\Http\Client\Events) to log method, URL, status, and the Dynamics correlation/request id. The request id is what Microsoft support asks for, and it is not in your application logs by default. Redact the Authorization header.How can I help you explore Laravel packages today?