Models: Accessors & Mutators
Introduction
Accessors and mutators allow you to modify attribute values when you retrieve or set them on model instances. If you'd ever used Laravel accessors or mutators, you'll feel right at home.
Accessors & Mutators
Defining An Accessor
For an example, lets say we are working with Active Directory and we
want to encode the thumbnailPhoto
attribute whenever we retrieve it
from our User
model.
To define an accessor for this attribute, we define a method named
getThumbnailphotoAttribute()
:
<?php
use LdapRecord\Models\Model;
class User extends Model
{
public function getThumbnailphotoAttribute(array $value): string
{
// Due to LDAP's multi-valued nature, all values will be
// contained inside of an array. We will attempt to
// retrieve the first one, or supply a default.
$data = $value[0] ?? file_get_contents('images/default_photo.jpg');
$image = base64_encode($data);
$mime = 'image/jpeg';
if (function_exists('finfo_open')) {
$finfo = finfo_open();
$mime = finfo_buffer($finfo, $data, FILEINFO_MIME_TYPE);
return "data:$mime;base64,$image";
}
return "data:$mime;base64,$image";
}
}
As you can see from the above, the attribute name we want to create
an accessor for, must be between get
and Attribute
.
The casing of
get
andAttribute
are very important. This casing difference is how LdapRecord detects accessor and mutator methods.
If your attribute contains a hyphen, use must use alternate casing
to indicate this. For example, lets create an accessor for the
apple-user-homeurl
attribute:
<?php
use LdapRecord\Models\Model;
class User extends Model
{
public function getAppleUserHomeurlAttribute(array $value): mixed
{
// Do something with its value.
return $value;
}
}
As you can see, alternate casing indicates to LdapRecord that the attribute we are looking for contains hyphens.
Defining A Mutator
A mutator does the opposite of an accessor. A mutator is a function you define that accepts the value of the attribute you are setting so you can transform it before it is set onto the model.
To define a mutator, we use the above accessor syntax with set
instead of get
.
For example, let's define a unicodepwd
mutator that automatically
encodes a password by setting the attribute:
<?php
use LdapRecord\Utilities;
use LdapRecord\Models\Model;
class User extends Model
{
public function setUnicodepwdAttribute(string $password): void
{
$this->attributes['unicodepwd'] = [Utilities::encodePassword($password)];
}
}
Now once we set the attribute, it will automatically encode the
password we are setting on the User
model:
$user = new User();
$user->unicodepwd = 'secret';
Date Mutators
By default, LdapRecord will convert the attributes createtimestamp
and
modifytimestamp
to instances of Carbon.
If you extend from
ActiveDirectory
models, the attributeswhenchanged
andwhencreated
will be converted instead.
When you define an attribute as a date, you can set its value to an
instance of DateTime
/ Carbon
instance, a UNIX timestamp, or
a date string (Y-m-d
). Upon saving your model, these will
be converted properly to be stored in your directory.
To define a mutator for an attribute that contains a timestamp,
we must set the $dates
property on the model. However, since
LDAP directories have different timestamp formats for
some attributes, we must tell LdapRecord what kind
of format to use for proper conversion.
For example, let's define a date mutator for the accountexpires
attribute that exists on Active Directory. To do so, we must set
the $dates
property to a key / value pair, where the key is
the attribute that contains the timestamp and the value is
the type of LDAP format to convert to and from:
<?php
use LdapRecord\Models\Model;
class User extends Model
{
protected array $dates = [
'accountexpires' => 'windows-int',
];
}
Now lets have our user's account expire at the same time tomorrow:
$user = User::find('cn=John Doe,dc=local,dc=com');
$user->accountexpires = new \DateTime('+1 day');
$user->save();
Once we've saved the model, the attribute will now automatically be converted to a Carbon instance so you can use any of Carbon's methods on the attribute:
$user = User::find('cn=John Doe,dc=local,dc=com');
if ($user->accountexpires->isPast()) {
// The user account is expired.
}
Available Types
Currently, there are 3 built-in date mutator types. They are:
ldap
windows
windows-int
LDAP Type
The ldap
type is the most common format for LDAP timestamps -
outside of Active Directory. This format converts LDAP timestamps
in the format of YYYYMMDDHHMMSST
. T is the time zone which
is usually 'Z' (Zulu Time Zone = UTC/GMT).
Windows Type
The windows
type is similar to the ldap
type, however it
differs slightly so it requires its own conversion type. Its
timestamp is in the format of YYYYMMDDHHMMSS.0T
. T is the
time zone which is usually 'Z' (Zulu Time Zone = UTC/GMT).
Windows Integer Type
The windows-int
type handles the 18-digit Active Directory timestamp
format, also named 'Windows NT time format', 'Win32 FILETIME or
SYSTEMTIME' or NTFS file time. An example of this would be
the accountexpires
attribute that exists on users:
132131246410000000
Which equals:
Monday, September 16, 2019 4:24:01 PM
Attribute Casting
Similarly with Laravel's Eloquent, the $casts
property on your model provides
a convenient method of converting attributes to common data types. The $casts
property should be an array where the key is the name of the attribute being
cast and the value is the type you wish to cast the column to.
The supported cast types are:
integer
real
float
double
decimal:<digits>
string
boolean
object
array
collection
datetime:<ldap/windows/windows-int>
To demonstrate attribute casting, let's cast the msExchHideFromAddressList
Active Directory attribute,
which determines whether a user account is shown in the Global Address List in Outlook.
This attribute is stored as a string in Active Directory, with the value TRUE
or FALSE
.
namespace App\Models\Ldap;
use LdapRecord\Models\ActiveDirectory\User as BaseUser;
class User extends BaseUser
{
protected array $casts = [
'msExchHideFromAddressList' => 'boolean',
];
}
Then, we can utilize it when we retrieve users from our directory:
$user = User::find('cn=John Doe,dc=local,dc=com');
if ($user->msExchHideFromAddressList) {
// This user is being hidden from the Global Address list.
}
Appending Accessors
Ported directly from Laravel's Eloquent, the $appends
array
property can be set directly on the model class to add an
accessor's value to the models array form.
Important: LDAP attributes cannot contain underscores (
_
). Therefore, all accessors that are PascalCased must be defined in their hyphenated format, and will appear in their hyphenated format in the model's array form.
namespace App\Models\Ldap;
use LdapRecord\Models\ActiveDirectory\User as BaseUser;
class User extends BaseUser
{
protected array $appends = ['full-name'];
public function getFullNameAttribute(): string
{
return 'John Doe';
}
}
$user = User::find('cn=john,dc=local,dc=com');
// Displays: "John Doe"
echo $user->full_name;
// Displays: "{"full-name":["John Doe"]}"
echo json_encode($user);