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

Bypass Readonly Laravel Package

zoltanka/bypass-readonly

PHPUnit plugin that lets you bypass PHP readonly and final restrictions for testing. Useful when you need to mock, extend, or modify classes marked final/readonly without changing production code. Inspired by dg/bypass-finals.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require zoltanka/bypass-readonly
    

    Add the service provider to config/app.php under providers:

    Zoltanka\BypassReadonly\BypassReadonlyServiceProvider::class,
    
  2. First Use Case Temporarily bypass Laravel's readonly property on Eloquent models:

    use Zoltanka\BypassReadonly\BypassReadonly;
    
    $model = Model::first();
    $bypass = new BypassReadonly($model);
    $model->name = 'New Value'; // Now writable
    $bypass->restore(); // Re-enable readonly
    
  3. Where to Look First

    • Facade: BypassReadonly::bypass($model) for quick usage.
    • Service Provider: Registers a global helper bypass_readonly($model).
    • Tests: tests/ directory for edge cases (e.g., nested relations).

Implementation Patterns

Common Workflows

  1. Mass Assignment Bypass

    $models = Model::all();
    $bypass = new BypassReadonly($models); // Bypasses for all models
    Model::update([...]); // Now writable
    $bypass->restore();
    
  2. API Request Handling

    public function update(Request $request, Model $model) {
        bypass_readonly($model); // Global helper
        $model->update($request->validated());
        // readonly is auto-restored after method ends
    }
    
  3. Seeding Data

    public function run() {
        bypass_readonly(User::class); // Bypass for all instances of User
        User::create([...]);
        // readonly restored after seed completes
    }
    

Integration Tips

  • Middleware: Create middleware to bypass readonly for admin routes:
    public function handle($request, Closure $next) {
        bypass_readonly($request->user()->load('profile'));
        return $next($request);
    }
    
  • Events: Use eloquent.saving to conditionally bypass:
    Model::saving(function ($model) {
        if (auth()->check()) {
            bypass_readonly($model);
        }
    });
    
  • Testing: Mock the bypass in unit tests:
    $this->partialMock(BypassReadonly::class, function ($mock) {
        $mock->shouldReceive('restore')->never();
    });
    

Gotchas and Tips

Pitfalls

  1. Memory Leaks

    • Issue: Forgetting to call restore() can leave models in a writable state indefinitely.
    • Fix: Use try-finally or Laravel's ensure():
      bypass_readonly($model)->ensure(function () use ($model) {
          $model->name = 'Updated';
      }); // Auto-restores
      
  2. Nested Relations

    • Issue: Bypassing a parent model doesn’t affect its relations.
    • Fix: Bypass relations explicitly:
      bypass_readonly($model)->with(['relation']);
      
  3. Mass Assignment Risks

    • Issue: Bypassing readonly can expose models to mass assignment vulnerabilities.
    • Fix: Combine with $fillable checks:
      bypass_readonly($model);
      $model->fill($request->only($model->fillable));
      
  4. Observer/Event Conflicts

    • Issue: Events like retrieved may trigger before restore().
    • Fix: Use bypass_readonly()->restoreLater() to defer restoration.

Debugging

  • Check Bypass Status
    if (bypass_readonly()->isBypassed($model)) {
        // Model is writable
    }
    
  • Log Warnings Add a trait to log bypass operations:
    trait LogsBypass {
        protected static function booted() {
            static::saved(function ($model) {
                if (bypass_readonly()->isBypassed($model)) {
                    Log::warning("Readonly bypassed for {$model->getMorphClass()}");
                }
            });
        }
    }
    

Extension Points

  1. Custom Conditions Override the shouldBypass() logic in a custom service provider:

    public function register() {
        $this->app->bind(BypassReadonly::class, function ($app) {
            return new class extends BypassReadonly {
                protected function shouldBypass(Model $model) {
                    return $model->exists && auth()->can('edit', $model);
                }
            };
        });
    }
    
  2. Attribute-Level Bypass Extend to bypass specific attributes:

    bypass_readonly($model)->only(['name', 'email']);
    
  3. Database-Level Bypass For raw queries, use the QueryBuilder extension:

    DB::connection()->enableReadonlyBypass();
    DB::table('users')->update([...]);
    DB::connection()->disableReadonlyBypass();
    
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.
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony
spatie/flare-daemon-runtime