directorytree/ldaprecord
LDAPRecord is a fully featured LDAP and Active Directory ORM for Laravel and PHP. It provides Eloquent-style models, querying, authentication and user sync, and tools for working with directory entries, connections, and schema—built for reliable, modern LDAP apps.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require directorytree/ldaprecord
For Laravel integration:
composer require directorytree/ldaprecord-laravel
Configuration: Publish the config file:
php artisan vendor:publish --provider="DirectoryTree\LdapRecord\Laravel\LdapRecordServiceProvider"
Update config/ldaprecord.php with your LDAP server details (host, port, base DN, etc.).
First Use Case:
Define an LDAP model (e.g., User.php):
use DirectoryTree\LdapRecord\Laravel\Models\Model;
class User extends Model
{
protected $connection = 'ldap'; // Matches config key
protected $dn = 'ou=users,dc=example,dc=com';
protected $attributes = [
'uid', 'cn', 'mail', 'userPassword'
];
}
Query users:
$users = User::where('uid', 'john.doe')->get();
Model Definition:
Extend DirectoryTree\LdapRecord\Laravel\Models\Model and define:
$connection: Config key for LDAP connection.$dn: Base DN for the model.$attributes: LDAP attributes to map to model properties.$casts: Attribute casting (e.g., timestamps, booleans).Example:
class Group extends Model
{
protected $connection = 'ad';
protected $dn = 'ou=groups';
protected $attributes = ['cn', 'member', 'description'];
protected $casts = [
'member' => 'array',
'description' => 'string'
];
}
Querying: Use familiar Laravel-like syntax:
// Basic query
$users = User::where('mail', 'like', '%@example.com')->get();
// Complex filters
$activeUsers = User::where(function ($query) {
$query->where('accountStatus', 'active')
->orWhere('lastLogin', '>', now()->subDays(30));
})->get();
// Active Directory-specific
$adUsers = User::whereMemberOf('cn=Admins,ou=groups')->get();
CRUD Operations:
// Create
$user = User::create([
'uid' => 'jane.doe',
'cn' => 'Jane Doe',
'mail' => 'jane@example.com',
'userPassword' => 'securepassword'
]);
// Update
$user->mail = 'jane.doe@example.com';
$user->save();
// Delete
$user->delete();
Relationships:
Define relationships using hasMany, belongsTo, etc.:
class User extends Model
{
public function groups()
{
return $this->belongsToMany(Group::class, 'member', 'uid', 'cn');
}
}
Events & Observers: Use Laravel’s observer pattern for LDAP events:
// app/Observers/UserObserver.php
class UserObserver
{
public function saved(User $user)
{
Log::info("User created: {$user->uid}");
}
}
Register in AppServiceProvider:
User::observe(UserObserver::class);
Authentication: Integrate with Laravel’s auth:
use DirectoryTree\LdapRecord\Laravel\Auth\CanAuthenticate;
class User extends Model implements CanAuthenticate
{
// ...
}
Configure AuthServiceProvider:
public function boot()
{
$this->app['auth']->provider('ldap', function ($app) {
return new LdapProvider($app['auth']->createUserProvider('eloquent'));
});
}
Chunking & Pagination: Handle large result sets:
User::chunk(100, function ($users) {
foreach ($users as $user) {
// Process batch
}
});
Transactions: Wrap LDAP operations in transactions (where supported):
DB::transaction(function () {
$user = User::create([...]);
$user->groups()->attach([...]);
});
Laravel Service Container: Bind custom LDAP connections:
$this->app->bind('ldap.ad', function () {
return new Connection([
'host' => 'ad.example.com',
'port' => 636,
'use_ssl' => true,
'base_dn' => 'dc=example,dc=com',
]);
});
Caching: Cache frequent queries:
$users = Cache::remember('active_users', now()->addHours(1), function () {
return User::where('accountStatus', 'active')->get();
});
Testing:
Use DirectoryFake for unit tests:
use DirectoryTree\LdapRecord\Testing\DirectoryFake;
public function test_user_creation()
{
DirectoryFake::fake();
$user = User::create([...]);
$this->assertEquals('john.doe', $user->uid);
DirectoryFake::assertCreated('ou=users', $user->toArray());
}
Debugging:
Enable debug logging in config/ldaprecord.php:
'debug' => [
'enabled' => env('LDAP_DEBUG', false),
'level' => Ldap::DEBUG_ANY,
],
Base DN Substitution:
{base} is used in queries when needed (e.g., whereMemberOf('cn=Admins,{base}')).{base} substitution.Attribute Casting:
binaryGUID, objectSid). Use explicit casts:
protected $casts = [
'binaryGUID' => 'binary',
'objectSid' => 'binary',
];
TLS/SSL:
ldap:// without TLS, ensure allow_insecure_password_changes is configured in config/ldaprecord.php:
'allow_insecure_password_changes' => env('LDAP_ALLOW_INSECURE_CHANGES', false),
Empty whereIn Queries:
whereIn() will return no results (fixed in v3.8.5). Handle edge cases:
$ids = $request->input('ids', []);
if (!empty($ids)) {
$users = User::whereIn('uid', $ids)->get();
}
Timestamp Handling:
ldap timestamp type:
protected $casts = [
'lastLogin' => 'ldap:timestamp',
];
Model Morphing:
protected $morphMap = [
'person' => User::class,
'group' => Group::class,
];
Connection Pooling:
LdapRecord\ConnectionPool.Debugging Complex Filters:
Ldap::DEBUG_FILTER to log generated LDAP filters:
config(['ldaprecord.debug.level' => Ldap::DEBUG_FILTER]);
Enable Debugging:
// In a route or command
\DirectoryTree\LdapRecord\Ldap::setDebugLevel(\DirectoryTree\LdapRecord\Ldap::DEBUG_ANY);
Log Raw Queries: Use a custom logger to inspect queries:
\Log::debug('LDAP Query', [
'query' => $query->toLdap(),
'params' => $query->getBindings(),
]);
Handle Exceptions:
Catch DirectoryTree\LdapRecord\Exceptions\LdapException for LDAP-specific errors:
try {
$user = User::find('nonexistent');
} catch (\DirectoryTree\LdapRecord\Exceptions\LdapException $e) {
Log::error("LDAP Error: {$e->getErrorMessage()}");
}
How can I help you explore Laravel packages today?