本文目录导读:

- 基础 DTO 类实现
- 使用 Spatie Data Transfer Object 包
- 使用 Laravel Eloquent API Resources
- 使用 AutoMapper 模式
- 高级实现 - 通用 DTO 映射器
- 使用 PHP 8 Attributes 自动映射
- 使用 Factory 模式
- 推荐方案
在 Laravel 中实现数据映射到 DTO(Data Transfer Object)有多种方式,我来介绍几种常用的实现方法:
基础 DTO 类实现
<?php
namespace App\DTOs;
class UserDTO
{
public function __construct(
public readonly int $id,
public readonly string $name,
public readonly string $email,
public readonly ?string $phone,
public readonly ?\DateTime $createdAt
) {}
public static function fromArray(array $data): self
{
return new self(
id: $data['id'],
name: $data['name'],
email: $data['email'],
phone: $data['phone'] ?? null,
createdAt: isset($data['created_at'])
? new \DateTime($data['created_at'])
: null
);
}
public static function fromModel($model): self
{
return new self(
id: $model->id,
name: $model->name,
email: $model->email,
phone: $model->phone,
createdAt: $model->created_at
);
}
public function toArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'phone' => $this->phone,
'created_at' => $this->createdAt?->format('Y-m-d H:i:s')
];
}
}
使用 Spatie Data Transfer Object 包
安装包:
composer require spatie/data-transfer-object
<?php
namespace App\DTOs;
use Spatie\DataTransferObject\DataTransferObject;
class UserDTO extends DataTransferObject
{
public int $id;
public string $name;
public string $email;
public ?string $phone;
public ?\DateTimeInterface $createdAt;
// 可选:添加验证规则
public function getValidationRules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email'],
];
}
}
// 使用示例
$userDTO = new UserDTO([
'id' => 1,
'name' => 'John Doe',
'email' => 'john@example.com',
'phone' => null,
'createdAt' => now(),
]);
使用 Laravel Eloquent API Resources
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'phone' => $this->phone,
'created_at' => $this->created_at,
'roles' => RoleResource::collection($this->whenLoaded('roles')),
];
}
}
// 控制器中使用
public function show(User $user): UserResource
{
return new UserResource($user);
}
// 集合
public function index(): ResourceCollection
{
return UserResource::collection(User::paginate());
}
使用 AutoMapper 模式
<?php
namespace App\Mappers;
use App\DTOs\UserDTO;
use App\Models\User;
class UserMapper
{
public function toDTO(User $user): UserDTO
{
return new UserDTO(
id: $user->id,
name: $user->name,
email: $user->email,
phone: $user->phone,
createdAt: $user->created_at
);
}
public function toDTOs($users): array
{
return $users->map(fn($user) => $this->toDTO($user))->toArray();
}
public function toModel(UserDTO $dto): User
{
$user = new User();
$user->name = $dto->name;
$user->email = $dto->email;
$user->phone = $dto->phone;
return $user;
}
public function updateFromDTO(User $user, UserDTO $dto): User
{
$user->name = $dto->name;
$user->email = $dto->email;
$user->phone = $dto->phone;
return $user;
}
}
高级实现 - 通用 DTO 映射器
<?php
namespace App\DTOs;
use Illuminate\Support\Str;
abstract class BaseDTO
{
public static function fromArray(array $data): static
{
$dto = new static();
foreach ($data as $key => $value) {
$property = Str::camel($key);
if (property_exists($dto, $property)) {
$dto->$property = $value;
}
}
return $dto;
}
public static function fromModel($model): static
{
return static::fromArray($model->toArray());
}
public function toArray(): array
{
$data = [];
foreach (get_object_vars($this) as $property => $value) {
$data[Str::snake($property)] = $value;
}
return $data;
}
}
// 使用
class UserDTO extends BaseDTO
{
public int $id;
public string $name;
public string $email;
public ?string $phone;
public ?string $createdAt;
}
// 控制器中使用
$userDTO = UserDTO::fromModel($user);
$usersDTOs = User::all()->map(fn($user) => UserDTO::fromModel($user));
使用 PHP 8 Attributes 自动映射
<?php
namespace App\Attributes;
use Attribute;
#[Attribute(Attribute::TARGET_PROPERTY)]
class MapFrom
{
public function __construct(
public string $source,
public ?string $callback = null
) {}
}
// DTO 实现
class AdvancedUserDTO
{
#[MapFrom('id')]
public int $id;
#[MapFrom('name')]
public string $name;
#[MapFrom('email')]
public string $email;
#[MapFrom('phone')]
public ?string $phone;
#[MapFrom('created_at', 'formatDate')]
public ?string $createdAt;
public static function fromModel($model): self
{
$dto = new self();
$reflection = new \ReflectionClass($dto);
foreach ($reflection->getProperties() as $property) {
$attributes = $property->getAttributes(MapFrom::class);
if (!empty($attributes)) {
$mapFrom = $attributes[0]->newInstance();
$sourceProperty = $mapFrom->source;
$value = $model->$sourceProperty ?? null;
if ($mapFrom->callback && method_exists($dto, $mapFrom->callback)) {
$value = $dto->{$mapFrom->callback}($value);
}
$property->setValue($dto, $value);
}
}
return $dto;
}
protected function formatDate($value): ?string
{
return $value ? $value->format('Y-m-d H:i:s') : null;
}
}
使用 Factory 模式
<?php
namespace App\Factories;
use App\DTOs\UserDTO;
use App\Models\User;
class UserDTOFactory
{
public function makeFromRequest(array $requestData): UserDTO
{
return new UserDTO(
id: $requestData['id'] ?? null,
name: $requestData['name'],
email: $requestData['email'],
phone: $requestData['phone'] ?? null,
createdAt: now()
);
}
public function makeFromModel(User $user): UserDTO
{
return new UserDTO(
id: $user->id,
name: $user->name,
email: $user->email,
phone: $user->phone,
createdAt: $user->created_at
);
}
public function makeCollection($users): array
{
return $users->map(fn($user) => $this->makeFromModel($user))->toArray();
}
}
// 注册为服务提供者
public function register()
{
$this->app->singleton(UserDTOFactory::class, function ($app) {
return new UserDTOFactory();
});
}
// 控制器中使用
public function index(UserDTOFactory $factory)
{
$users = User::all();
return $factory->makeCollection($users);
}
推荐方案
对于新项目,我推荐使用 Spatie Data Transfer Object 包,它提供了:
- 类型安全
- 自动验证
- 序列化和反序列化
- 不变性(可选)
- 嵌套 DTO 支持
选择哪种方案取决于你的具体需求:
- 简单项目:基础 DTO 类
- 需要验证:Spatie DTO 包
- API 响应:Laravel API Resources
- 复杂映射:AutoMapper 模式