'Make an Eloquent immutable model magically record edits in another model/table and retrieve them
Is it possible to have a models such as Cars and Overrides where I retain the original record in Cars, but substitute any update in Overrides (signified by col/val in overrides table).
Ideally this would be global anytime the record is retrieved.
So upon record entry to cars it would be:
| id | color | make | model |
|---|---|---|---|
| 1 | blue | ford | f150 |
But after creating an override:
The original record would remain car:blue,ford,f150 in cars but any columns that have a corresponding override are always displayed out when the record is fetched.
Immutable original record in cars
| id | color | make | model |
|---|---|---|---|
| 1 | blue | ford | f150 |
Its overrides:
| id | model_id | column_override | value_override |
|---|---|---|---|
| 1 | 1 | color | red |
| 2 | 1 | make | chevy |
Virtually retrieved as:
| id | color | make | model |
|---|---|---|---|
| 1 | red | chevy | f150 |
Essentially
- Keeping the original record the same.
- Recording the column level change history.
- But always showing the record with the latest override when requested via Laravel.
(ie). no changes show in cars. This would be different than Auditing since you would always want the overrides to show up.
Solution 1:[1]
Even though we discussed that you prefer to make some of Laravel's magic happen, I'll try to make my suggested approche clearer.
The foundation of the solution
Tables structure
cars table
| id | color | ... | make | model | created_at |
|---|
overrides table
| car_id | id | color | ... | make | model | created_at |
|---|
Eloquent relationships
class Cars extends Model{
// ...
public function overrides()
{
return $this->hasMany(Overrides::class);
}
public function latestOverride()
{
return $this->hasOne(Overrides::class)->latest();
}
// ...
}
A use case scenario
- First care insertion
| id | color | make | model | created_at |
|---|---|---|---|---|
| 1 | blue | ford | f150 | now() |
The 1st car row from now on is immutable, no code will be made to change it.
| car_id | id | color | make | model | created_at |
|---|
First overrideis the user changing thecolortored
| car_id | id | color | make | model | created_at |
|---|---|---|---|---|---|
| 1 | 1 | red | ford | f150 | now() |
Second overrideis the user changing thecolortobrownandmaketochevy
| car_id | id | color | make | model | created_at |
|---|---|---|---|---|---|
| 1 | 1 | red | ford | f150 | TIMESTAMP |
| 1 | 2 | brown | ford | chevy | now() |
Execution
Car creation
The creation of the Car is straight forward Car::create($data)
Overriding
public function storeOverride(StoreOverrideRequest $request ,Car $car){
$latest_override = $car->latestOverride()->first();
// Compare the request using "OR" with Override first (if the car have at least one override).
// The Car also this request may be 'blue'. But the latest override is 'red'.
// So this request count as a new different Override.
// The following logic is applied to all properties
// Stop the check and create at first diff
if($request->color !== $latest_override->color || $request->color !== $car->color){
Override::creat($request->validated());
}
}
Retrieving
public function showCar(Car $car){
return [
'car' => $car,
'latestOverride' => $car->latestOverride()->first(),
];
}
Benefits
- No hidden logic.
- Easy versionning. Your user can save overrides and navigate forward and backward through his history of changes.
- Guaranteed few exchanges with the database.
A column/value override approche will limit you on retrieval. you will be obliged to do something like $car->overrides and then loop through the Collection to replace the changes on $car.
Notes
- Using Eloquent getters to query the latest value from the other model would result in a cumbersome code and a huge amount of fetching queries.
- My point of view is that magic may get too complicated and hurt your code evolution and debugging (remember Doctor Strange causing the universe of madness XD)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
