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

Php Cloneable Laravel Package

spatie/php-cloneable

Trait for PHP 8.1+ that makes objects with readonly properties cloneable. Safely “clone with changes” by copying an object while overriding readonly fields—handy until PHP gets native clone-with support.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require spatie/php-cloneable
    

    Add the trait to your model or entity class:

    use Spatie\Cloneable\Cloneable;
    
    class User implements \Cloneable
    {
        use Cloneable;
    
        public function __construct(
            public readonly string $id,
            public readonly string $name,
        ) {}
    }
    
  2. First Use Case: Clone an object with readonly properties:

    $original = new User(id: '1', name: 'John');
    $clone = clone $original;
    
    // Modify clone without affecting original
    $clone->name = 'Jane'; // ❌ Error (readonly)
    

Where to Look First

  • Trait Documentation: Review the README for basic usage.
  • PHP 8.1 Gotchas: Understand why readonly properties break cloning natively (Stitcher.io article).
  • Tests: Check the test suite for edge cases (e.g., nested objects, interfaces).

Implementation Patterns

Core Workflow

  1. Mark Properties as Readonly:

    class Product
    {
        use Cloneable;
    
        public function __construct(
            public readonly int $id,
            public string $name,
        ) {}
    }
    
  2. Clone with Deep Copy:

    $product = new Product(id: 1, name: 'Laptop');
    $copy = clone $product;
    $copy->name = 'Phone'; // Works (non-readonly)
    
  3. Handle Nested Objects:

    class Order
    {
        use Cloneable;
    
        public function __construct(
            public readonly int $id,
            public Product $product,
        ) {}
    }
    

    The trait recursively clones nested Cloneable objects.


Integration Tips

  • Laravel Eloquent Models: Extend the trait in models with readonly fields (e.g., created_at):

    class Post extends Model
    {
        use Cloneable;
    
        protected $attributes = [
            'created_at' => 'readonly',
        ];
    }
    

    Note: Override __clone() if custom logic is needed.

  • DTOs/Data Transfer Objects: Ideal for immutable DTOs:

    class UserDTO
    {
        use Cloneable;
    
        public function __construct(
            public readonly string $uuid,
            public readonly array $metadata,
        ) {}
    }
    
  • Collections: Clone collections of Cloneable objects:

    $users = collect([new User('1', 'Alice'), new User('2', 'Bob')]);
    $clonedUsers = $users->map(fn ($user) => clone $user);
    

Gotchas and Tips

Pitfalls

  1. Non-Cloneable Nested Objects: If a nested property isn’t Cloneable, cloning fails silently. Validate dependencies:

    class InvalidOrder
    {
        use Cloneable;
    
        public function __construct(
            public readonly NonCloneableClass $item, // ❌ Fails
        ) {}
    }
    
  2. Circular References: The trait doesn’t handle circular references (e.g., User referencing Order which references User). Use __clone() overrides or external libraries like dcopy for complex cases.

  3. PHP 8.0+ Only: The package targets PHP 8.1+. Downgrading breaks functionality.


Debugging

  • Verify Cloning: Override __clone() to log clones:

    protected function __clone()
    {
        \Log::debug('Cloning ' . static::class);
    }
    
  • Check Readonly Properties: Use var_dump((new \ReflectionClass($obj))->getProperty('prop')->isReadonly()) to confirm readonly status.


Extension Points

  1. Custom Cloning Logic: Override __clone() for custom behavior:

    protected function __clone()
    {
        $this->resetCache(); // Example side effect
    }
    
  2. Exclude Properties: Use shouldSkipCloning() to exclude properties:

    protected function shouldSkipCloning(string $property): bool
    {
        return $property === 'tempToken';
    }
    
  3. Post-Clone Initialization: Add a postClone() method:

    protected function postClone()
    {
        $this->generateToken();
    }
    

Config Quirks

  • No Configuration: The trait is zero-config. All behavior is controlled via method overrides.

  • Performance: Deep cloning is expensive for large objects. Cache clones if reused:

    private static array $clones = [];
    
    public static function cloneIfCached(): static
    {
        return self::$clones[self::class] ??= clone new static(...);
    }
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport