feature: add ConnectionProvider creation flow
- Introduced `HandlesOperations` trait for reusable error handling with toasts and logging. - Added `StoreConnectionProviderForm` Livewire component with validation and slug uniqueness check. - Implemented `StoreConnectionProviderRequestData` DTO for structured data handling. - Updated `ConnectionProviderService` to include `create()` method using database transactions. - Registered new routes for creating connection providers. - Enhanced `ConnectionProvider` model with `Fillable` attribute for `name` and `slug`.
This commit is contained in:
parent
db8e34b622
commit
bdffb30b12
42
app/Concerns/HandlesOperations.php
Normal file
42
app/Concerns/HandlesOperations.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Mary\Traits\Toast;
|
||||
use Throwable;
|
||||
|
||||
trait HandlesOperations
|
||||
{
|
||||
use Toast;
|
||||
|
||||
public function attempt(callable $action, ?callable $onError = null, string $successMessage = 'Success !', string $errorMessage = 'Something gone wrong!'): void
|
||||
{
|
||||
try {
|
||||
// call the action callback
|
||||
$action();
|
||||
$this->success($successMessage);
|
||||
} catch (ValidationException $exception) {
|
||||
|
||||
/**
|
||||
* We are catching this exception intentionally so that, generic throwable catch block
|
||||
* doesn't catch this, which will lead to close the modal.
|
||||
*
|
||||
* But, we are rethrowing this so that validation errors can be handled by others.
|
||||
*/
|
||||
|
||||
throw $exception;
|
||||
} catch (Throwable $exception) {
|
||||
if (null !== $onError) {
|
||||
// call the callback
|
||||
$onError();
|
||||
}
|
||||
$this->error($errorMessage);
|
||||
Log::error($exception->getMessage());
|
||||
Log::error($exception->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
21
app/Data/ConnectedApp/StoreConnectionProviderRequestData.php
Normal file
21
app/Data/ConnectedApp/StoreConnectionProviderRequestData.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Data\ConnectedApp;
|
||||
|
||||
use Spatie\LaravelData\Attributes\MapInputName;
|
||||
use Spatie\LaravelData\Data;
|
||||
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
||||
|
||||
#[MapInputName(SnakeCaseMapper::class)]
|
||||
class StoreConnectionProviderRequestData extends Data
|
||||
{
|
||||
public string $slug;
|
||||
|
||||
public function __construct(
|
||||
public string $name,
|
||||
) {
|
||||
$this->slug = str($name)->slug()->toString();
|
||||
}
|
||||
}
|
||||
52
app/Livewire/Forms/StoreConnectionProviderForm.php
Normal file
52
app/Livewire/Forms/StoreConnectionProviderForm.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Livewire\Forms;
|
||||
|
||||
use App\Data\ConnectedApp\StoreConnectionProviderRequestData;
|
||||
use App\Models\ConnectionProvider;
|
||||
use App\Services\ConnectionProviderService;
|
||||
use Closure;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Form;
|
||||
|
||||
class StoreConnectionProviderForm extends Form
|
||||
{
|
||||
public string $name = '';
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
'min:3',
|
||||
'max:255',
|
||||
// Custom Closure to check the generated slug
|
||||
function (string $attribute, mixed $value, Closure $fail): void {
|
||||
$slug = Str::slug($value);
|
||||
|
||||
// Check if the slug already exists in your database table
|
||||
$exists = ConnectionProvider::query()
|
||||
->where('slug', $slug)
|
||||
->exists();
|
||||
|
||||
if ($exists) {
|
||||
$fail("The name '{$value}' is already taken. Please choose a different name.");
|
||||
}
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$this->validate();
|
||||
$service = app(ConnectionProviderService::class);
|
||||
$data = new StoreConnectionProviderRequestData(
|
||||
name: $this->name,
|
||||
);
|
||||
$service->create($data);
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
#[Fillable(['name', 'slug'])]
|
||||
final class ConnectionProvider extends Model {}
|
||||
|
||||
@ -4,11 +4,20 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Data\ConnectedApp\StoreConnectionProviderRequestData;
|
||||
use App\Models\ConnectionProvider;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ConnectionProviderService
|
||||
{
|
||||
public function create(StoreConnectionProviderRequestData $requestData): void
|
||||
{
|
||||
DB::transaction(function () use ($requestData): void {
|
||||
ConnectionProvider::query()->create($requestData->toArray());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, ConnectionProvider>
|
||||
*/
|
||||
|
||||
39
resources/views/pages/connecton-providers/⚡create.blade.php
Normal file
39
resources/views/pages/connecton-providers/⚡create.blade.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use App\Concerns\HandlesOperations;
|
||||
use App\Livewire\Forms\StoreConnectionProviderForm;
|
||||
use Laravel\Mcp\Server\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
use Mary\Traits\Toast;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
new
|
||||
#[Title('Create Connection Provider')]
|
||||
class extends Component {
|
||||
use HandlesOperations;
|
||||
|
||||
public StoreConnectionProviderForm $form;
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$this->attempt(
|
||||
action: function () {
|
||||
$this->form->save();
|
||||
$this->redirectRoute('dashboard', navigate: true);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
?>
|
||||
|
||||
<div>
|
||||
<x-shared.card :title="__('Create Connection Provider')">
|
||||
<x-mary-form :no-separator="true" class="p-4" wire:submit="save">
|
||||
<x-mary-input wire:model.blur="form.name" :label="__('Name')"/>
|
||||
<x-slot:actions>
|
||||
<x-mary-button class="btn-primary" type="submit" spinner="save" wire:click="form.store"
|
||||
:label="__('Save')"/>
|
||||
</x-slot:actions>
|
||||
</x-mary-form>
|
||||
</x-shared.card>
|
||||
</div>
|
||||
@ -12,6 +12,10 @@
|
||||
Route::prefix('connected-apps')->name('connected-apps.')->group(function (): void {
|
||||
Route::livewire('create', 'pages::connected-apps.create')->name('create');
|
||||
});
|
||||
|
||||
Route::prefix('connection-providers')->name('users.')->group(function (): void {
|
||||
Route::livewire('create', 'pages::connecton-providers.create')->name('create');
|
||||
});
|
||||
});
|
||||
|
||||
require __DIR__.'/settings.php';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user