Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ The package is intentionally small — four production files plus tests.
- **Config** is read in `getConfig()` from `{$this->config_key}.*` (`ttl`, `enabled`, `use_tags`) plus `app.debug` for the `$debug` flag controlling `Log::debug` output. The base default is `'cache_decorator'`; `RepositoryCacheDecorator` overrides it to `'repository_cache'`.
- **`src/ServiceProvider.php`** — publishes both config files: `config/cache_decorator.php` under the `cache-decorator-config` tag and `config/repository_cache.php` under the `repository-cache-config` tag; `register()` is empty. The provider is auto-discovered via `extra.laravel.providers` in `composer.json`.

The classes use Laravel facades (`Cache`, `Config`, `Log` from `Illuminate\Support\Facades`), so this package is Laravel-coupled — the TODO in the header notes a future goal to decouple. When testing the no-arg construction path, the decorated class is instantiated via `new $class` inside `initDecorated()` unless an instance is injected through the constructor.
The classes use Laravel facades (`Cache`, `Config`, `Log` from `Illuminate\Support\Facades`), so this package is Laravel-coupled — the TODO in the header notes a future goal to decouple. When testing the no-arg construction path, the decorated class is resolved through Laravel's service container via `resolve($class)` inside `initDecorated()` unless an instance is injected through the constructor. Because resolution goes through the container, the decorated class may declare auto-wired constructor dependencies and `decoratedClass()` may return a container-bound interface.

## Conventions

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ class CachedReportingService extends CacheDecorator {
$cached = new CachedReportingService;
```

The FQCN returned by `decoratedClass()` is resolved through Laravel's service container (via `resolve()`), so the decorated class may declare constructor dependencies (they are auto-wired), and `decoratedClass()` may return an interface that's bound in the container.

### Custom caching logic for a single method

If a particular method needs hand-tuned caching, override it in the subclass and use the protected helpers:
Expand Down
11 changes: 7 additions & 4 deletions src/CacheDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,12 @@ protected function cacheMiss(): object
}

/**
* Override to return the FQCN of the class to default-instantiate when no
* instance is passed to the constructor. Return null (the default) to
* require an instance via the constructor.
* Override to return the FQCN of the class to resolve when no instance is
* passed to the constructor. The FQCN is resolved through Laravel's service
* container via resolve(), so the decorated class may declare auto-wired
* constructor dependencies, and this may return an interface bound in the
* container. Return null (the default) to require an instance via the
* constructor.
*
* @return class-string<TInner>|null FQCN of the decorated class, or null
*/
Expand Down Expand Up @@ -182,7 +185,7 @@ public function initDecorated(?object $decorated): void
);
}

$decorated = new $class;
$decorated = resolve($class);
}

$this->decorated = $decorated;
Expand Down
13 changes: 13 additions & 0 deletions src/tests/CachedStubServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Trm42\CacheDecorator\ServiceProvider;
use Trm42\CacheDecorator\Tests\Stubs\CachedAutoStubService;
use Trm42\CacheDecorator\Tests\Stubs\CachedStubService;
use Trm42\CacheDecorator\Tests\Stubs\CachedStubServiceWithDependency;
use Trm42\CacheDecorator\Tests\Stubs\StubService;

/**
Expand Down Expand Up @@ -105,6 +106,18 @@ public function test_no_arg_construction_via_decorated_class()
$this->assertEquals(['id' => 7, 'name' => 'thing-7'], $result);
}

#[Test]
public function test_decorated_class_is_resolved_through_container_with_dependencies()
{
// StubServiceWithDependency requires a StubCollaborator constructor
// argument, so `new $class` would fail. Resolving through the container
// auto-wires the dependency.
$service = new CachedStubServiceWithDependency;
$service->setTtl(300);

$this->assertEquals('hello from collaborator', $service->delegatedGreeting());
}

#[Test]
public function test_constructor_without_instance_or_decorated_class_throws()
{
Expand Down
23 changes: 23 additions & 0 deletions src/tests/Stubs/CachedStubServiceWithDependency.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Trm42\CacheDecorator\Tests\Stubs;

use Trm42\CacheDecorator\CacheDecorator;

/**
* CacheDecorator subclass whose decoratedClass() returns a service with a
* constructor dependency, exercising container-based resolution in
* initDecorated().
*
* @extends CacheDecorator<StubServiceWithDependency>
*/
class CachedStubServiceWithDependency extends CacheDecorator
{
protected ?string $prefix_key = 'dep-svc';

#[\Override]
protected function decoratedClass(): ?string
{
return StubServiceWithDependency::class;
}
}
15 changes: 15 additions & 0 deletions src/tests/Stubs/StubCollaborator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Trm42\CacheDecorator\Tests\Stubs;

/**
* A simple collaborator that gets injected into StubServiceWithDependency to
* prove constructor dependencies are auto-wired by the container.
*/
class StubCollaborator
{
public function greeting(): string
{
return 'hello from collaborator';
}
}
17 changes: 17 additions & 0 deletions src/tests/Stubs/StubServiceWithDependency.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Trm42\CacheDecorator\Tests\Stubs;

/**
* Service with a constructor dependency, used to prove that decoratedClass()
* resolution goes through Laravel's container and auto-wires the dependency.
*/
class StubServiceWithDependency
{
public function __construct(protected StubCollaborator $collaborator) {}

public function delegatedGreeting(): string
{
return $this->collaborator->greeting();
}
}