Bu yapı sayesinde CRUD işlemleri, listeleme, güncelleme ve silme gibi ortak işlevler tek bir merkezden yönetilir ve her model için tekrar tekrar yazılmasına gerek kalmaz.
Bu mimari 3 temel parçadan oluşur:
ServiceInterface, tüm servislerin uyması gereken kuralları tanımlar.
Bu sayede her servis:
Interface üzerinde Generic Type (Template) kullanılarak:
tipleri dinamik hale getirilmiştir.
Bu yaklaşım sayesinde IDE desteği artar ve tip güvenliği sağlanır.
BaseService, tüm servislerin ortak kullandığı çekirdek iş mantığını barındırır.
Burada tanımlanan işlemler:
Tüm bu işlemler:
Bu sayede her model için standart CRUD işlemleri otomatik olarak hazır gelir.
Service katmanında doğrudan Request yerine DTO kullanılması:
<?php
namespace App\Contracts\Internal;
use App\Dtos\BaseDTO;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Model;
/**
* @template TModel of Model
* @template TStoreDTO of BaseDTO
* @template TUpdateDTO of BaseDTO
* @template TListDTO of BaseDTO
*/
interface ServiceInterface
{
/**
* @param TListDTO $data
* @return LengthAwarePaginator<TModel>
*/
public function all(BaseDTO $data, array $with = []): LengthAwarePaginator;
/**
* @param TStoreDTO $data
* @return TModel
*/
public function create(BaseDTO $data): Model;
/**
* @param TModel $model
* @return TModel
*/
public function show(Model $model): Model;
/**
* @param TModel $model
* @param TUpdateDTO $data
* @return TModel
*/
public function update(Model $model, BaseDTO $data): Model;
/**
* @param TModel $model
*/
public function delete(Model $model): bool;
}
<?php
namespace App\Services\Internal;
use App\Contracts\Internal\ServiceInterface;
use App\Dtos\BaseDTO;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Model;
/**
* @template TModel of Model
* @template TStoreDTO of BaseDTO
* @template TUpdateDTO of BaseDTO
* @template TListDTO of BaseDTO
*
* @implements ServiceInterface<TModel, TStoreDTO, TUpdateDTO, TListDTO>
*/
class BaseService implements ServiceInterface
{
/**
* @var TModel
*/
protected $model;
/**
* @param TModel $model
*/
public function __construct(Model $model)
{
$this->model = $model;
}
/**
* @param TListDTO $data
* @return LengthAwarePaginator<TModel>
*/
public function all(BaseDTO $data, array $with = []): LengthAwarePaginator
{
$query = $this->model->query();
$query->with($with);
if ($data->search) {
$query->where('name', 'like', '%' . $data->search . '%');
}
return $query->paginate($data->per_page);
}
/**
* @param TStoreDTO $data
* @return TModel
*/
public function create(BaseDTO $data): Model
{
return $this->model::create($data->toArray());
}
/**
* @param TModel $model
* @return TModel
*/
public function show(Model $model): Model
{
return $this->model->find($model->id);
}
/**
* @param TModel $model
* @param TUpdateDTO $data
* @return TModel
*/
public function update(Model $model, BaseDTO $data): Model
{
$this->model->find($model->id)->update($data->toArray());
return $this->model;
}
/**
* @param TModel $model
*/
public function delete(Model $model): bool
{
return $this->model->find($model->id)->delete();
}
}
<?php
namespace App\Dtos;
abstract class BaseDTO
{
public int $per_page;
public ?string $search;
abstract public static function fromRequest(array $data): static;
abstract public function toArray(): array;
protected function searchStrReplace(string $search): string
{
$search = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $search);
return $search;
}
}
<?php
namespace App\Dtos\Messages;
use App\Dtos\BaseDTO;
class MessageListDTO extends BaseDTO
{
public function __construct(
public int $per_page,
public ?string $search,
) {
if ($this->search) {
$this->search = $this->searchStrReplace($this->search);
}
}
/**
* {@inheritDoc}
*/
public static function fromRequest(array $data): static
{
return new self(
per_page: $data['per_page'] ?? 10,
search: $data['search'] ?? null
);
}
/**
* {@inheritDoc}
*/
public function toArray(): array
{
return [
'per_page' => $this->per_page,
'search' => $this->search,
];
}
}
<?php
namespace App\Dtos\Messages;
use App\Dtos\BaseDTO;
class MessageStoreDTO extends BaseDTO
{
/**
* Create a new class instance.
*/
public function __construct(
public string $content
) {
//
}
/**
* @inheritDoc
*/
public static function fromRequest(array $data): static
{
return new self(
content: $data['content']
);
}
/**
* @inheritDoc
*/
public function toArray(): array
{
return [
'content' => $this->content,
'user_id' => auth()->id(),
];
}
}
<?php
namespace App\Contracts\Internal;
use App\Contracts\Internal\ServiceInterface;
/**
* @extends BaseService<\App\Models\Message, \App\Dtos\Messages\MessageStoreDTO, \App\Dtos\Messages\MessageUpdateDto, \App\Dtos\Messages\MessageListDTO>
*/
interface MessageServiceInterface extends ServiceInterface
{
}
<?php
namespace App\Services\Internal;
use App\Contracts\Internal\MessageServiceInterface;
use App\Models\Message;
use App\Services\Internal\BaseService;
/**
* @extends BaseService<\App\Models\Message, \App\Dtos\Messages\MessageStoreDTO, \App\Dtos\Messages\MessageUpdateDTO, \App\Dtos\Messages\MessageListDTO>
*/
class MessageService extends BaseService implements MessageServiceInterface
{
/**
* Create a new class instance.
*/
public function __construct(Message $model)
{
parent::__construct($model);
}
}
<?php
namespace App\Http\Requests\Messages;
use App\Dtos\Messages\MessageStoreDTO;
use Illuminate\Foundation\Http\FormRequest;
class StoreMessageRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'content' => 'required|max:255|min:10',
];
}
public function toDto(): MessageStoreDTO
{
return MessageStoreDTO::fromRequest($this->validated());
}
}
<?php
namespace App\Http\Controllers;
use App\Contracts\Internal\MessageServiceInterface;
use App\Http\Requests\Messages\ListMessageRequest;
use App\Http\Requests\Messages\StoreMessageRequest;
use App\Http\Resources\MessageResource;
use App\Support\Helpers\ApiResponse;
class MessageController extends Controller
{
public function __construct(
private MessageServiceInterface $messageService
) {
}
/**
* Display a listing of the resource.
*/
public function index(ListMessageRequest $request)
{
return ApiResponse::collection(
MessageResource::collection(
$this->messageService->all(
$request->toDto()
)
)
);
}
/**
* Store a newly created resource in storage.
*/
public function store(StoreMessageRequest $request)
{
$this->messageService->create($request->toDto());
return ApiResponse::created();
}
}