28

前几天写了一遍关于laravel分表关联的查询,但是我个人觉得还不算完美,于是今天重新看了一下laravel模型的底层代码,终于写出我暂时觉得最满意的代码-laravel风格,简洁、优雅是核心。

下面直接上demo代码,主要是3个表,一个用户表,一个是用户送礼表,送礼表按月分表

user表
id用户ID
nickname昵称

<?php

namespace App\Models;

use Eloquent;

class User extends Eloquent
{
  
}

send_gift_2017_07,send_gift_2017_08 送礼表
id自增主键
user_id用户ID
worth礼物价值
uper_id主播ID(也是用户表的)
created_at送礼时间

<?php

namespace App\Models;

use Eloquent;

class SendGift extends Eloquent
{
    //关联用户表,外键是user_id
    public function user()
    {
        return $this->belongsTo(User::class);
    }
    
    关联用户表 外键是uper_id
    public function uper()
    {
        return $this->belongsTo(User::class);
    }
}

现在做一个输入开始日期和结束日期查看用户的送礼记录并且分页。

<?php

namespace App\Http\Controllers;

use App\Models\SendGift;
use DB;
use Carbon\Carbon;

class IndexController extends Controller
{
    public function index()
    {

        // 开始日期,和日期有关的,都建议用内置的carbon处理,非常好用,而且简洁
        $start = Carbon::parse(request('start'));
        // 结束日期
        $end = Carbon::parse(request('end'));
        // 查询集合
        $queries = collect();
        
        // 循环比较年月,添加每一张表的查询
        for ($i = $start->copy(); $i->format('Y-m') <= $end->format('Y-m'); $i->addMonth()) {
            $queries->push(
                DB::table("send_gift_{$i->format('Y_m')}")
                    // 建议都用select查询字段,SQL尽可能的优化性能
                    ->select('user_id', 'worth', 'uper_id', 'created_at')
                    ->whereBetween('created_at', [$start, $end->tomorrow()])
            );
        }

        // 出列一张表作为union的开始
        $unionQuery = $queries->shift();
        // 循环剩下的表添加union
        $queries->each(function ($item, $key) use ($unionQuery) {
            $unionQuery->unionAll($item);
        });

        // 设置临时表的名称,添加临时表,顺序不能反过来,否则用关联约束会找不到表
        $lists = with(new SendGift)->setTable('union_send_gift')
            // 添加临时表
            ->from(DB::raw("({$unionQuery->toSql()}) as union_send_gift"))
            // 合并查询条件
            ->mergeBindings($unionQuery)
            // 关联约束,不在用户表的不用查出来
            ->has('user')
            // 按时间倒序
            ->orderBy('created_at', 'desc')
            // 分页
            ->paginate();

        // 加载关联关系
        $lists->load([
            'user' => function ($query) {
                // 关联查询也要优化sql
                $query->select('id', 'nickname');
            },
            'uper' => function ($query) {
                $query->select('id', 'nickname');
            }
        ]);

        // 测试结果
        $lists->each(function ($item, $key) {
            echo "用户:{$item->user_id}-{$item->user->nickname}在{$item->created_at->toDateTimeString()}送出了价值{$item->worth}元的礼物给主播:{$item->uper_id}-{$item->uper->nickname}<br>";
        });
        
    }
}

然后我们看看SQL。

clipboard.png

关联查询用了渴求式加载,就能有效减少SQL的条数,保证数据库的性能。


吟渐的小风dee
609 声望21 粉丝