iter8/ldaprecord-bundle
A Symfony bundle that integrates LdapRecord for easy LDAP authentication and directory queries, with ActiveRecord-style models, fluent querying, and simple configuration for connecting to LDAP servers.
Check your platform: v4.0 requires PHP 8.1+ and Symfony 6.1+. Install:
composer require iter8/ldaprecord-bundle:^4.0
If you register manually, note the bundle class namespace changed in v4:
// config/bundles.php
// Old (v3.x): Iter8\Bundle\LdapRecordBundle\Iter8LdapRecordBundle::class => ['all' => true],
// New (v4.x):
Iter8\LdapRecordBundle\Iter8LdapRecordBundle::class => ['all' => true],
Flex should handle this for new installs; existing projects upgrading from v3 must update the entry.
Open the bundle’s DependencyInjection/Configuration.php and README.md first. It is still a thin Symfony wrapper around LdapRecord, so the important details are the config root key, supported connection options, and whether LdapRecord\Container is registered as a service. v4 ships against LdapRecord v3 — skim LdapRecord’s upgrade guide if you are porting code from v2.
Configure at least one connection. The config tree generally mirrors LdapRecord’s native connection array:
# config/packages/ldaprecord.yaml
ldaprecord:
connections:
default:
hosts: ['%env(LDAP_HOST)%']
base_dn: '%env(LDAP_BASE_DN)%'
username: '%env(LDAP_BIND_DN)%'
password: '%env(LDAP_BIND_PASSWORD)%'
port: 389
use_tls: true
Confirm the real root key with:
bin/console config:dump-reference ldaprecord
bin/console debug:config ldaprecord
First use case: define a model and query the directory. The bundle’s main value is that it bootstraps LdapRecord’s container/connections from Symfony config, so model queries work without manual connection setup:
namespace App\Ldap;
use LdapRecord\Models\Model;
class User extends Model
{
protected $connection = 'default';
protected $objectClasses = [
'top',
'person',
'organizationalPerson',
'inetOrgPerson',
];
}
$user = \App\Ldap\User::where('mail', '=', $email)->first();
if ($user) {
$displayName = $user->getFirstAttribute('displayName');
}
Bind models to explicit connections. If you have more than one directory, name connections clearly (default, ad, openldap) and set $connection on each model. Do not rely on implicit ordering or assume the bundle’s “default” matches your naming.
class AdUser extends Model
{
protected $connection = 'ad';
protected $objectClasses = ['top', 'person', 'organizationalPerson', 'user'];
}
Use an authenticator/service layer rather than putting LDAP calls in controllers. A Symfony authenticator can validate credentials against LdapRecord and then load or provision a local user:
use LdapRecord\Container;
$connection = Container::getConnection('default');
if (!$connection->auth()->attempt($username, $password)) {
throw new BadCredentialsException('Invalid LDAP credentials.');
}
$entry = \App\Ldap\User::where('uid', '=', $username)->firstOrFail();
Sync directory attributes into local entities on login or via a command. LDAP is usually the identity source, not the application database. Map a small, explicit set of attributes (mail, displayName, uid, memberOf) and keep the mapping in one service.
Use LdapRecord’s query builder instead of raw filters. Prefer where(), first(), get(), and model methods. If you must build raw filters, escape user input carefully to avoid LDAP filter injection. The query-builder surface is stable between LdapRecord v2 and v3, so most common patterns port cleanly.
Handle large directories with pagination/chunking. LDAP servers often cap results (commonly 1000 entries). For imports, iterate in pages or chunks rather than get() on a broad search.
Keep directory-specific behavior in subclasses. Active Directory and OpenLDAP differ in attributes, object classes, and authentication semantics. Use separate models or a shared base model plus directory-specific subclasses.
Test with a fake connection or thin adapter. Isolate LDAP behind your own service interface, or swap in LdapRecord’s testing/fake connection where available. This keeps unit tests fast and avoids requiring a live directory in CI.
Use console commands for synchronization. Long-running imports belong in Symfony commands, with progress output, batching, and idempotent upserts.
Iter8\LdapRecordBundle\Iter8LdapRecordBundle (previously Iter8\Bundle\LdapRecordBundle\Iter8LdapRecordBundle). Any code, imports, or config/bundles.php entries referencing the old FQCN will break at container build. Grep for Iter8\\Bundle\\LdapRecordBundle when upgrading.Configuration.php rather than copying examples from Laravel. Use config:dump-reference and debug:config.default. If your bundle config names it something else, set the model’s $connection explicitly or configure the default alias.%env()% for bind DN/password. Watch quoting and special characters in passwords; test with a real ldapsearch/ldaps connection first.use_tls and use_ssl are not interchangeable. Verify port, certificate trust, and whether your server requires StartTLS. Test outside Symfony before debugging bundle wiring.getFirstAttribute() when you expect a scalar, and handle missing attributes defensively.LdapRecord\Container/connections directly in services.yaml.^4.0, watch for follow-up patches, and keep the option of wiring LdapRecord directly in services.yaml if the bundle lags again.How can I help you explore Laravel packages today?