Skip to content

Commit 1879aa0

Browse files
committed
docs: Update docs for memoization/2.7
1 parent 0e52d95 commit 1879aa0

30 files changed

+2646
-11
lines changed

docs/.vitepress/config.mts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default withMermaid(defineVersionedConfig({
1010
description: "Immutable Value Objects for PHP 8.3+",
1111
base: BASE_PATH,
1212
versioning: {
13-
latestVersion: '2.6',
13+
latestVersion: '2.7',
1414
},
1515
head: [
1616
[
@@ -75,6 +75,54 @@ export default withMermaid(defineVersionedConfig({
7575

7676
sidebar: {
7777
"/": [
78+
{
79+
"text": "Get Started",
80+
"items": [
81+
{"text": "Installation", "link": "/install"},
82+
{"text": "Basic Usage", "link": "/basic-usage"}
83+
]
84+
},
85+
{
86+
"text": "Using Bag",
87+
"items": [
88+
{"text": "Collections", "link": "/collections"},
89+
{"text": "Casting Values", "link": "/casting"},
90+
{"text": "Mapping", "link": "/mapping"},
91+
{"text": "Variadics", "link": "/variadics"},
92+
{"text": "Hiding Properties", "link": "/hidden"},
93+
{"text": "Transformers", "link": "/transformers"},
94+
{"text": "Optionals", "link": "/optionals"},
95+
{"text": "Validation", "link": "/validation"},
96+
{"text": "Computed Properties", "link": "/computed-properties"},
97+
{"text": "Output", "link": "/output"},
98+
{"text": "Wrapping", "link": "/wrapping"},
99+
{"text": "Factories / Testing", "link": "/testing"},
100+
{"text": "TypeScript", "link": "/typescript"},
101+
{"text": "Memoization", link: "/memoization"}
102+
]
103+
},
104+
{
105+
"text": "Laravel Integration",
106+
"items": [
107+
{"text": "Controller Injection", "link": "/laravel-controller-injection"},
108+
{"text": "Route Parameter Binding", "link": "/laravel-route-parameter-binding"},
109+
{"text": "Eloquent Casting", "link": "/laravel-eloquent-casting"},
110+
{"text": "Generating Bag Classes", "link": "/laravel-artisan-make-bag-command"},
111+
{"text": "Laravel Debugbar Integration", "link": "/laravel-debugbar"}
112+
]
113+
},
114+
{
115+
"text": "Other",
116+
"items": [
117+
{"text": "Creating Bags from Objects", "link": "/object-to-bag"},
118+
{"text": "Why Bag?", "link": "/why"},
119+
{"text": "How Bag Works", "link": "/how-bag-works"},
120+
]
121+
},
122+
{"text": "What's New", "link": "/whats-new"},
123+
{"text": "Upgrading to Bag 2", "link": "/upgrading"}
124+
],
125+
"2.6": [
78126
{
79127
"text": "Get Started",
80128
"items": [

docs/memoization.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Memoization
2+
3+
If you find yourself creating the same value objects multiple times (e.g. related models for an Eloquent collection), you can use the `Bag::memoize()` or `Bag::memoizeUsing()` methods to cache the created value objects in memory.
4+
5+
> [!NOTE]
6+
> Memoization is only done in memory, and will not be persisted e.g. across requests.
7+
8+
## Using `Bag::memoize()`
9+
10+
To use `Bag::memoize()` you must first add the `Bag\Attributes\MemoizeUsing` attribute to your value class:
11+
12+
```php
13+
use Bag\Attributes\MemoizeUsing;
14+
use Bag\Bag;
15+
16+
#[MemoizeUsing('id')]
17+
readonly class MyValue extends Bag
18+
{
19+
public function __construct(
20+
public int $id,
21+
public string $name,
22+
) {}
23+
}
24+
```
25+
26+
You may specify one or more properties (as an array) to use as the cache key.
27+
28+
Once you have added the attribute, you can use the `Bag::memoize()` method to create or retrieve a cached instance of the value object:
29+
30+
```php
31+
$value1 = MyValue::memoize(id: 1, name: 'Davey Shafik');
32+
$value2 = MyValue::memoize(id: 1, name: 'Not Davey Shafik');
33+
34+
$value1 === $value2; // true
35+
```
36+
37+
In the above example, the second call to `MyValue::memoize()` returns the same instance as the first call, because the `id` property is used as the cache key.
38+
39+
## Using `Bag::memoizeUsing()`
40+
41+
If you need more control over the cache key, you can use the `Bag::memoizeUsing()` method instead. This method allows you to specify the properties to use as the cache key at the time of calling the method.
42+
43+
This will override the `#[MemoizeUsing]` attribute if it is present.
44+
45+
```php
46+
$value1 = MyValue::memoizeUsing('id', id: 1, name: 'Davey Shafik');
47+
$value2 = MyValue::memoizeUsing(['id'], id: 1, name: 'Not Davey Shafik');
48+
49+
$value1 === $value2; // true
50+
```

docs/versions/2.6/basic-usage.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Basic Usage
2+
3+
## Creating a Value Object
4+
5+
To create a new Value Object, extend the `Bag\Bag` class and define your properties in the constructor:
6+
7+
```php
8+
use Bag\Bag;
9+
10+
readonly class MyValue extends Bag {
11+
public function __construct(
12+
public string $name,
13+
public int $age,
14+
) {
15+
}
16+
}
17+
```
18+
19+
> [!TIP]
20+
> You can add an `@method` annotation to your class to provide auto-complete for the `::from()` method, or use the [Artisan Command with the --doc option](laravel-artisan-make-bag-command.md#updating-documentation) to generate it for you.
21+
22+
23+
## Instantiating a Value Object
24+
25+
To create a new instance of your Value Object, call the `::from()` method. You can use an array, a Collection, named arguments, or positional arguments.
26+
27+
28+
### Named Arguments
29+
30+
```php
31+
$value = MyValue::from(
32+
name: 'Davey Shafik',
33+
age: => 40,
34+
);
35+
```
36+
37+
### Array or Collection of Arguments
38+
39+
```php
40+
$value = MyValue::from([
41+
'name' => 'Davey Shafik',
42+
'age' => 40,
43+
]);
44+
```
45+
46+
or:
47+
48+
```php
49+
$value = MyValue::from(collect([
50+
'name' => 'Davey Shafik',
51+
'age' => 40,
52+
]));
53+
```
54+
55+
### Positional Arguments
56+
57+
```php
58+
$value = MyValue::from('Davey Shafik', 40);
59+
```
60+
61+
> [!WARNING]
62+
> If you use positional arguments, you must ensure they are in the same order as the constructor.
63+
64+
> [!WARNING]
65+
> If you have a single array argument, **and** an array [transformer](./transformers), the transformer will be applied to the array, potentially causing unwanted side-effects.
66+
67+
## Type Casting
68+
69+
If the input value matches the type of the property (including [union types](https://www.php.net/manual/en/language.types.type-system.php#language.types.type-system.composite.union)), it will be used as-is. Otherwise, Bag will cast all values to their defined type _automatically_ for all scalar types, as well as the following:
70+
71+
- `Bag` objects
72+
- `\Bag\Collection` and `\Illuminate\Support\Collection` objects
73+
- `\DateTimeInterface` objects will be cast using standard [PHP Date/Time formats](https://www.php.net/manual/en/datetime.formats.php)
74+
- This includes `\DateTime`, `\DateTimeImmutable`, `\Carbon\Carbon` and `\Carbon\CarbonImmutable`
75+
- Enums
76+
77+
> [!TIP]
78+
> We recommend using `\Carbon\CarbonImmutable` for all date times.
79+
80+
## Default Values
81+
82+
You can define default values for your properties by setting them in the constructor:
83+
84+
```php
85+
use Bag\Bag;
86+
87+
readonly class MyValue extends Bag {
88+
public function __construct(
89+
public string $name,
90+
public int $age = 40,
91+
) {
92+
}
93+
}
94+
95+
$value = MyValue::from([
96+
'name' => 'Davey Shafik',
97+
])->toArray(); // ['name' => 'Davey Shafik', 'age' => 40]
98+
```
99+
100+
## Nullable Values
101+
102+
Bag will fill missing nullable values without a default value with `null`:
103+
104+
```php
105+
use Bag\Bag;
106+
107+
readonly class MyValue extends Bag {
108+
public function __construct(
109+
public string $name,
110+
public ?int $age,
111+
) {
112+
}
113+
}
114+
115+
$value = MyValue::from([
116+
'name' => 'Davey Shafik',
117+
])->toArray(); // ['name' => 'Davey Shafik', 'age' => null]
118+
```
119+
120+
## Stripping Extra Parameters
121+
122+
By default, Bag will throw a `\Bag\Exceptions\AdditionalPropertiesException` exception if you try to instantiate a non-variadic Bag with extra parameters. You can disable this behavior by adding the `StripExtraParameters` attribute to the class:
123+
124+
```php
125+
use Bag\Attributes\StripExtraParameters;
126+
use Bag\Bag;
127+
128+
#[StripExtraParameters]
129+
readonly class MyValue extends Bag {
130+
public function __construct(
131+
public string $name,
132+
public ?int $age,
133+
) {
134+
}
135+
}
136+
137+
$value = MyValue::from([
138+
'name' => 'Davey Shafik',
139+
'test' => true,
140+
])->toArray(); // [ 'name' => 'Davey Shafik', 'age' => null ] (note that 'test' is stripped)
141+
```
142+
143+
> [!TIP]
144+
> You can also use the `StripExtraParameters` attribute when [injecting a Bag object into a controller](./laravel-controller-injection.md#avoiding-extra-parameters).
145+
146+
### Modifying a Value Object
147+
148+
Value Objects are immutable, so you cannot change their properties directly. Instead, you can create a new instance with the updated values using the `Bag->with()` or `Bag->append()` methods:
149+
150+
```php
151+
$value = MyValue::from([
152+
'name' => 'Davey Shafik',
153+
'age' => 40,
154+
]);
155+
156+
$newValue = $value->with(age: 41);
157+
158+
dump($newValue->toArray()); // ['name' => 'Davey Shafik', 'age' => 41]
159+
```
160+
161+
You can pass either named arguments, or an array or Collection of key-value pairs to the `Bag->with()` method.
162+
163+
> [!TIP]
164+
> The `Bag->append()` method works the same way as `Bag->with()`, but it will not validate the new value object. You can manually validate the object using `Bag->valid()`.

0 commit comments

Comments
 (0)