diff --git a/README.md b/README.md index 34d9a3e..84ecb09 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,55 @@ For model properties autocomplete: - add in its class dock block using **@property** all the models properties/attributes/columns - add in the model's class dock block **@property ChildBaseModelAttributes $a** and **@mixin ChildBaseModelAttributes** - use **$model->a->** instead of **$model->** +- BaseModelFrozenAttributes can be also extended on the same logic and used for model read only situations - DTO without setters (Reflection or Closure binding usage will retrieve/set private stdClass not Model - but the model can be retrieved from DB by its primary key that is readable in this frozen model): +```php +#OperationModel example for BaseModelFrozenAttributes + public function getFrozen(): OperationFrozenAttributes + { + return parent::getFrozen(); // this is needed for autocompletion and will include also the loaded relations + // or + return new OperationFrozenAttributes((clone $this)->forceFill($this->toArray())); + // or just attributes without loaded relations + return new OperationFrozenAttributes((clone $this)->forceFill($this->attributesToArray())); + } +``` + +```php +#OperationService example for BaseModelAttributes and BaseModelFrozenAttributes + public function someFunction(): void + { + // BaseModelAttributes + echo $this->model-a->value; // has autocomplete - will print for example 1 + echo $this->model-a->value = 10; // has autocomplete - will print 10 + echo $this->model->value; // has autocomplete - will print 10 + + // BaseModelFrozenAttributes + $dto = $this->model->getFrozen(); + echo $dto->client_id; // has autocomplete - will print for example 1 + $dto->client_id = 4; // Exception: Dynamic properties are forbidden. + + if (isset($dto->client)) { + /** @var ClientFrozenAttributes $client */ + // $client will be an stdClass that has autocomplete like a ClientFrozenAttributes + $client = $dto->client; + echo $client->name; // has autocomplete - will print for example 'name' + $client->name = 'text'; // NO Exception + echo $client->name; // will print 'text' + // $client changes can happen, but they will not be persisted in the $dto ($client is a stdClass clone) + echo $dto->client->name; // will print 'name' + } + + foreach (($dto->products ?? []) as $k => $product) { + /** @var ProductFrozenAttributes $product */ + // $product will be an stdClass that has autocompletes like a ProductFrozenAttributes + echo $product->value; // has autocomplete - will print for example 1 + $product->value = 2; // NO Exception + echo $product->value; // will print 2 + // $product changes can happen, but they will not be persisted in the $dto ($product is a stdClass clone) + echo $dto->products[$k]->name; // will print 1 + } + } +``` Add this new resource to the above map. diff --git a/src/Models/Attributes/BaseModelFrozenAttributes.php b/src/Models/Attributes/BaseModelFrozenAttributes.php new file mode 100644 index 0000000..a95fe58 --- /dev/null +++ b/src/Models/Attributes/BaseModelFrozenAttributes.php @@ -0,0 +1,54 @@ +mirror = (object)\json_decode(\json_encode($ownerBaseModel->attributesToArray())); + } + + public function __get(string $key): mixed + { + if (!$this->__isset($key)) { + return null; + } + + if ($this->mirror->{$key} instanceof \stdClass) { + return (object)\json_decode(\json_encode($this->mirror->{$key})); + } + + if (\is_array($this->mirror->{$key})) { + return (array)\json_decode(\json_encode($this->mirror->{$key})); + } + + return $this->mirror->{$key}; + } + + /** + * @throws \Exception + */ + public function __set(string $key, mixed $value): void + { + throw new \Exception('Dynamic properties are forbidden.'); + } + + public function __isset(string $key): bool + { + return isset($this->mirror->{$key}); + } + + public function __toString(): string + { + return (string)\json_encode($this->mirror); + } +} diff --git a/src/Models/BaseModel.php b/src/Models/BaseModel.php index 8fe52b1..33524db 100644 --- a/src/Models/BaseModel.php +++ b/src/Models/BaseModel.php @@ -12,6 +12,7 @@ use MacropaySolutions\LaravelCrudWizard\Eloquent\CustomRelations\HasCleverRelationships; use MacropaySolutions\LaravelCrudWizard\Helpers\GeneralHelper; use MacropaySolutions\LaravelCrudWizard\Models\Attributes\BaseModelAttributes; +use MacropaySolutions\LaravelCrudWizard\Models\Attributes\BaseModelFrozenAttributes; /** * For properties autocompletion declare in the children classes (with @ property) ChildBaseModelAttributes $a @@ -318,6 +319,22 @@ public function getAttribute($key) return parent::getAttribute($key); } + /** + * @throws \Exception + */ + public function getFrozen(): BaseModelFrozenAttributes + { + $frozenAttributes = + \substr($class = static::class, 0, $l = (-1 * (\strlen($class) - \strrpos($class, '\\') - 1))) . + 'Attributes\\' . \substr($class, $l) . 'FrozenAttributes'; + + if (\class_exists($frozenAttributes)) { + return new $frozenAttributes((clone $this)->forceFill($this->toArray())); + } + + throw new \Exception('Class not found: ' . $frozenAttributes); + } + /** * This will mass update the whole table if the model does not exist! * @inheritDoc