Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Property Access Laravel Package

symfony/property-access

Symfony PropertyAccess lets you read and write values on objects and arrays using a simple property path string notation. It supports nested access, getters/setters, and array indexes, making data mapping and form handling easier.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require symfony/property-access
    

    For Laravel, prefer using Symfony’s components via symfony/finder or symfony/var-dumper packages if already in use.

  2. Basic Usage:

    use Symfony\Component\PropertyAccess\PropertyAccess;
    use Symfony\Component\PropertyAccess\PropertyPathInterface;
    
    $accessor = PropertyAccess::createPropertyAccessor();
    $data = ['user' => ['name' => 'John', 'address' => ['city' => 'Paris']]];
    
    // Read
    $city = $accessor->getValue($data, '[user][address][city]');
    // or with dot notation (for objects/arrays)
    $city = $accessor->getValue($data, 'user.address.city');
    
    // Write
    $accessor->setValue($data, 'user.address.city', 'Lyon');
    
  3. First Use Case: Transform an API request payload into a domain object:

    $payload = $request->json()->all();
    $user = new User();
    $accessor = PropertyAccess::createPropertyAccessor();
    
    $accessor->setValue($user, 'email', $payload['data']['user']['email']);
    $accessor->setValue($user, 'address.city', $payload['data']['user']['address']['city']);
    

Where to Look First

  • Official Documentation for API reference and path syntax.
  • PropertyPath class for advanced path manipulation (e.g., wildcards, filters).
  • PropertyAccessorInterface for method signatures and expected behavior.

Implementation Patterns

Core Workflows

1. Data Transformation

  • API Request/Response:
    $accessor = PropertyAccess::createPropertyAccessor();
    $domainObject = new Order();
    $accessor->setValue($domainObject, 'customer.name', $request->input('data.customer.name'));
    
  • Form Handling:
    $formData = $request->all();
    $user = $accessor->getValue($formData, 'user');
    $accessor->setValue($user, 'preferences.notifications.email', true);
    

2. Dynamic Property Access

  • Configurable Rules:
    $rulePath = 'user.tier.' . $tier . '.discount';
    $discount = $accessor->getValue($config, $rulePath);
    
  • A/B Testing:
    $variant = $accessor->getValue($user, 'experiment.variant.' . $experimentId);
    

3. Integration with Laravel

  • Service Provider Binding:
    $this->app->singleton('property.accessor', function () {
        return PropertyAccess::createPropertyAccessor();
    });
    
  • Facade for Cleaner Syntax:
    // app/Facades/PropertyAccess.php
    public static function get($object, string $path) {
        return app('property.accessor')->getValue($object, $path);
    }
    
    Usage:
    $city = PropertyAccess::get($user, 'address.city');
    

4. Validation and Serialization

  • Laravel Validator:
    $validator = Validator::make($data, [
        'user.address.city' => 'required|string',
    ], [
        'user.address.city.required' => 'City is required.',
    ]);
    
  • JSON:API Serialization:
    $serialized = $accessor->getValue($resource, '[data][attributes][name]');
    

Advanced Patterns

Custom Property Accessors

Extend PropertyAccessor to handle Laravel-specific logic:

use Symfony\Component\PropertyAccess\PropertyAccessor;

class LaravelPropertyAccessor extends PropertyAccessor {
    public function getValue($object, $propertyPath) {
        // Add Laravel-specific logic (e.g., Eloquent relationships)
        if (str_starts_with($propertyPath, 'relationship.')) {
            $relation = substr($propertyPath, 12);
            return $object->$relation;
        }
        return parent::getValue($object, $propertyPath);
    }
}

Path Builders

Create reusable path strings:

function buildUserPath(string $segment): string {
    return "user.{$segment}";
}
$email = $accessor->getValue($data, buildUserPath('email'));

Bulk Operations

Use PropertyPath for complex queries:

$path = PropertyPath::create('user[addresses][*][city]');
$cities = $accessor->getValue($data, $path);

Integration with Laravel Collections

$users = User::all();
$cities = $users->map(fn ($user) => PropertyAccess::get($user, 'address.city'));

Gotchas and Tips

Pitfalls

  1. Path Syntax Quirks:

    • Arrays: Use [key] notation ([user][address][city]).
    • Objects: Use dot notation (user.address.city).
    • Mixed Structures: Prefer dot notation for consistency; arrays will be traversed as keys.
    • Avoid: user.address[city] (invalid; use [user][address][city] for arrays).
  2. Null/Undefined Properties:

    • By default, accessing a non-existent path returns null. Use PropertyPath::create() with PropertyPath::WILDCARD or PropertyPath::EXISTS to check existence:
      if ($accessor->isReadable($data, 'user.address.city')) {
          $city = $accessor->getValue($data, 'user.address.city');
      }
      
  3. Getter/Setter Conflicts:

    • If a property and getter method share the same name (e.g., getName() and $name), the component prioritizes the property by default. To force method calls, use:
      $accessor->setMethodInvoker(function ($object, $method, array $arguments) {
          return $object->$method(...$arguments);
      });
      
  4. Performance Overhead:

    • Reflection Cache: The accessor caches reflection metadata, but repeated calls on the same object/property are still slower than direct access. Benchmark in hot paths (e.g., API loops).
    • Avoid in Loops: For performance-critical loops, cache the accessor or use direct property access:
      // Slow (avoid in loops)
      foreach ($users as $user) {
          $city = $accessor->getValue($user, 'address.city');
      }
      // Faster (if possible)
      foreach ($users as $user) {
          $city = $user->address->city; // Direct access
      }
      
  5. Security Risks:

    • Arbitrary Property Access: Allowing user-defined paths (e.g., $_GET['path']) can expose sensitive data. Validate paths strictly:
      $allowedPaths = ['user.name', 'user.email'];
      if (!in_array($path, $allowedPaths)) {
          abort(403);
      }
      
    • Method Invocation: Avoid paths that trigger methods with side effects (e.g., user.logout()).
  6. PHP 8.4+ Features:

    • Read-only Properties: The component respects readonly properties in PHP 8.2+.
    • Asymmetric Visibility: Works with public properties and private/protected getters/setters.

Debugging Tips

  1. Enable Debug Mode:

    $accessor = PropertyAccess::createPropertyAccessor();
    $accessor->setDebug(true); // Logs path resolution issues
    
  2. Validate Paths: Use PropertyPath::create() to validate syntax before access:

    try {
        $path = PropertyPath::create('user.address[city]');
        $value = $accessor->getValue($data, $path);
    } catch (\Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException $e) {
        // Handle invalid path
    }
    
  3. Check for Circular References: The component throws CircularReferenceException for recursive structures. Break cycles manually or use:

    $accessor->setCircularReferenceHandler(function ($path, $value) {
        return '[Circular Reference]';
    });
    
  4. Inspect Reflection Cache: For debugging, inspect the internal cache:

    $reflectionCache = $accessor->getReflectionCache();
    

Extension Points

  1. Custom Property Accessors: Implement PropertyAccessorInterface to override default behavior (e.g., Laravel-specific logic).

  2. Path Transformers: Use PropertyPath::create() with custom transformers for dynamic paths:

    $path = PropertyPath::create('user[addresses
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata