Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

All notable changes to `model-required-fields` will be documented in this file.

## 3.2.0 - 2025-11-07

### What's Changed

* Exclude event filled fields by model observers, boot events, and event listeners to know really what are required fields and what are application defatult fields by @WatheqAlshowaiter in https://github.com/WatheqAlshowaiter/model-fields/pull/36

**Full Changelog**: https://github.com/WatheqAlshowaiter/model-fields/compare/3.1.8...3.2.0

## 3.1.8 - 2025-10-05

* Update Readme
Expand Down
119 changes: 111 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
[link-palestine]: https://github.com/TheBSD/StandWithPalestine/blob/main/docs/README.md
<!-- ./shields -->

Quickly retrieve **required**, **nullable**, and **default** fields for any Laravel model. Think that's simple? You probably haven’t faced the legacy projects I have. :).
Quickly retrieve **required**, **nullable**, and **default** fields for any Laravel model. Think that's simple? You
probably haven’t faced the legacy projects I have. :).

> [!Note]
> This is the documentation for version 3, if you want the version 1 or version 2 documentations go
Expand Down Expand Up @@ -153,6 +154,62 @@ Post::requiredFields(); // returns ['user_id', 'ulid', 'title', 'description']
php artisan model:fields App\\Models\\Post --required # or -r
```

#### Observer and Event-Filled Fields

Fields that are automatically filled when creating by model observers, boot events, and event listeners are
automatically excluded from required fields.

The package supports three patterns:

- **Boot method closures:** `self::creating()`, `self::saving()`
- **Observer pattern:** `PostObserver` class
- **Dispatched events:** `$dispatchesEvents` property

For example, given this model:

```php
class Post extends Model
{
protected $dispatchesEvents = [
// a dispatched event trigger listener that fills the `user_id` field
'creating' => PostCreatingEvent::class,
];

protected static function boot()
{
parent::boot();
self::observe(PostObserver::class);

self::creating(function ($model) {
$model->ulid = Str::ulid();
});

self::saving(function ($model) {
$model->title = 'default title'
});
}
}

class PostObserver
{
public function creating(Post $model): void
{
$model->description = 'default description';
}

public function saving(Post $model): void
{
$model->description = 'default saving description';
}
}
```

```php
Post::requiredFields();

// returns [] because it excludes auto-filled fields
```

### And more

We have the flexibility to get all fields, required fields, nullable fields, primary key, database default fields,
Expand Down Expand Up @@ -236,18 +293,54 @@ Fields::model(Post::class)->applicationDefaultFields();
//or
Post::applicationDefaultFields();

// If there is default attributes in the model
// If there are default attributes in the model
class Post extends Model
{
protected $attributes = [
'title' => 'default title',
'description' => 'default description',
'description' => null, // will be ignored
];

protected $dispatchesEvents = [
// if there is a field autofilled by this event,
// then it will be added to the application default fields
'creating' => PostCreatingEvent::class,
];

// or any event-filled fields
protected static function boot(): void
{
parent::boot();
self::observe(PostObserver::class);

self::creating(function ($model) {
$model->uuid = Str::uuid();
});

self::saving(function ($model) {
$model->ulid = Str::ulid();
});
}
}

// the same in the observer class
class PostObserver
{

public function creating(Post $model): void
{
// ..
}

public function saving(Post $model): void
{
// ..
}
}

// returns
// [
// 'title', 'description',
// 'title', 'uuid', 'ulid',
// ]
```

Expand All @@ -269,8 +362,16 @@ class Post extends Model
{
protected $attributes = [
'title' => 'default title',
'description' => 'default description',
];

protected static function boot(): void
{
parent::boot();

self::creating(function ($model) {
$model->description = 'default description';
});
}
}

// returns
Expand Down Expand Up @@ -307,7 +408,7 @@ php artisan model:fields \\Modules\\Order\\src\\Models\\Order
php artisan model:fields "Modules\Order\src\Models\Order"
```

- You have 3 output formats: list, json and table. the list is the default
- You have 3 output formats: list, json, and table. the list is the default

```sh
php artisan model:fields User --format=json
Expand Down Expand Up @@ -378,8 +479,10 @@ them.

## Related Packages

