You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: CHANGELOG.md
+17-6
Original file line number
Diff line number
Diff line change
@@ -16,10 +16,11 @@ Version 6 contains a number of changes to improve the security and performance o
16
16
### Data Types
17
17
18
18
- Added `SignedSerializeHandler` as a catch-all datatype, which will attempt to serialize the data using PHP's `serialize()` function. The payload is cryptographically signed with an HMAC before being stored in the database to prevent PHP object injection attacks.
19
-
20
19
- Deprecated `SerializableHandler` in favor of the new `SignedSerializeHandler` datatype. The `SerializableHandler` will be removed in a future release. In the interim, added the `metable.options.serializable.allowedClasses` config to protect against unserializing untrusted data.
21
20
- Deprecated `ArrayHandler` and `ObjectHandler`, due to the ambiguity of nested array/objects switching type. These will be removed in a future release. The `SignedSerializeHandler` should be used instead.
22
21
- Added `PureEnumHandler` and `BackedEnumHandler` which adds support for storing enum values as Meta.
22
+
- Added `StringableHandler` which adds support for storing `Illuminate\Support\Stringable` objects as Meta.
23
+
- Added `DateTimeImmutableHandler` which adds support for storing `DateTimeImmutable`/`CarbonImmutable` objects as Meta.
23
24
-`ModelHandler` will now validate that the encoded class is a valid Eloquent Model before attempting to instantiate it during unserialization. If the class is invalid, the meta value will return `null`.
24
25
-`ModelHandler` will no longer throw a model not found exception if the model no longer exists. Instead, the meta value will return `null`. This is more in line with the existing behavior of the `ModelCollectionHandler`.
25
26
-`ModelCollectionHandler` will now validate that the encoded collection class is a valid Eloquent collection before attempting to instantiate it during unserialization. If the class is invalid, an instance of `Illuminate\Database\Eloquent\Collection` will be used instead.
@@ -28,13 +29,13 @@ Version 6 contains a number of changes to improve the security and performance o
28
29
- Added `isIdempotent(): bool` method to `HandlerInterface` which should indicate whether multiple calls to the `serialize()` method with the same value will produce the same serialized output. This is used to determine if the complete serialized value can be used when searching for meta values.
29
30
- Added `useHmacVerification(): bool` method to `HandlerInterface` which should indicate whether the integrity of the serialized data should be verified with an HMAC.
30
31
31
-
### Commands
32
+
### New Commands
32
33
33
34
- Added `metable:refresh` artisan command which will decode and re-encode all meta values in the database. This is useful if you have changed the data type handlers and need to update the serialized data and indexes in the database.
34
35
35
-
### Mediable trait
36
+
### Searching Metables By Meta Value
36
37
37
-
-`whereMeta()`, `whereMetaIn()`, and `orderByMeta()` query scopes will now scan the indexed `string_value` column instead of the serialized `value` column. This greatly improves performance when searching for meta values against larger datasets.
38
+
-the Metable `whereMeta()`, `whereMetaIn()`, and `orderByMeta()` query scopes will now scan the indexed `string_value` column instead of the serialized `value` column. This greatly improves performance when searching for meta values against larger datasets.
38
39
-`whereMetaNumeric()` and `orderByMetaNumeric()` query scopes will now scan the indexed `numeric_value` column instead of the serialized `value` column. This greatly improves performance when searching for meta values against larger datasets.
39
40
-`whereMetaNumeric()` query scope will now accept a value of any type. It will be converted to an integer or float by the handler. This is more consistent with the behaviour of the other query scopes.
40
41
- Added additional query scopes to more easily search meta values based on different criteria:
@@ -47,10 +48,20 @@ Version 6 contains a number of changes to improve the security and performance o
47
48
-`whereMetaNotBetweenNumeric()`
48
49
-`whereMetaIsNull()`
49
50
-`whereMetaIsModel()`
50
-
- If the data type handlers cannot convert the search value provided to a whereMeta* query scope to a string or numeric value (as appropriate for the scope), then an exception will be thrown.
51
+
- If the data type handlers cannot convert the search value provided to a whereMeta* query scope to a string or numeric value (as appropriate for the method), then an exception will be thrown.
52
+
53
+
### Metable Casting
54
+
55
+
- Added support for casting meta values to specific types by defining the `$castMeta` property or `castMeta(): array` method on the model. This is similar to the `casts` property of Eloquent Models used for attributes. All cast types supported by Eloquent Models are also available for Meta values.Values will be cast before values are stored in the database to ensure that they are indexed consistently
56
+
- the `encrypted:` cast prefix can be combined with any other cast type to cast to the desired type before the value is encrypted to be stored it in the database. Encrypted values are not searchable.
57
+
- A value of `null` is ignored by all cast types.
58
+
- Added `mergeMetaCasts()` method which can be used to override the defined cast on a meta key at runtime.
51
59
52
60
### Meta
53
-
- Added `$meta->raw_value` property which exposes the raw serialized value of the meta key. This is useful for debugging purposes.
61
+
- Added `$meta->string_value` and `$meta->numeric_value` attributes, which are used for optimizing queries filtering by meta value
62
+
- Added `$meta->hmac` attribute, which is used by some data type handlers to validate that the payload has not been tampered with.
63
+
- Added `$meta->raw_value` virtual attribute, which exposes the raw serialized value of the meta key. This is useful for debugging purposes.
64
+
- Added `encrypt()` method, used internally by the `Metable::setMetaEncrypted()` method
54
65
55
66
# 5.0.1 - 2021-09-19
56
67
- Fixed `setManyMeta()` not properly serializing certain types of data.
Copy file name to clipboardexpand all lines: UPGRADING.md
+21-3
Original file line number
Diff line number
Diff line change
@@ -2,19 +2,37 @@
2
2
3
3
## 5.X -> 6.X
4
4
5
+
### Compatibility
6
+
5
7
* Minimum PHP version moved to 8.1
6
8
* Minimum Laravel version moved to 10
7
9
* Some methods have had their signatures adjusted to use PHP 8+ mixed and union types. If extending any class or implementing any interface from this package, method signatures may need to be updated.
10
+
11
+
### Schema Changes
12
+
8
13
* A new schema migration has been added which adds three new columns to the meta table and improves indexing for querying by meta values.
9
-
* Add the `PureEnumHandler` and `BackedEnumHandler` classes to the `datatypes` config. These handlers provide support for storing enum values as Meta.
10
-
* Recommended to add the `SignedSerializeHandler` to the end of `datatypes` config (catch-all).
14
+
15
+
### Configuration Changes
16
+
17
+
* Add the `Plank\Metable\DateType\PureEnumHandler`, `Plank\Metable\DateType\BackedEnumHandler`, `Plank\Metable\DateType\DateTimeImmutableHandler`, `Plank\Metable\DateType\StringableHandler` classes to the `datatypes` config. The order of these handlers is not important, except for `DateTimeImmutableHandler` which must come before `DateTimeHandler` if both are used.
18
+
* Recommended to add the `Plank\Metable\DateType\SignedSerializeHandler` class to the end of `datatypes` config list (catch-all).
11
19
* The `SerializableHandler`, `ArrayHandler`, and `ObjectHandler` data types have been deprecated in favor of the new `SignedSerializeHandler`. If you have any Meta encoded using any of these data types, you should continue to include them in the `datatypes` config _after_ the `SignedSerializeHandler` to ensure that existing values will continue to be properly decoded, but new values will use the new encoding. Once all old values have been migrated, you may remove the deprecated data types from the `datatypes` config.
12
20
* For security reasons, if you have any existing Meta encoded using `SerializableHandler`, you must configure the `metable.serializableHandlerAllowedClasses` config to list classes that are allowed to be unserialized. Otherwise, all objects will be returned as `__PHP_Incomplete_Class`. This config may be set to `true` to disable this security check and allow any class, but this is not recommended.
21
+
22
+
### Handlers
23
+
13
24
* If you have any custom data types, you will need to implement the new methods from the `HandlerInterface`:
14
25
*`getStringValue(): ?string` and `getNumericValue(): null|int|float`: These are used to populate the new indexed search columns. You may return `null` if the value cannot be converted into the specified format or does not need to be searchable.
15
26
*`isIdempotent(): bool`: This method should indicate whether multiple calls to the `serialize()` method with the same value will produce the same serialized output. This is used to determine if the complete serialized value can be used when searching for meta values.
16
27
*`useHmacVerification(): bool`: if the integrity of the serialized data should be verified with a HMAC, return `true`. If unserializing this data type is safe without HMAC verification, you may return `false`.
17
-
* Once you have applied the schema migration and configured the changes to the `datatypes` config, you should run the `metable:refresh` Artisan command to update all existing meta values to use the new types and populate the index columns. After this command has been run, you may remove the deprecated data types from the `datatypes` config.
28
+
29
+
### Update Existing Data
30
+
31
+
* Once you have applied the schema migration and updated the `datatypes` config, you should run the `metable:refresh` Artisan command to update all existing meta values to use the new types and populate the index columns.
32
+
* After this command has been run, you may remove the deprecated data types from the `datatypes` config.
33
+
34
+
### Query Scopes
35
+
18
36
* Review the documentation about which data types can be queried with the various `whereMeta*` and `whereMeta*Numeric` query scopes. If you are querying the serialized `value` column directly, be aware that the formatting of array/object data types may have changed.
Objects and arrays will be serialized using PHP's ``serialize()`` function, to allow for the storage and retrieval of complex data structures. The serialized value is cryptographically signed with an HMAC which is verified before the data is unserialized to prevent PHP object injection attacks. The application's ``APP_KEY`` is used as the HMAC signing key.
217
+
Objects and arrays will be serialized using PHP's ``serialize()`` function, to allow for the storage and retrieval of complex data structures.
185
218
186
219
::
187
220
188
221
<?php
189
222
$metable->setMeta('data', ['key' => 'value']);
190
223
$metable->setMeta('data', new MyValueObject(123));
191
224
192
-
HMAC verification is generally sufficient for preventing PHP object injection attacks, but it possible to further restrict what can be unserialized by specifying an array or class name in the ``metable.serializableHandlerAllowedClasses`` config in the ``config/metable.php`` file.
225
+
The serialized value is cryptographically signed with an HMAC which is verified before the data is unserialized to prevent PHP object injection attacks. The application's ``APP_KEY`` is used as the HMAC signing key. HMAC verification is generally sufficient for preventing PHP object injection attacks, but it possible to further restrict what can be unserialized by specifying an array or class name in the ``metable.SignedSerializeHandlerAllowedClasses`` config in the ``config/metable.php`` file.
193
226
194
227
.. note:: The ``Plank\Metable\DataType\SignedSerializeHandler`` class should generally be the last entry the ``config/metable.php`` datatypes array, as it will accept data of any type, causing any handlers below it to be ignored for serializing new meta values. Any handlers defined below it will still be used for unserializing existing meta values. This can be used to temporarily provide backwards compatibility for deprecated data types.
Copy file name to clipboardexpand all lines: docs/source/handling_meta.rst
+45
Original file line number
Diff line number
Diff line change
@@ -54,6 +54,19 @@ To replace existing meta with a new set of meta, you can pass an associative arr
54
54
'age' => 18,
55
55
]);
56
56
57
+
Encrypting Meta
58
+
---------------
59
+
60
+
If storing sensitive data, you can instruct the package to encrypt a meta value when it is stored in the database. Encrypted values are automatically decrypted when retrieved. To encrypt a value, use the ``setMetaEncrypted()`` method or pass ``true`` as the third argument to the ``setMeta()`` method.
Data of any type can be encrypted. Encrypted values are never searchable or sortable with query scopes.
69
+
57
70
Retrieving Meta
58
71
---------------
59
72
@@ -106,6 +119,7 @@ Alternatively, you may set default values as key-value pairs on the model itself
106
119
107
120
//...
108
121
}
122
+
109
123
::
110
124
111
125
<?php
@@ -116,6 +130,37 @@ Alternatively, you may set default values as key-value pairs on the model itself
116
130
117
131
.. note:: If a falsey value (e.g. ``0``, ``false``, ``null``, ``''``) has been manually set for the key, that value will be returned instead of the default value. The default value will only be returned if no meta exists at the key.
118
132
133
+
Casting Meta
134
+
------------
135
+
136
+
You can enforce that any meta attached to a particular key is always of a particular data type by specifying casts on the Metable model. Casts can be defined by specifying a $metaCasts attribute, or by adding a ``metaCasts(): array`` methods to the model.
137
+
138
+
::
139
+
140
+
<?php
141
+
class ExampleMetable extends Model {
142
+
use Metable;
143
+
144
+
protected $metaCasts = [
145
+
'optin' => 'boolean',
146
+
'age' => 'integer',
147
+
'secret' => 'encrypted:string',
148
+
'parent' => ExampleMetable::class,
149
+
'children' => 'collection:\App\ExampleMetable',
150
+
];
151
+
152
+
//...
153
+
}
154
+
155
+
All `cast types supported by Eloquent<https://laravel.com/docs/11.x/eloquent-mutators#attribute-casting>`_ are supported, with the following modifications:
156
+
157
+
- Casts are applied on write, not read. This means that the value will be serialized and stored in the database in the specified format, and indexes will be populated in a consistent manner. However, old data prior to the addition of the cast will not be automatically converted.
158
+
- All casts ignore values of ``null``. If a value is set to ``null``, it will be stored as ``null`` in the database, and will not be cast to the specified type.
159
+
- The ``encrypted`` cast will tell the package to always encrypt the value of that key, even if the 3rd parameter of ``setMeta()`` is not set to ``true``.
160
+
- The ``encrypted:`` cast prefix can be combined with any other cast type to convert the value to the specified type before encrypting it.
161
+
- when a class name is provided as a cast, if it implements ``\Illuminate\Contracts\Database\Eloquent\Castable``, it will be used to cast the value per the interface. Otherwise, it will enforce that the value is an instance of that class. If an instance of a different class is provided, an exception will be thrown. If the class is an Eloquent model, and an an integer or string is provided, it will attempt to retrieve the model from the database.
162
+
- The ``collection`` cast will preserve ``Illuminate\Database\Eloquent\Collection`` instances and contents, using the ``Plank\Metable\DataType\ModelCollection`` data type to store them. If passed a single model instance, it will be wrapped in an eloquent collection. A class name can be provided as an argument to enforce that the collection contains only instances of that class.
0 commit comments