You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 8中如何获取Dns关联的各类型最新DnsCheck记录?

问题描述

我正在开发一个Laravel 8前后端应用,包含三个模型:

  • Domain:存储网站域名(如example.com)
  • Dns:通过belongsTo关联Domain,存储DNS检查元数据
  • DnsCheck:通过belongsTo关联Dns,每条记录对应一种DNS类型(如a/aaaa)的检查结果,checked_at字段标记检查时间

每个域名对应多条不同类型的DnsCheck记录(最多15条),Dns的interval字段指定检查频率。现在需要获取各类型的最新DnsCheck记录,返回可遍历的数组,但latestOfMany()只能返回单条结果,请问如何实现?

现有模型代码

Dns模型

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Dns extends Model
{
    use HasFactory;

    /**
     * Indicates if the model's ID is auto-incrementing.
     *
     * @var bool
     */
    public $incrementing = false;

    /**
    * The table associated with the model.
    *
    * @var string
    */
    protected $table = 'dns';

    /**
     * The attributes that should be cast.
     *
     * @var array
     */
    protected $casts = [
        'last_notified_at' => 'datetime',
        'last_checked_at' => 'datetime',
        'disabled_at' => 'datetime',

        // record types that we can monitor
        'a_record_is_enabled' => 'boolean',
        'aaaa_record_is_enabled' => 'boolean',
        'caa_record_is_enabled' => 'boolean',
        'cname_record_is_enabled' => 'boolean',
        'mx_record_is_enabled' => 'boolean',
        'ns_record_is_enabled' => 'boolean',
        'soa_record_is_enabled' => 'boolean',
        'srv_record_is_enabled' => 'boolean',
        'txt_record_is_enabled' => 'boolean',
        'ptr_record_is_enabled' => 'boolean'
    ];

    /**
     * Get the dns checks that this dns has
     */
    public function dns_checks()
    {
        return $this->hasMany(DnsCheck::class);
    }

    /**
     * Get the latest dns check
     */
    public function latest_dns_check()
    {
        return $this->hasOne(DnsCheck::class)->latestOfMany();
    }

    /**
     * Get the domain that owns this dns
     */
    public function domain()
    {
        return $this->belongsTo(Domain::class);
    }

    /**
     * The "booted" method of the model.
     *
     * @return void
     */
    protected static function booted()
    {
        static::deleted(function ($model) {
            $model->dns_checks()->delete();
        });
    }
}

DnsCheck模型

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class DnsCheck extends Model
{
    use HasFactory;

    /**
    * The table associated with the model.
    *
    * @var string
    */
    protected $table = 'dns_checks';

    /**
     * Get the dns that owns this dns check
     */
    public function certificate()
    {
        return $this->belongsTo(Dns::class);
    }
}

解决方案

1. 修正DnsCheck模型关联错误

首先注意到DnsCheck模型中的关联方法命名错误,原方法certificate应改为dns,否则关联无法正常工作:

// DnsCheck模型中修正关联
public function dns()
{
    return $this->belongsTo(Dns::class);
}

2. 通过Eloquent关联获取各类型最新记录

在Dns模型中添加一个新的关联方法,通过子查询筛选出每个type分组下的最新记录:

// 在Dns模型中添加
public function latest_dns_checks_by_type()
{
    return $this->hasMany(DnsCheck::class)
        ->whereIn('id', function ($query) {
            $query->selectRaw('MAX(id)')
                ->from('dns_checks')
                ->whereColumn('dns_id', 'dns.id')
                ->groupBy('type');
        })
        ->orderBy('type');
}

使用时直接预加载关联即可得到可遍历的集合:

// 获取指定Dns及其各类型最新检查记录
$dns = Dns::with('latest_dns_checks_by_type')->find($dnsId);
$latestChecks = $dns->latest_dns_checks_by_type; // 可直接循环遍历

如果更倾向于用checked_at字段判断最新,可修改子查询逻辑:

public function latest_dns_checks_by_type()
{
    return $this->hasMany(DnsCheck::class)
        ->whereRaw('(dns_id, type, checked_at) IN (
            SELECT dns_id, type, MAX(checked_at)
            FROM dns_checks
            WHERE dns_id = ?
            GROUP BY dns_id, type
        )', [$this->id]);
}

3. 直接用查询构造器查询

如果不需要模型关联,可直接通过查询构造器获取指定Dns的各类型最新记录:

use Illuminate\Support\Facades\DB;

$latestChecks = DnsCheck::query()
    ->select('dns_checks.*')
    ->joinSub(
        DnsCheck::query()
            ->select('dns_id', 'type', DB::raw('MAX(checked_at) as max_checked_at'))
            ->where('dns_id', $dnsId)
            ->groupBy('dns_id', 'type'),
        'latest_checks',
        function ($join) {
            $join->on('dns_checks.dns_id', '=', 'latest_checks.dns_id')
                ->on('dns_checks.type', '=', 'latest_checks.type')
                ->on('dns_checks.checked_at', '=', 'latest_checks.max_checked_at');
        }
    )
    ->get();

内容的提问来源于stack exchange,提问作者Ryan H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 12:24:18