Skip to content

add BaseModelFrozenAttributes #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 19, 2024
Merged
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
54 changes: 54 additions & 0 deletions src/Models/Attributes/BaseModelFrozenAttributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace MacropaySolutions\LaravelCrudWizard\Models\Attributes;

use MacropaySolutions\LaravelCrudWizard\Models\BaseModel;

/**
* For properties autocompletion declare in the children classes (with @ property) all the model's parameters (columns)
*
* To avoid declaring them twice, put @ mixin ChildBaseModelFrozenAttributes in ChildBaseModelAttributes
*/
class BaseModelFrozenAttributes implements \Stringable
{
private \stdClass $mirror;

public function __construct(BaseModel $ownerBaseModel) {
$this->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);
}
}
17 changes: 17 additions & 0 deletions src/Models/BaseModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down