chargefield/laravel-savable

Savable is a Laravel package that will help you organize your business logic.

v1.0.0 2021-09-05 16:36 UTC

This package is auto-updated.

Last update: 2024-11-06 09:43:54 UTC


README

Social Icon of Laravel Savable

Laravel Savable

Latest Stable Version Total Downloads License Tests

Savable is a Laravel package that will help you organize your business logic.

Installation

You can install the package via composer:

composer require chargefield/laravel-savable

Usage

Savable Trait

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Chargefield\Savable\Traits\IsSavable;

class Post extends Model
{
    use IsSavable;
}

Example

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();
    }
}

Savable Columns

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([...])

Savable Data

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();

Validation

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.

Fields

String Field:

StringField::make('title');

Slug Field:

SlugField::make('slug')->fromField('title')->separateBy('-');

File Field:

FileField::make('image')->disk('local')->path('images')->withOriginalName();

Boolean Field:

BooleanField::make('is_featured');

Integer Field:

IntegerField::make('age')->strict();

Json Field:

JsonField::make('options')->pretty()->depth(512);

Datetime Field:

DatetimeField::make('published_at');

Additional Methods:

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);
});

Custom Fields

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

Testing

You can run the tests with:

vendor/bin/phpunit

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email support@chargefield.com instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.