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

Marshaller Laravel Package

spiral/marshaller

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require spiral/marshaller
    

    Ensure your project uses PHP 8.1+.

  2. Basic Serialization: For a simple class like User with public properties, no annotations are needed:

    use Spiral\Marshaller\Marshaller;
    use Spiral\Marshaller\Mapper\AttributeMapperFactory;
    use Spiral\Attributes\AttributeReader;
    
    $marshaller = new Marshaller(new AttributeMapperFactory(new AttributeReader()));
    $user = new User('John', 'Doe');
    $array = $marshaller->marshal($user);
    
  3. First Use Case: Convert an Eloquent model to an array for API responses:

    $post = Post::find(1);
    $data = $marshaller->marshal($post);
    return response()->json($data);
    

Where to Look First

  • README.md: Focus on the "Usage" section for quick examples.
  • Marshaller class: Core interface for marshal() and unmarshal() methods.
  • #[Marshal] attribute: Key to customizing serialization behavior.

Implementation Patterns

Core Workflows

  1. Auto-Mapping Public Properties:

    class User {
        public function __construct(
            public string $name,
            public string $email
        ) {}
    }
    // Automatically maps to `['name' => 'John', 'email' => 'john@example.com']`.
    
  2. Customizing with Attributes:

    class User {
        #[Marshal(name: 'full_name')]
        private string $name;
    }
    // Outputs `['full_name' => 'John']` instead of `['name' => 'John']`.
    
  3. Nested Objects:

    class Address {
        public function __construct(
            public string $street,
            public string $city
        ) {}
    }
    
    class User {
        #[Marshal(of: Address::class)]
        public Address $address;
    }
    // Recursively serializes the `Address` object.
    
  4. Collections/Arrays:

    class User {
        #[MarshalArray(of: Post::class)]
        public array $posts;
    }
    // Serializes each `Post` in the array.
    
  5. Deserialization:

    $data = ['name' => 'John', 'email' => 'john@example.com'];
    $user = $marshaller->unmarshal($data, new User(...));
    

Integration Tips

  • Laravel HTTP Layer: Use unmarshal() to populate DTOs from request data:

    public function store(Request $request) {
        $dto = $marshaller->unmarshal($request->all(), new CreateUserDto());
        // Validate/process $dto.
    }
    
  • API Resources: Replace toArray() with marshal() for consistency:

    public function toArray($request) {
        return $this->marshaller->marshal($this->resource);
    }
    
  • Database Hydration: Unmarshal query results into collections:

    $users = User::all()->map(fn ($user) => $marshaller->marshal($user));
    
  • Event Payloads: Standardize event data shapes:

    event(new UserRegistered($marshaller->marshal($user)));
    

Advanced Patterns

  1. Custom Type Handlers: Extend TypeInterface for unsupported types (e.g., custom UUIDs):

    class UuidType implements TypeInterface {
        public function marshal($value): array { ... }
        public function unmarshal(array $data): UuidInterface { ... }
    }
    
  2. Decorators: Modify behavior globally via decorators:

    $marshaller->decorate(new IgnoreNullDecorator());
    
  3. Caching: Cache marshalled results for performance:

    $cacheKey = 'user:'.$user->id;
    $data = Cache::remember($cacheKey, now()->addHour(), fn () =>
        $marshaller->marshal($user)
    );
    

Gotchas and Tips

Pitfalls

  1. Private/Protected Properties:

    • Issue: Without #[Marshal], private/protected properties are ignored.
    • Fix: Explicitly annotate or use AttributeMapperFactory with reflection.
  2. Circular References:

    • Issue: v1.0.0 lacks built-in circular reference handling (e.g., UserPost bidirectional relationships).
    • Workaround: Use #[Marshal(ignore: true)] or implement a custom TypeInterface.
  3. Enum/Complex Types:

    • Issue: Enums or custom objects (e.g., UuidInterface) require explicit type hints:
      #[Marshal(of: Status::class, type: EnumType::class)]
      private Status $status;
      
    • Tip: Prefer EnumType for enums and ObjectType for custom classes.
  4. Array Keys:

    • Issue: Nested arrays may lose keys during unmarshaling.
    • Fix: Ensure array keys match the target object’s property names.
  5. Performance:

    • Issue: Reflection-based mapping can be slow for large objects.
    • Tip: Cache AttributeReader instances or use AttributeMapperFactory with pre-compiled metadata.

Debugging Tips

  1. Inspect Metadata: Use AttributeReader to debug annotations:

    $reader = new AttributeReader();
    $reflection = new ReflectionClass(User::class);
    $attributes = $reader->getAttributes($reflection->getProperty('name'));
    
  2. Validate Input Data: Always validate unmarshalled data:

    try {
        $user = $marshaller->unmarshal($data, new User(...));
    } catch (MarshallingException $e) {
        // Handle missing/invalid fields.
    }
    
  3. Test Edge Cases:

    • Null values.
    • Empty arrays/collections.
    • Nested objects with missing properties.

Extension Points

  1. Custom Attributes: Extend Marshal or create new attributes (e.g., #[Marshal(skip_if: fn ($val) => empty($val))]).

  2. Type System: Implement TypeInterface for unsupported types (e.g., Carbon, Money).

  3. Decorators: Modify marshaling behavior globally (e.g., add timestamps, filter fields):

    $marshaller->decorate(new AddTimestampsDecorator());
    
  4. Integration with Laravel: Bind Marshaller to the container:

    $app->singleton(Marshaller::class, fn () => new Marshaller(
        new AttributeMapperFactory(new AttributeReader())
    ));
    

Configuration Quirks

  1. AttributeReader:

    • Use Spiral\Attributes\AttributeReader (not Laravel’s Attribute facade) for consistency.
  2. Case Sensitivity:

    • Property names in arrays must match exactly (case-sensitive) with annotated names.
  3. Default Values:

    • Unmarshalled arrays may omit optional properties; ensure defaults are set in the constructor.

Pro Tips

  1. Pair with Laravel Validation: Use unmarshal() + ValidatesWhen for DTO validation:

    $dto = $marshaller->unmarshal($request->all(), new CreateUserDto());
    $this->validate($dto);
    
  2. API Versioning: Use decorators to version API responses:

    $marshaller->decorate(new V2ResponseDecorator());
    
  3. Document Annotations: Add PHPDoc to classes to clarify expected input/output shapes:

    /**
     * @property string $name
     * @property array{street: string, city: string} $address
     */
    class User { ... }
    
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.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle