PHP项目Laravel数据转换与格式化

wen PHP项目 3

本文目录导读:

PHP项目Laravel数据转换与格式化

  1. API Resource(最推荐)
  2. Eloquent Accessors(模型访问器)
  3. 日期格式转换
  4. 集合转换与格式化
  5. 数据转换中间件
  6. 复杂转换示例
  7. 使用Traits复用转换逻辑
  8. 实用转换方法集合

在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方法 适合数据处理和转换。

抱歉,评论功能暂时关闭!