- **[Backup Tables](https://github.com/WatheqAlshowaiter/backup-tables)** - Backup single or multiple database tables with ease.
- **[Filament Sticky Table Header](https://github.com/WatheqAlshowaiter/filament-sticky-table-header)** - Make Filament table headers stick when scrolling for better UX.
- **[Backup Tables](https://github.com/WatheqAlshowaiter/backup-tables)** - Backup single or multiple database tables
with ease.
- **[Filament Sticky Table Header](https://github.com/WatheqAlshowaiter/filament-sticky-table-header)** - Make Filament
table headers stick when scrolling for better UX.

## Credits

Expand Down
25 changes: 23 additions & 2 deletions src/FieldsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public function requiredFields()
}

$modelDefaultAttributes = Helpers::getModelDefaultAttributes($this->modelClass);
$observerDefaultAttributes = Helpers::getObserverFilledFields($this->modelClass);

$primaryIndex = $this->primaryField();

Expand All @@ -105,6 +106,9 @@ public function requiredFields()
->reject(function ($column) use ($modelDefaultAttributes) {
return in_array($column['name'], $modelDefaultAttributes);
})
->reject(function ($column) use ($observerDefaultAttributes) {
return in_array($column['name'], $observerDefaultAttributes);
})
->pluck('name')
->unique()
->values()
Expand Down Expand Up @@ -256,12 +260,13 @@ public function applicationDefaultFields()
$this->throwIfNotUsingModelMethodFirst();

$modelInstance = new $this->modelClass;
$attributes = $modelInstance->getAttributes();
$attributes = collect($modelInstance->getAttributes())->filter()->keys()->toArray(); // ignore null values
$observerDefaultAttributes = Helpers::getObserverFilledFields($this->modelClass);

$allFields = $this->allFields();

return collect($attributes)
->keys()
->merge($observerDefaultAttributes)
->filter(function ($field) use ($allFields) {
return in_array($field, $allFields);
})
Expand Down Expand Up @@ -374,6 +379,7 @@ protected function requiredFieldsForSqlite()
{
$table = Helpers::getTableFromThisModel($this->modelClass);
$modelDefaultAttributes = Helpers::getModelDefaultAttributes($this->modelClass);
$observerDefaultAttributes = Helpers::getObserverFilledFields($this->modelClass);

$queryResult = DB::select(/** @lang SQLite */ "PRAGMA table_info($table)");

Expand All @@ -389,6 +395,9 @@ protected function requiredFieldsForSqlite()
->reject(function ($column) use ($modelDefaultAttributes) {
return in_array($column['name'], $modelDefaultAttributes);
})
->reject(function ($column) use ($observerDefaultAttributes) {
return in_array($column['name'], $observerDefaultAttributes);
})
->pluck('name')
->toArray();
}
Expand Down Expand Up @@ -481,6 +490,7 @@ protected function requiredFieldsForMysqlAndMariaDb()
{
$table = Helpers::getTableFromThisModel($this->modelClass);
$modelDefaultAttributes = Helpers::getModelDefaultAttributes($this->modelClass);
$observerDefaultAttributes = Helpers::getObserverFilledFields($this->modelClass);

$queryResult = DB::select(
/** @lang SQLite */ "
Expand Down Expand Up @@ -519,6 +529,9 @@ protected function requiredFieldsForMysqlAndMariaDb()
->reject(function ($column) use ($modelDefaultAttributes) {
return in_array($column['name'], $modelDefaultAttributes);
})
->reject(function ($column) use ($observerDefaultAttributes) {
return in_array($column['name'], $observerDefaultAttributes);
})
->pluck('name')
->toArray();
}
Expand Down Expand Up @@ -803,6 +816,7 @@ protected function requiredFieldsForPostgres()
{
$table = Helpers::getTableFromThisModel($this->modelClass);
$modelDefaultAttributes = Helpers::getModelDefaultAttributes($this->modelClass);
$observerDefaultAttributes = Helpers::getObserverFilledFields($this->modelClass);

$primaryIndex = DB::select(/** @lang PostgreSQL */ "
SELECT
Expand Down Expand Up @@ -868,6 +882,9 @@ protected function requiredFieldsForPostgres()
->reject(function ($column) use ($modelDefaultAttributes) {
return in_array($column['name'], $modelDefaultAttributes);
})
->reject(function ($column) use ($observerDefaultAttributes) {
return in_array($column['name'], $observerDefaultAttributes);
})
->pluck('name')
->unique()
->toArray();
Expand Down Expand Up @@ -956,6 +973,7 @@ protected function requiredFieldsForSqlServer()
{
$table = Helpers::getTableFromThisModel($this->modelClass);
$modelDefaultAttributes = Helpers::getModelDefaultAttributes($this->modelClass);
$observerDefaultAttributes = Helpers::getObserverFilledFields($this->modelClass);

$primaryIndex = DB::select(/** @lang TSQL */ '
SELECT
Expand Down Expand Up @@ -1006,6 +1024,9 @@ protected function requiredFieldsForSqlServer()
->reject(function ($column) use ($modelDefaultAttributes) {
return in_array($column['name'], $modelDefaultAttributes);
})
->reject(function ($column) use ($observerDefaultAttributes) {
return in_array($column['name'], $observerDefaultAttributes);
})
->pluck('name')
->toArray();
}
Expand Down
25 changes: 23 additions & 2 deletions src/ModelFieldsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ public function boot()
$table = Helpers::getTableFromThisModel($this->getModel());

$modelDefaultAttributes = Helpers::getModelDefaultAttributes($this->getModel());
$observerDefaultAttributes = Helpers::getObserverFilledFields($this->getModel());

$primaryIndex = $this->primaryField();

Expand All @@ -191,6 +192,9 @@ public function boot()
->reject(function ($column) use ($modelDefaultAttributes) {
return in_array($column['name'], $modelDefaultAttributes);
})
->reject(function ($column) use ($observerDefaultAttributes) {
return in_array($column['name'], $observerDefaultAttributes);
})
->pluck('name')
->unique()
->values()
Expand Down Expand Up @@ -590,12 +594,13 @@ public function boot()
Builder::macro('applicationDefaultFields', function () {
$modelClass = $this->getModel();
$modelInstance = new $modelClass;
$attributes = $modelInstance->getAttributes();
$attributes = collect($modelInstance->getAttributes())->filter()->keys()->toArray(); // ignore null values
$observerDefaultAttributes = Helpers::getObserverFilledFields($this->getModel());

$allFields = $this->allFields();

return collect($attributes)
->keys()
->merge($observerDefaultAttributes)
->filter(function ($field) use ($allFields) {
return in_array($field, $allFields);
})
Expand Down Expand Up @@ -755,6 +760,7 @@ public function boot()
Builder::macro('requiredFieldsForSqlite', function () {
$table = Helpers::getTableFromThisModel($this->getModel());
$modelDefaultAttributes = Helpers::getModelDefaultAttributes($this->getModel());
$observerDefaultAttributes = Helpers::getObserverFilledFields($this->getModel());

$queryResult = DB::select(/** @lang SQLite */ "PRAGMA table_info($table)");

Expand All @@ -770,13 +776,17 @@ public function boot()
->reject(function ($column) use ($modelDefaultAttributes) {
return in_array($column['name'], $modelDefaultAttributes);
})
->reject(function ($column) use ($observerDefaultAttributes) {
return in_array($column['name'], $observerDefaultAttributes);
})
->pluck('name')
->toArray();
});

Builder::macro('requiredFieldsForMysqlAndMariaDb', function () {
$table = Helpers::getTableFromThisModel($this->getModel());
$modelDefaultAttributes = Helpers::getModelDefaultAttributes($this->getModel());
$observerDefaultAttributes = Helpers::getObserverFilledFields($this->getModel());

$queryResult = DB::select(
/** @lang SQLite */ "
Expand Down Expand Up @@ -815,13 +825,17 @@ public function boot()
->reject(function ($column) use ($modelDefaultAttributes) {
return in_array($column['name'], $modelDefaultAttributes);
})
->reject(function ($column) use ($observerDefaultAttributes) {
return in_array($column['name'], $observerDefaultAttributes);
})
->pluck('name')
->toArray();
});

Builder::macro('requiredFieldsForPostgres', function () {
$table = Helpers::getTableFromThisModel($this->getModel());
$modelDefaultAttributes = Helpers::getModelDefaultAttributes($this->getModel());
$observerDefaultAttributes = Helpers::getObserverFilledFields($this->getModel());

$primaryIndex = DB::select(/** @lang PostgreSQL */ "
SELECT
Expand Down Expand Up @@ -887,6 +901,9 @@ public function boot()
->reject(function ($column) use ($modelDefaultAttributes) {
return in_array($column['name'], $modelDefaultAttributes);
})
->reject(function ($column) use ($observerDefaultAttributes) {
return in_array($column['name'], $observerDefaultAttributes);
})
->pluck('name')
->unique()
->toArray();
Expand All @@ -895,6 +912,7 @@ public function boot()
Builder::macro('requiredFieldsForSqlServer', function () {
$table = Helpers::getTableFromThisModel($this->getModel());
$modelDefaultAttributes = Helpers::getModelDefaultAttributes($this->getModel());
$observerDefaultAttributes = Helpers::getObserverFilledFields($this->getModel());

$primaryIndex = DB::select(/** @lang TSQL */ '
SELECT
Expand Down Expand Up @@ -945,6 +963,9 @@ public function boot()
->reject(function ($column) use ($modelDefaultAttributes) {
return in_array($column['name'], $modelDefaultAttributes);
})
->reject(function ($column) use ($observerDefaultAttributes) {
return in_array($column['name'], $observerDefaultAttributes);
})
->pluck('name')
->toArray();
});
Expand Down
Loading