This PR adds support for encrypted casts. Laravel's default encrypted casts ('encrypted', 'encrypted:array', 'encrypted:collection', 'encrypted:json', 'encrypted:object') work out of the box, but the custom casts have to be specified in VirtualColumn::$customEncryptedCastables.
The problem with encrypted casts is that VirtualColumn uses $model->setAttribute(), and when using the encrypted casts, the password gets encrypted multiple times in the end. To deal with that, we check if the value is encrypted. If it is, use $model->attributes[$key] = $value instead of $model->setAttribute($key, $value) to bypass the encryption.
Shared state issues
When multiple models extended a base class that used VirtualColumn, there were issues with the shared state. Booting the trait would add event listeners specific to the model the trait's used on to the static $afterListeners property. The problem is that this was happening with each used model, and in the end, the models initialized after the first one wouldn't handle the events properly. For example, creating an instance of model Bar after creating Foo would result in Bar getting encoded using the same custom columns as Foo because Foo got initialized first.
In this PR, most of the static things in the VirtualColumn trait got changed to non-static, so that the state isn't shared across models.
Encrypted casts
This PR adds support for encrypted casts. Laravel's default encrypted casts (
'encrypted'
,'encrypted:array'
,'encrypted:collection'
,'encrypted:json'
,'encrypted:object'
) work out of the box, but the custom casts have to be specified inVirtualColumn::$customEncryptedCastables
.The problem with encrypted casts is that VirtualColumn uses
$model->setAttribute()
, and when using the encrypted casts, the password gets encrypted multiple times in the end. To deal with that, we check if the value is encrypted. If it is, use$model->attributes[$key] = $value
instead of$model->setAttribute($key, $value)
to bypass the encryption.Shared state issues
When multiple models extended a base class that used VirtualColumn, there were issues with the shared state. Booting the trait would add event listeners specific to the model the trait's used on to the static
$afterListeners
property. The problem is that this was happening with each used model, and in the end, the models initialized after the first one wouldn't handle the events properly. For example, creating an instance of model Bar after creating Foo would result in Bar getting encoded using the same custom columns as Foo because Foo got initialized first.In this PR, most of the static things in the VirtualColumn trait got changed to non-static, so that the state isn't shared across models.