alexanevsky/getter-setter-accessor-bundle
composer require alexanevsky/getter-setter-accessor-bundle
config/bundles.php:
Alexanevsky\GetterSetterAccessorBundle\GetterSetterAccessorBundle::class => ['all' => true],
GetterSetterAccessor into a controller or service:
use Alexanevsky\GetterSetterAccessorBundle\GetterSetterAccessor;
public function __construct(private GetterSetterAccessor $accessor) {}
public function updateProfile(Request $request, User $user) {
$accessor = $this->accessor->createAccessor($user);
$accessor->setValue('userName', $request->input('name')); // Set via snake/camel case
return response()->json(['status' => 'updated']);
}
Dynamic Property Access:
Use getValue()/setValue() for runtime property manipulation without direct reflection:
$value = $accessor->getValue('userEmail'); // Works for both snake_case and camelCase
$accessor->setValue('user_email', 'new@example.com');
Validation Before Access: Check getter/setter existence before operations:
if ($accessor->hasGetter('profilePicture')) {
$image = $accessor->getValue('profilePicture');
}
Bulk Property Inspection: Iterate over available getters to build forms or APIs:
foreach ($accessor->getGetters() as $getter) {
echo sprintf(
'Field: %s, Type: %s, Nullable: %s',
$getter->getName(),
implode(', ', $getter->getTypes()),
$getter->isNullable() ? 'Yes' : 'No'
);
}
Type-Safe Operations:
Use getTypes() to validate input before setting values:
$types = $accessor->getGetter('age')->getTypes();
if (!in_array('int', $types)) {
throw new \InvalidArgumentException('Age must be an integer');
}
Integration with Forms: Dynamically generate form fields from model getters:
$fields = collect($accessor->getGetters())
->map(fn($getter) => [
'name' => $getter->getName(),
'label' => ucwords(str_replace('_', ' ', $getter->getName())),
'type' => $getter->isNullable() ? 'text' : 'required_text',
]);
Case Sensitivity:
snake_case and camelCase, but mixed cases (e.g., userName vs user_name) must be consistent. Test both formats if unsure.hasGetter('userName') to verify the correct format before access.Non-Public Properties:
Circular References:
$accessor->setValue('address.city', 'NY')) may fail if the intermediate object isn’t initialized.Performance:
getGetters() reflects the entire object. Cache results if called frequently:
private $gettersCache;
public function getGetters() {
return $this->gettersCache ??= $this->accessor->getGetters();
}
Symfony-Specific Quirks:
@Assert annotations don’t interfere with dynamic access.@Assert\Type constraints to avoid runtime errors.Inspect Available Getters: Dump the getter list to debug missing properties:
dd($accessor->getGetters()->map(fn($g) => $g->getName()));
Handle Exceptions:
Wrap getValue()/setValue() in try-catch for missing methods:
try {
$accessor->setValue('invalidProperty', 'value');
} catch (\RuntimeException $e) {
// Log or handle gracefully
}
Attribute Introspection:
Use getAttribute() to debug custom attributes (e.g., from Symfony UX):
if ($accessor->getGetter('email')->hasAttribute(\Symfony\UX\Attribute\Email::class)) {
// Handle email-specific logic
}
Custom Accessor Logic:
Extend the base AccessorInterface to add logic (e.g., default values):
class CustomAccessor implements AccessorInterface {
public function getValue(string $property) {
return $this->accessor->getValue($property) ?: 'default_value';
}
}
Event Listeners:
Hook into setter operations via Symfony events (e.g., kernel.request):
# config/services.yaml
services:
App\EventListener\PropertySetterListener:
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
Testing: Mock the accessor in tests to avoid reflection:
$mockAccessor = $this->createMock(GetterSetterAccessor::class);
$mockAccessor->method('getValue')->willReturn('test_value');
Laravel-Specific:
public function rules() {
$getters = $this->accessor->getGetters();
return collect($getters)
->filter(fn($g) => !$g->isNullable())
->pluck('name')
->mapWithKeys(fn($name) => [$name => 'required'])
->toArray();
}
How can I help you explore Laravel packages today?