symfony/property-access
Symfony PropertyAccess lets you read and write values on objects and arrays using a simple property-path string syntax. Supports nested properties, indexed access, getters/setters, and safe navigation for flexible data mapping and forms.
Installation:
composer require symfony/property-access
Add to composer.json if using Laravel Mix or Vite (though this is a PHP package).
First Use Case: Access nested properties in an object or array using a dot-notation path.
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
// Create an accessor instance
$accessor = PropertyAccess::createPropertyAccessor();
// Example object
$user = new class {
public $name = 'John';
public $profile = [
'address' => [
'street' => '123 Main St',
'city' => 'Metropolis'
]
];
};
// Read a nested value
$city = $accessor->getValue($user, 'profile.address.city');
// Returns: 'Metropolis'
// Write a nested value
$accessor->setValue($user, 'profile.address.city', 'Gotham');
Where to Look First:
PropertyAccess class methods: getValue(), setValue(), isWritable(), isReadable().Use getValue() to traverse objects/arrays with dot notation.
$accessor = PropertyAccess::createPropertyAccessor();
$value = $accessor->getValue($entity, 'path.to.nested.property');
Common Use Cases:
$data = $request->all();
$city = $accessor->getValue($data, 'user.address.city');
$dto = new UserDto();
$accessor->setValue($dto, 'profile.email', $arrayData['email']);
$responseData['user']['address']['city'] = $accessor->getValue($user, 'address.city');
Use setValue() to update nested properties.
$accessor->setValue($object, 'path.to.property', $newValue);
Example: Dynamic Updates
$rules = ['user.address.city' => 'required|string'];
foreach ($rules as $path => $validation) {
$accessor->setValue($formData, $path, $request->input(explode('.', $path)));
}
Service Container Binding:
$this->app->singleton(PropertyAccessor::class, function () {
return PropertyAccess::createPropertyAccessor();
});
Then inject PropertyAccessor into controllers/services.
Form Request Validation:
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
public function rules()
{
return [
'user.address.city' => 'required|string',
];
}
public function withValidator($validator)
{
$accessor = app(PropertyAccessorInterface::class);
$validator->after(function ($validator) use ($accessor) {
$data = $validator->getData();
$accessor->setValue($data, 'user.address.city', strtoupper($data['user']['address']['city']));
});
}
The package automatically handles both:
$array = ['user' => ['name' => 'Alice']];
$accessor->getValue($array, 'user.name'); // 'Alice'
$object = new stdClass();
$object->user = (object)['name' => 'Bob'];
$accessor->getValue($object, 'user.name'); // 'Bob'
Extend functionality with custom type hints or logic:
$accessor = PropertyAccess::createPropertyAccessorBuilder()
->enableExceptionOnInvalidIndex()
->enableMagicAccessor()
->getPropertyAccessor();
Performance Optimization:
PropertyAccessor instance as a singleton in Laravel’s service container.PropertyPath:
$path = PropertyPath::fromString('user.profile.address');
$accessor->getValue($data, $path);
Error Handling:
$accessor = PropertyAccess::createPropertyAccessorBuilder()
->setExceptionOnInvalidIndex(true) // Throw \InvalidArgumentException
->setExceptionOnInvalidPropertyPath(true)
->getPropertyAccessor();
isReadable()/isWritable() to check path validity before access:
if ($accessor->isReadable($data, 'user.address.city')) {
$city = $accessor->getValue($data, 'user.address.city');
}
Laravel-Specific Patterns:
$user = User::find(1);
$city = $accessor->getValue($user, 'address.city');
public function toArray($request)
{
return [
'address' => [
'city' => $this->accessor->getValue($this->resource, 'address.city'),
],
];
}
Testing:
PropertyAccessor in unit tests:
$mockAccessor = $this->createMock(PropertyAccessorInterface::class);
$mockAccessor->method('getValue')->with($obj, 'path')->willReturn('value');
Getter/Setter Ambiguity:
$accessor = PropertyAccess::createPropertyAccessorBuilder()
->getPropertyAccessor();
// Defaults to properties > getters for same-named access.
Circular References:
A->B->A) may cause infinite loops.enableExceptionOnCircularReference() or handle manually.PHP 8.4+ Asymmetric Visibility:
public getName(): string).^6.4 for PHP 7.4).Array vs. Object Behavior:
array[0]), while objects use property/method access.['key' => (object)[]]) may behave unexpectedly.Performance Overhead:
PropertyAccessor instance.$path = PropertyPath::fromString('user.address.city');
foreach ($users as $user) {
$accessor->getValue($user, $path); // Faster than string paths
}
Case Sensitivity:
user.Name ≠ user.name).PropertyPath::fromString() with custom normalization.Enable Debug Mode:
PropertyAccess::DEBUG to log access attempts:
define('PropertyAccess::DEBUG', true);
Validate Paths:
isReadable()/isWritable() to debug invalid paths:
if (!$accessor->isReadable($data, 'user.address.city')) {
dd('Invalid path: user.address.city');
}
Common Errors:
\InvalidArgumentException: Invalid path or missing property.
\RuntimeException: Circular references or unsupported types.
enableExceptionOnCircularReference() to catch these early.How can I help you explore Laravel packages today?