wmds/mindbodyonline-api-bundle
Symfony bundle that integrates the Mindbody Online API into your PHP application, offering ready-made services and configuration for booking, scheduling, and client data access. Simplifies authentication and API calls so you can connect to Mindbody faster.
This bundle is a thin Symfony 2 wrapper around the MindBody SOAP API (Class, Client, Appointment, Sale, Staff, Site services). It predates Symfony Flex, autowiring, and the MindBody REST API, so treat it as glue code you will likely extend rather than a batteries-included integration.
Install and register
composer require wmds/mindbodyonline-api-bundle
// app/AppKernel.php
public function registerBundles()
{
$bundles = [
// ...
new WMDS\MindBodyAPIBundle\WMDSMindBodyAPIBundle(),
];
}
Configure credentials in app/config/config.yml (verify exact keys against DependencyInjection/Configuration.php before committing to them):
wmds_mind_body_api:
api_key: %mindbody_api_key%
site_id: %mindbody_site_id%
source_name: %mindbody_source_name%
source_password: %mindbody_source_password%
Keep the values in parameters.yml / environment-specific config. MindBody has two credential pairs — the API key (developer/source) and site credentials (staff user/password for a studio) — and mixing them up is the most common first-day failure.
Find the service ID. Open Resources/config/services.xml (and DependencyInjection/) first. The bundle exposes either a single facade (commonly mindbody.api or similar) or one service per MindBody service. Also read the README and any Tests/ fixtures; with 4 stars there is minimal documentation, so the service definition is the source of truth.
First useful call — pull a week of classes and confirm the round-trip works before wiring anything into your domain:
$api = $this->get('mindbody.api'); // check services.xml for the real id
$classes = $api->getClasses([
'StartDateTime' => new \DateTime('today'),
'EndDateTime' => new \DateTime('+7 days'),
'PageSize' => 100,
]);
If you are on a modern Symfony, plan to install this only as a reference: copy the request/response plumbing into a small client of your own (Guzzle/SOAP + DTOs) rather than fighting Symfony 2 bundle conventions.
Wrap the bundle in your own service. Never let controllers call the MindBody service directly. Create AppBundle\MindBody\MindBodyGateway that injects the bundle service, owns pagination, retries, and mapping. This isolates the (likely abandoned) dependency behind an interface you can swap for the REST API later.
Separate read models from write operations. For day-to-day work the split is almost always:
Booking flow pattern — the most common integration shape:
public function bookClass($clientId, $classId)
{
$client = $this->gateway->getClient($clientId);
$class = $this->gateway->getClass($classId, ['StaffCredentials' => ...]);
if ($class->isCancelled() || $class->getSpotsOpen() < 1) {
throw new BookingUnavailable('Class full or cancelled');
}
$result = $this->gateway->addClientToClass($clientId, $classId);
$this->repository->recordBooking($clientId, $classId, $result->getId());
}
Always re-read availability immediately before booking; the schedule cache is for display only.
Sync via console commands. Nightly or hourly commands (app/console mindbody:sync-classes, mindbody:sync-clients) keep local tables warm. Use PageIndex/PageSize with an explicit ceiling and a while loop — MindBody defaults to small page sizes, and an unpaginated call silently returns only the first page.
DTO mapping layer. SOAP responses come back as nested stdClass/arrays with inconsistent casing (ClassScheduleID vs ClassScheduleId). Map once into typed objects in a single ResponseMapper; every other layer should be free of MindBody-shaped data. This is also where you normalise datetimes to the site's timezone and money to integer cents.
Testing. Because the bundle calls out over SOAP, mock your own gateway interface in tests. For an integration smoke test, only run against the MindBody sandbox credentials and mark it @group integration.
Event hooks for side effects. Dispatch your own domain events (ClientAddedEvent, ClassBookedEvent) after a successful MindBody call so email/Slack/ERP listeners stay decoupled from SOAP.
ext-soap. WSDL caching can make schema changes look stale — disable soap.wsdl_cache in dev or clear the cache after deploy.401/AccessDenied is more often the wrong source than a wrong API key.GetClasses, GetClients, GetAppointments all paginate. Looping until an empty page is the only safe pattern.SoapFault thrown by the transport) and MindBody's own Status/ErrorCode in the response body. Wrap both in one exception type in your gateway.ClientSync queue) rather than looping in a request cycle.app/config/services.yml or decorate the bundle's service with your own; add compiler passes to inject a custom HTTP/SOAP client. This is the intended way to fix behaviour without patching vendor code.composer.json, vendor the source if necessary, and budget for migrating to the MindBody REST API (v6) or a maintained client.How can I help you explore Laravel packages today?