Savable is a Laravel package that will help you organize your business logic.
You can install the package via composer:
composer require chargefield/laravel-savable
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Chargefield\Savable\Traits\IsSavable;
class Post extends Model
{
use IsSavable;
}
A simple example for storing a record from a controller:
namespace App\Http\Controllers;
use App\Models\Post;
use Chargefield\Savable\Fields\SlugField;
use Chargefield\Savable\Fields\StringField;
use Illuminate\Http\Request;
class PostController
{
public function store(Request $request)
{
$post = Post::make()->savable($request->all())->columns([
StringField::make('title'),
SlugField::make('slug')->fromField('title'),
StringField::make('body'),
])->save();
}
}
Setting columns:
$post = Post::make()->savable([...])->columns([
StringField::make('title'),
SlugField::make('slug')->fromField('title'),
StringField::make('body'),
])->save();
Alternatively, you can set savable columns in a model:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Chargefield\Savable\Fields\Field;
use Chargefield\Savable\Traits\IsSavable;
use Chargefield\Savable\Fields\JsonField;
use Chargefield\Savable\Fields\SlugField;
use Chargefield\Savable\Fields\FileField;
use Chargefield\Savable\Fields\StringField;
use Chargefield\Savable\Fields\BooleanField;
use Chargefield\Savable\Fields\IntegerField;
use Chargefield\Savable\Fields\DatetimeField;
class Post extends Model
{
use IsSavable;
/**
* @return Field[]
*/
public function savableColumns(): array
{
return [
StringField::make('title')->rules('required|string'),
SlugField::make('slug')->fromField('title'),
StringField::make('body')->rules('required|string'),
FileField::make('image')->nullable()->rules('nullable|image'),
BooleanField::make('is_featured')->rules('required|boolean'),
IntegerField::make('order')->strict()->rules('required|integer|min:1'),
JsonField::make('options')->nullable(),
DatetimeField::make('published_at')->nullable(),
];
}
}
NOTE: savableColumns()
will get overridden by columns([...])
Setting data:
$post = Post::make()->savable(request()->all())->columns([...])->save();
or
$post = Post::make()->savable()->data(request()->all())->columns([...])->save();
Setting data from request:
$post = Post::make()->savable()->fromRequest()->columns([...])->save();
Setting data from a given request:
$post = Post::make()->savable()->fromRequest(request())->columns([...])->save();
Validating before saving (throws Illuminate\Validation\ValidationException):
$post = Post::make()->savable()->data([...])->columns([...])->validate()->save();
Validating without throwing an exception:
Post::make()->savable()->data([...])->columns([...])->hasErrors();
// return bool
or
Post::make()->savable()->data([...])->columns([...])->getErrors();
// return Illuminate\Support\MessageBag
NOTE: Fields must set rules([...])
in order to validate their data.
StringField::make('title');
SlugField::make('slug')->fromField('title')->separateBy('-');
FileField::make('image')->disk('local')->path('images')->withOriginalName();
BooleanField::make('is_featured');
IntegerField::make('age')->strict();
JsonField::make('options')->pretty()->depth(512);
DatetimeField::make('published_at');
Sets the column name and default value:
StringField::make('title', 'Default Title');
or
StringField::make('title')->value('Default Title');
Sets the field name if not the same as the column name:
StringField::make('title')->fieldName('name');
Sets the nullable flag, null will be returned if value is empty/null/exception:
StringField::make('title')->nullable();
Sets the validation rules for the field (Laravel validation rules):
StringField::make('user_id')->rules('required|exists:users,id');
or
StringField::make('user_id')->rules([
'required',
Rule::exists('users', 'id'),
]);
Sets a closure to transform the value:
StringField::make('title')->transform(function ($fieldName, $fieldValue, $fieldsData) {
return Str::title($fieldValue);
});
You can create custom fields with ease using the artisan command.
php artisan make:field CustomField
Outputs:
namespace App\Fields;
use Chargefield\Savable\Fields\Field;
class CustomField extends Field
{
/**
* @param array $data
* @return mixed
*/
public function handle(array $data = [])
{
if (empty($this->value) && $this->nullable) {
return null;
}
// Logic goes here
return $this->value;
}
}
Testing custom fields:
Field::assertHandle
$field = CustomField::fake('title');
$field->value('Example Title');
$field->assertHandle('Example Title'); // passed
$field->assertHandle('Not The Same'); // failed
Field::assertTransform
$field = CustomField::fake('title');
$field->value('Example Title');
$field->transform(function ($name, $value, $data) {
return "{$data['prefix']} {$value}";
});
$field->assertTransform('Prefixed Example Title', ['prefix' => 'Prefixed']); // passed
$field->assertTransform('Example Title', ['prefix' => 'Prefixed']); // failed
Field::assertValidation
$field = CustomField::fake('title');
$field->rules('required|string');
$field->assertValidation('Example Text'); // passed
$field->assertValidation(''); // failed
You can run the tests with:
vendor/bin/phpunit
Please see CHANGELOG for more information what has changed recently.
Please see CONTRIBUTING for details.
If you discover any security related issues, please email [email protected] instead of using the issue tracker.
The MIT License (MIT). Please see License File for more information.