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

Output Normalizer Bundle Laravel Package

alexanevsky/output-normalizer-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use

  1. Install the Package Add the package to your Laravel project via Composer:

    composer require alexanevsky/output-normalizer-bundle
    

    Register the bundle in config/app.php under providers (if not auto-discovered).

  2. Define an Output Class Create a class implementing OutputInterface to define the output structure. Example:

    use Alexanevsky\OutputNormalizerBundle\Output\OutputInterface;
    
    class UserOutput implements OutputInterface {
        public int $id;
        public string $name;
    }
    
  3. Inject the Normalizer Add the OutputNormalizer to your controller/service constructor:

    use Alexanevsky\OutputNormalizerBundle\OutputNormalizer;
    
    public function __construct(private OutputNormalizer $outputNormalizer) {}
    
  4. Normalize an Entity Call normalize() with your entity and output class:

    $user = new User();
    $output = $this->outputNormalizer->normalize($user, UserOutput::class);
    // Returns: ['id' => 1, 'name' => 'John Doe']
    

First Use Case: API Response Transformation

Use the normalizer to transform Eloquent models into API responses with only the required fields, avoiding bloated JSON:

public function show(User $user) {
    $output = $this->outputNormalizer->normalize($user, UserOutput::class);
    return response()->json($output);
}

Implementation Patterns

Workflow: Normalizing Eloquent Models

  1. Define Output Classes Create OutputInterface classes for each Eloquent model (e.g., UserOutput, PostOutput). Use public properties or getters/setters to control output.

  2. Leverage Getters for Transformation Use getters in output classes to modify values during normalization:

    class UserOutput implements OutputInterface {
        public string $email;
    
        public function getEmail(): string {
            return strtolower($this->email);
        }
    }
    
  3. Handle Relationships Normalize nested objects by defining output classes for related models (e.g., PostOutput for Post model). Use EntityToId for identifiers:

    class UserOutput implements OutputInterface {
        #[EntityToId]
        public User $author;
    }
    
  4. Global Normalization Rules Implement ObjectNormalizerInterface for reusable normalization logic (e.g., formatting dates or hashing sensitive fields):

    class DateNormalizer implements ObjectNormalizerInterface {
        public function supports(object $object): bool {
            return $object instanceof \DateTime;
        }
        public function normalize(object $object): string {
            return $object->format('Y-m-d');
        }
    }
    
  5. Modify Output Dynamically Use OutputModifierInterface to filter or transform data post-normalization (e.g., removing sensitive fields):

    class UserOutputModifier implements OutputModifierInterface {
        public function supports(object $output, object $source): bool {
            return $output instanceof UserOutput;
        }
        public function modify(OutputInterface $output, object $source): void {
            unset($output->password);
        }
    }
    

Integration Tips

  • API Resources: Replace Laravel’s ApiResource with this package for fine-grained control over serialization.
  • DTOs: Use output classes as Data Transfer Objects (DTOs) for consistent API responses.
  • Testing: Mock OutputNormalizer in unit tests to isolate business logic from serialization concerns.

Gotchas and Tips

Pitfalls

  1. Getter/Setter Precedence If a property has both a getter and a public field, the getter’s value is used. Override properties with getters to control output:

    // Avoids exposing private $password directly
    public function getPassword(): string {
        return '*****';
    }
    
  2. Circular References Normalizing objects with circular references (e.g., UserPost) may cause infinite loops. Use EntityToId or break cycles manually:

    class UserOutput implements OutputInterface {
        #[EntityToId]
        public User $author; // Avoids deep nesting
    }
    
  3. Case Sensitivity Property names in the output are converted to snake_case. Override with custom getters if needed:

    public function getFullName(): string {
        return $this->firstName . ' ' . $this->lastName;
    }
    
  4. Modifier Order OutputModifierInterface methods are called in registration order. Use dependency injection to enforce order:

    // Register modifiers in a specific sequence in your service provider
    $container->register(UserOutputModifier::class, [], Tags::MODIFIER);
    

Debugging Tips

  • Inspect Normalized Data Use var_dump() or Laravel’s dd() to debug intermediate output:

    $output = $this->outputNormalizer->normalize($user, UserOutput::class);
    dd($output);
    
  • Check Modifier Support Ensure supports() in modifiers returns true for the correct output/source pairs. Log conditions for debugging:

    public function supports(object $output, object $source): bool {
        if (!$output instanceof UserOutput) {
            logger()->debug('Modifier skipped: Output not UserOutput');
        }
        return $output instanceof UserOutput;
    }
    

Extension Points

  1. Custom Normalizers Extend ObjectNormalizerInterface to handle domain-specific types (e.g., Money, UUID).

  2. Dynamic Output Classes Generate output classes at runtime using reflection or factories to avoid manual definitions.

  3. Caching Normalized Output Cache normalized results for performance-critical APIs:

    $cacheKey = 'user.' . $user->id;
    return Cache::remember($cacheKey, now()->addHour(), function () use ($user) {
        return $this->outputNormalizer->normalize($user, UserOutput::class);
    });
    
  4. Validation Integration Combine with Laravel’s validation to ensure output matches API contracts:

    $output = $this->outputNormalizer->normalize($user, UserOutput::class);
    $validator = Validator::make($output, [
        'email' => 'required|email',
        'roles' => 'array',
    ]);
    
  5. Symfony Integration Use Symfony’s Serializer alongside this package for hybrid normalization (e.g., JSON:API compliance).

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.
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
atriumphp/atrium