本文目录导读:

在Laravel中处理数据转换与格式化有多种方法,我将为您详细介绍最常用的几种方式。
API Resource(最推荐)
基础Resource
<?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,
'created_at' => $this->created_at->format('Y-m-d H:i:s'),
'profile' => [
'avatar' => $this->profile->avatar_url ?? null,
'bio' => $this->profile->bio ?? null,
],
'roles' => RoleResource::collection($this->whenLoaded('roles')),
];
}
}
带条件的Resource
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class OrderResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'order_no' => $this->order_no,
'total_amount' => $this->formatAmount($this->total_amount),
'status' => $this->status,
'status_text' => $this->getStatusText(),
// 使用when条件
'items' => OrderItemResource::collection($this->whenLoaded('items')),
'invoice' => $this->when($this->hasInvoice(),
new InvoiceResource($this->invoice)
),
// 使用whenHas
'coupon' => $this->whenHas('coupon', function() {
return [
'code' => $this->coupon->code,
'discount' => $this->coupon->discount
];
}),
// 只返回特定角色才能看到的字段
'internal_notes' => $this->when(
$request->user()->isAdmin(),
$this->internal_notes
),
];
}
private function formatAmount($amount)
{
return number_format($amount, 2, '.', ',');
}
private function getStatusText()
{
return [
'pending' => '待处理',
'processing' => '处理中',
'completed' => '已完成',
'cancelled' => '已取消'
][$this->status] ?? $this->status;
}
}
Eloquent Accessors(模型访问器)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
class Product extends Model
{
// 使用Attribute类(Laravel 9+)
protected function price(): Attribute
{
return Attribute::make(
get: fn ($value) => $value / 100, // 分转元
set: fn ($value) => $value * 100 // 元转分
);
}
// 传统方式
public function getPriceFormatAttribute()
{
return '¥' . number_format($this->price, 2);
}
public function getStatusColorAttribute()
{
return [
'active' => 'green',
'inactive' => 'gray',
'soldout' => 'red'
][$this->status] ?? 'gray';
}
// 复合访问器
protected function displayName(): Attribute
{
return Attribute::make(
get: fn () => $this->name . ' (' . $this->sku . ')'
);
}
}
日期格式转换
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Event extends Model
{
protected $casts = [
'started_at' => 'datetime:Y-m-d H:i:s',
'ends_at' => 'datetime:Y-m-d H:i:s',
'is_active' => 'boolean',
'config' => 'array',
'metadata' => 'object',
];
// 自定义日期格式
protected function startDate(): Attribute
{
return Attribute::make(
get: fn () => $this->started_at?->format('m/d/Y'),
set: fn ($value) => Carbon::parse($value)
);
}
}
// 在Resource中使用
class EventResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'start_date' => $this->start_date,
'end_date' => $this->ends_at->translatedFormat('d F Y'),
'duration' => $this->getDurationHuman()
];
}
}
集合转换与格式化
<?php
namespace App\Http\Controllers\API;
use App\Models\User;
use Illuminate\Support\Collection;
class UserController extends Controller
{
public function index()
{
$users = User::with('roles')->latest()->get();
// 使用Collection的方法
$formatted = $users->map(function ($user) {
return [
'name' => $user->name,
'email' => $user->email,
'role_names' => $user->roles->pluck('name')->implode(', '),
'age' => $user->birthday?->age ?? '未知'
];
})->filter(function ($user) {
return !empty($user['role_names']);
})->values();
// 分组转换
$grouped = $users->groupBy('role')->map->count();
// 排序
$sorted = $users->sortByDesc(function ($user) {
return $user->orders_count;
})->values();
return response()->json([
'data' => $formatted,
'meta' => [
'total' => $users->count(),
'grouped' => $grouped
]
]);
}
}
数据转换中间件
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class FormatResponse
{
public function handle(Request $request, Closure $next)
{
$response = $next($request);
// 只处理JSON响应
if ($response instanceof \Illuminate\Http\JsonResponse) {
$data = $response->getData(true);
// 统一响应格式
$formatted = [
'success' => $response->getStatusCode() < 400,
'data' => $data['data'] ?? $data,
'message' => $data['message'] ?? '',
'timestamp' => now()->toISOString(),
'status_code' => $response->getStatusCode()
];
if ($response->getStatusCode() >= 400) {
$formatted['errors'] = $data['errors'] ?? null;
}
$response->setData($formatted);
}
return $response;
}
}
复杂转换示例
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class DashboardResource extends JsonResource
{
public function toArray($request)
{
return [
'user' => new UserResource($this->user),
'statistics' => $this->getStatistics(),
'notifications' => $this->getNotifications(),
'recent_orders' => $this->getRecentOrders(),
'chart_data' => [
'labels' => $this->getChartLabels(),
'datasets' => $this->getChartDatasets()
]
];
}
private function getStatistics(): array
{
return [
'total_revenue' => money_format($this->totalRevenue),
'orders_count' => $this->ordersCount,
'customers_count' => $this->customersCount,
'avg_order_value' => money_format(
$this->totalRevenue / max($this->ordersCount, 1)
),
'growth_rate' => $this->calculateGrowthRate() . '%'
];
}
private function getNotifications(): Collection
{
return $this->notifications
->take(5)
->map(fn ($notification) => [
'id' => $notification->id,
'type' => $notification->type,
'message' => $notification->data['message'],
'read' => (bool) $notification->read_at,
'time_ago' => $notification->created_at->diffForHumans()
]);
}
private function getChartDatasets(): array
{
$colors = ['#FF6384', '#36A2EB', '#FFCE56'];
return $this->statisticsByMonth
->map(function ($data, $index) use ($colors) {
return [
'label' => $data->month,
'color' => $colors[$index % count($colors)],
'data' => [...array_values($data->values)],
'borderWidth' => 2
];
})
->values()
->all();
}
}
使用Traits复用转换逻辑
<?php
namespace App\Traits;
trait FormatsModelData
{
public function formatMoney($amount, $currency = 'USD'): string
{
$symbols = [
'USD' => '$',
'CNY' => '¥',
'EUR' => '€'
];
$symbol = $symbols[$currency] ?? '$';
return $symbol . number_format($amount, 2);
}
public function formatDate($date, $format = 'Y-m-d'): ?string
{
return $date ? $date->format($format) : null;
}
public function formatBytes($bytes, $precision = 2): string
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, $precision) . ' ' . $units[$pow];
}
}
// 在模型中使用
class File extends Model
{
use FormatsModelData;
public function getHumanSizeAttribute(): string
{
return $this->formatBytes($this->size);
}
public function getUploadedAtAttribute($value): string
{
return $this->formatDate($value, 'Y-m-d H:i') ?? '未知';
}
}
实用转换方法集合
<?php
namespace App\Helpers;
class DataFormatter
{
public static function normalizePhone($phone): string
{
$phone = preg_replace('/[^0-9+]/', '', $phone);
// 中国手机号格式化
if (strlen($phone) === 11 && substr($phone, 0, 1) === '1') {
return substr($phone, 0, 3) . '-' . substr($phone, 3, 4) . '-' . substr($phone, 7);
}
return $phone;
}
public static function maskEmail($email, $visibleChars = 3): string
{
[$username, $domain] = explode('@', $email);
$maskedUsername = substr($username, 0, $visibleChars) . str_repeat('*', max(0, strlen($username) - $visibleChars));
return $maskedUsername . '@' . $domain;
}
public static function generateSlug($text, $maxLength = 60): string
{
$text = strtolower($text);
$text = preg_replace('/[^a-z0-9]+/', '-', $text);
$text = trim($text, '-');
return substr($text, 0, $maxLength);
}
public static function formatAddress(array $address): string
{
return implode(' ', array_filter([
$address['province'] ?? '',
$address['city'] ?? '',
$address['district'] ?? '',
$address['detail'] ?? ''
]));
}
}
使用这些转换方法时,请根据您的具体需求选择合适的方式。API Resource 最适合API响应格式转换,Accessors 适合模型层的数据格式化,而Collection方法 适合数据处理和转换。