wip: refactor service connection components and add ConnectionProtocol and ConnectionProvider services
- Renamed `ConnectedServicesService` to `ConnectedAppService` and adjusted related factories, DTOs, and forms. - Introduced `ConnectionProtocolService` and `ConnectionProviderService` for managing protocols and providers. - Updated Livewire forms to improve validation and allow real-time provider searches. - Added a `ConnectionStatus` field and related dropdown to service connection forms. - Enhanced form handling with new services, search functionality, and validation rules. - Refactored factory and DTO structure for better consistency and modularity. - Added service layer documentation and streamlined component data mappings.
This commit is contained in:
parent
6f07068b1d
commit
df3a37d6cb
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Data\ConnectService;
|
||||
|
||||
use Spatie\LaravelData\Data;
|
||||
|
||||
class ConnectServiceData extends Data
|
||||
{
|
||||
public function __construct(
|
||||
public string $name,
|
||||
public int $serviceProviderId,
|
||||
public int $serviceProtocolId,
|
||||
public ?string $connectionService = null,
|
||||
public ?string $logoUrl = null,
|
||||
public ?string $slug = null,
|
||||
) {}
|
||||
}
|
||||
23
app/Data/ConnectedApp/ConnectAppRequest.php
Normal file
23
app/Data/ConnectedApp/ConnectAppRequest.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?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 ConnectAppRequest extends Data
|
||||
{
|
||||
public function __construct(
|
||||
public string $name,
|
||||
public int $connectionProviderId,
|
||||
public int $connectionProtocolId,
|
||||
public ?string $connectionService = null,
|
||||
public ?string $logoUrl = null,
|
||||
public ?string $slug = null,
|
||||
public ?int $statusId = null,
|
||||
) {}
|
||||
}
|
||||
@ -4,25 +4,26 @@
|
||||
|
||||
namespace App\Factories;
|
||||
|
||||
use App\Data\ConnectService\ConnectServiceData;
|
||||
use App\Data\ConnectedApp\ConnectAppRequest;
|
||||
use App\Enums\ConnectionStatusEnum;
|
||||
use App\Models\ConnectionStatus;
|
||||
|
||||
final class ConnectedAppFactory
|
||||
{
|
||||
public function createDataFromDto(ConnectServiceData $data): array
|
||||
public function createDataFromDto(ConnectAppRequest $data): array
|
||||
{
|
||||
$result = [
|
||||
'name' => $data->name,
|
||||
'service_provider_id' => $data->serviceProviderId,
|
||||
'service_protocol_id' => $data->serviceProtocolId,
|
||||
'connection_provider_id' => $data->connectionProviderId,
|
||||
'connection_protocol_id' => $data->connectionProtocolId,
|
||||
'logo_url' => $data->logoUrl,
|
||||
'slug' => $data->slug,
|
||||
'service_status_id' => $data->statusId,
|
||||
'connection_status_id' => $data->statusId,
|
||||
];
|
||||
|
||||
// Check if the staus is filled, if not select disconnected by default
|
||||
if (null === $data->statusId) {
|
||||
$status = ConnectionStatusEnum::query()
|
||||
$status = ConnectionStatus::query()
|
||||
->where('name', '=', ConnectionStatusEnum::Disconnected->value)
|
||||
->pluck('id');
|
||||
$result['service_status_id'] = $status[0];
|
||||
|
||||
16
app/Livewire/Forms/README.md
Normal file
16
app/Livewire/Forms/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
All the forms used in the application are stored here.
|
||||
|
||||
Each form should validate the data before sending it to the server.
|
||||
This is done by using the `validate` method.
|
||||
|
||||
Validations that require the db, should not be live (realtime),
|
||||
they should be done on the only once, preferably after the form is submitted.
|
||||
|
||||
Form can be used to both create and update data. Name should start with:
|
||||
|
||||
- `Store` for both cases - preferred
|
||||
- `Create` for creating a new record
|
||||
- `Update` for updating an existing record
|
||||
|
||||
When handle both cases, use separate `save` and `update` methods.
|
||||
Livewire form docs: https://livewire.laravel.com/docs/4.x/forms
|
||||
@ -4,8 +4,8 @@
|
||||
|
||||
namespace App\Livewire\Forms;
|
||||
|
||||
use App\Data\ConnectService\ConnectServiceData;
|
||||
use App\Services\ConnectedServicesService;
|
||||
use App\Data\ConnectedApp\ConnectAppRequest;
|
||||
use App\Services\ConnectedAppService;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Form;
|
||||
|
||||
@ -14,12 +14,15 @@ class StoreConnectedAppFrom extends Form
|
||||
#[Validate('required|string|max:255|min:3')]
|
||||
public string $name = '';
|
||||
|
||||
#[Validate('required|numeric|min:1')]
|
||||
#[Validate('required|numeric|min:1|exists:connection_providers,id')]
|
||||
public int $providerId = 0;
|
||||
|
||||
#[Validate('required|numeric|min:1')]
|
||||
#[Validate('required|numeric|min:1|exists:connection_protocols,id')]
|
||||
public int $protocolId = 0;
|
||||
|
||||
#[Validate('required|numeric|min:1|exists:connection_statuses,id')]
|
||||
public int $statusId = 0;
|
||||
|
||||
#[Validate('nullable|string|max:255|min:3')]
|
||||
public ?string $logoUrl = null;
|
||||
|
||||
@ -27,15 +30,16 @@ public function save(): void
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$serviceData = new ConnectServiceData(
|
||||
$serviceData = new ConnectAppRequest(
|
||||
name: $this->name,
|
||||
serviceProviderId: $this->providerId,
|
||||
serviceProtocolId: $this->protocolId,
|
||||
connectionProviderId: $this->providerId,
|
||||
connectionProtocolId: $this->protocolId,
|
||||
logoUrl: $this->logoUrl,
|
||||
slug: str($this->name)->slug()->toString(),
|
||||
statusId: $this->statusId,
|
||||
);
|
||||
|
||||
app(ConnectedServicesService::class)->create($serviceData);
|
||||
app(ConnectedAppService::class)->create($serviceData);
|
||||
$this->reset();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,14 +4,14 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Data\ConnectService\ConnectServiceData;
|
||||
use App\Data\ConnectedApp\ConnectAppRequest;
|
||||
use App\Factories\ConnectedAppFactory;
|
||||
use App\Models\ConnectedApp;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
final class ConnectedServicesService
|
||||
final class ConnectedAppService
|
||||
{
|
||||
public function create(ConnectServiceData $data): void
|
||||
public function create(ConnectAppRequest $data): void
|
||||
{
|
||||
$data = app(ConnectedAppFactory::class)->createDataFromDto($data);
|
||||
DB::transaction(
|
||||
30
app/Services/ConnectionProtocolService.php
Normal file
30
app/Services/ConnectionProtocolService.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\ConnectionProtocol;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
readonly class ConnectionProtocolService
|
||||
{
|
||||
/**
|
||||
* @var Builder<ConnectionProtocol>
|
||||
*/
|
||||
private Builder $builder;
|
||||
|
||||
public function __construct(
|
||||
) {
|
||||
$this->builder = ConnectionProtocol::query();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, ConnectionProtocol>
|
||||
*/
|
||||
public function getAll(): Collection
|
||||
{
|
||||
return $this->builder->get();
|
||||
}
|
||||
}
|
||||
22
app/Services/ConnectionProviderService.php
Normal file
22
app/Services/ConnectionProviderService.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\ConnectionProvider;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class ConnectionProviderService
|
||||
{
|
||||
/**
|
||||
* @return Collection<int, ConnectionProvider>
|
||||
*/
|
||||
public function search(string $value): Collection
|
||||
{
|
||||
return ConnectionProvider::query()
|
||||
->whereLike('slug', "%{$value}%")
|
||||
->orWhereLike('name', "%{$value}%")
|
||||
->get();
|
||||
}
|
||||
}
|
||||
6
art/STRUCTURE.md
Normal file
6
art/STRUCTURE.md
Normal file
@ -0,0 +1,6 @@
|
||||
## Service Layer
|
||||
|
||||
Service layer should return DTO objects to application layer (Livewire Components, Forms, Views).
|
||||
As Livewire sends whole properties to the component, any data returned from Repository should not be returned to the
|
||||
components.
|
||||
It's a critical security concern not to expose any sensitive data to the components.
|
||||
@ -3,6 +3,10 @@
|
||||
use App\Livewire\Forms\StoreConnectedAppFrom;
|
||||
use App\Models\ConnectionProtocol;
|
||||
use App\Models\ConnectionProvider;
|
||||
use App\Models\ConnectionStatus;
|
||||
use App\Services\ConnectionProtocolService;
|
||||
use App\Services\ConnectionProviderService;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\Title;
|
||||
@ -18,15 +22,36 @@ public function providers()
|
||||
return ConnectionProvider::query()->get();
|
||||
}
|
||||
|
||||
|
||||
#[Computed]
|
||||
public function protocols()
|
||||
public function protocols(): Collection
|
||||
{
|
||||
return ConnectionProtocol::query()->get()->map(function (ConnectionProtocol $protocol) {
|
||||
$protocol->name = ucfirst($protocol->name);
|
||||
$protocolService = app(ConnectionProtocolService::class);
|
||||
|
||||
return $protocolService->getAll()->map(function (ConnectionProtocol $protocol) {
|
||||
$protocol->name = strtoupper($protocol->name);
|
||||
return $protocol;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Collection<int, ConnectionStatus>
|
||||
*/
|
||||
#[Computed]
|
||||
public function connectionStatuses(): Collection
|
||||
{
|
||||
return ConnectionStatus::query()->get()->map(function (ConnectionStatus $status) {
|
||||
$status->name = ucfirst($status->name);
|
||||
return $status;
|
||||
});
|
||||
}
|
||||
|
||||
public function searchProvider(string $name)
|
||||
{
|
||||
$providerService = app(ConnectionProviderService::class);
|
||||
$this->providers = $providerService->search($name);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->form->save();
|
||||
@ -54,6 +79,16 @@ public function save()
|
||||
:single="true"
|
||||
:options="$this->providers"
|
||||
searchable
|
||||
search-function="searchProvider"
|
||||
debounce="300"
|
||||
/>
|
||||
<x-choices
|
||||
required
|
||||
wire:model.live="form.statusId"
|
||||
:label="__('Status')"
|
||||
:single="true"
|
||||
:options="$this->connectionStatuses"
|
||||
placeholder="Select"
|
||||
/>
|
||||
<div class="md:col-span-2 flex gap-x-4 font-medium justify-end">
|
||||
<x-shared.button>Cancel</x-shared.button>
|
||||
|
||||
2
storage/debugbar/.gitignore
vendored
Normal file
2
storage/debugbar/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
Loading…
x
Reference in New Issue
Block a user