LatestOfMany() of BelongsToMany() relationship



我已经为hasMany()关系使用latestOfmany()将它们定义为hasOne()很长一段时间了。最近,我一直需要类似的应用程序,但需要belongsToMany()关系。不幸的是,Laravel没有这个功能。

我的代码库如下:

文档

  • id
  • 上传日期
  • 标识符代码

个人

  • id
  • 名称

DocumentPerson(pivot)

  • id
  • person_id
  • person_id
  • 代币

我的目标是:定义获取Person的第一个文档(根据upload_date)的关系。正如你所看到的,这是一种多对多的关系。

到目前为止我尝试过的:

public function firstDocument()
{
return $this->hasOne(DocumentPerson::class)->oldestOfMany('document.upload_date');
//this was my safe bet but oldestOfMany() and ofMany() doesn't allow aggregating on relationship column.
}
public function firstDocument()
{
return $this->belongToMany(Document::class)->oldestOfMany('upload_date')
}
public function firstDocument()
{
return $this->belongToMany(Document::class)->oldest()->limit(1);
}
public function firstDocument()
{
return $this->hasOneThrough(Document::class, DocumentPerson::class, 'id', 'document_id', 'id', 'person_id')->latestOfMany('upload_date'); 
}

在这一点上,我几乎肯定目前的关系基础不支持这样的事情,所以我正在制定其他方法来解决这个问题。我的两个选择:

  • 在Person表上添加一个名为first_document_id的列,使用belongsTo()简单快速地执行该操作。但缺点是,我必须实现这么多事件侦听器,以确保它始终与实际关系一致。如果文档的上传日期是更新等(基本上是数据库不一致),该怎么办
  • 在pivot(document_person)表上添加一个order列,该列将按upload_date保存相关文档的顺序。这样我就可以完成hasOne(DocumentPerson::class)->oldestOfMany('order');//or just ofMany()并使用它。这也带来了数据库不一致的风险

可以公平地说,我正处于十字路口。欢迎并赞赏任何想法和建议。非常感谢。请阅读限制,以防止提出对我的情况不可行的事情。

限制:

(请)

  1. 严格来说,这应该是一种关系。我将在不同的地方使用它,它肯定是关系,这样我就可以急切地加载和查询它。我的下一个目标是通过这个关系进行查询,所以这是必要的
  2. 不要建议访问者,这对我的案子不太好
  3. 不要建议收集方法,它需要在查询中完成
  4. 不要建议使用->limit()->take()->first(),它们容易导致加载不一致的结果

更新1

Q: 为什么一个人的第一份文件必须是一段关系?

A: 因为接下来我将在各种不同的实例中查询它。将在哪里使用它的示例查询:

  1. 获取其第一个文档(根据upload_date)在2022-01-01和2022-06-08之间upload_date。(以及其他10个作用域和过滤器)
  2. 获取其第一个文档(根据upload_date)identifier_code以"开头的所有用户;Lorem";id大于100

这些只是举几个例子,在很多情况下,我真的必须以各种方式查询它。这就是我迫切需要它成为关系的原因,所以我可以使用Person::whereHas('firstDocument',function($subQuery){ return $subQuery->someScope1()->anotherScope2()->where(...); }轻松地查询它

如果我只需要显示它,是的,当然用闭包进行热切加载会很好,甚至收集方法或访问器就足够了。但既然能够查询它是需要的,关系才是本质。请记住,Person表有大约50万条记录,因此需要在数据库层查询它。

好吧,这是我选择的解决方案(在我的选择中,在问题中解释)。我实现了";在枢轴上添加CCD_ 13列";桌子因为与其他选项相比,它的扩展性更好,而且相当灵活。它允许查询最后一个文档、第一个文档、第三个文档等。虽然它甚至不需要任何聚合函数(适用于->latestOfMany()中的Max、min),但这会提高性能。考虑到这些限制,这个解决方案是可行的。以下是我如何应用它,以防其他人正在考虑类似的事情。

目前,这种方法唯一明显的缺点是无法访问任何额外的数据透视数据

新增订单栏:

//migration
$table->unsignedTinyInteger('document_upload_date_order')->nullable()->after('token');
$table->index('document_upload_date_order');//for performance

Person.php(模型)


//... other stuff
public function personalDocuments()
{//my old relationship, which I'll still keep for display/index purposes.
return $this->belongsToMany(Document::class)->withPivot('token')->where('type_slug','personal');
}

//NEW RELATIONSHIP
public function firstDocument()
{//Eloquent relationship, allows for querying and eager loading
return $this->hasOneThrough(
Document::class,
DocumentPerson::class,//pivot class for the pivot table
'person_id',
'id',
'id',
'document_id')
->where('document_upload_date_order',1);//magic here

SomeService.php

public function determineDocumentUploadDateOrders(Person $person){
$sortLogic=[
['upload_date', 'asc'],
['created_at', 'asc'],
];
$documentsOrdered=$person->documents->sortBy($sortLogic)->values();//values() is for re-indexing the array keys
foreach ($documentsOrdered as $index=>$document){
//updating through pivot tables ORM model
DocumentPerson::where('id',$document->pivot->id)->update([
'document_upload_date_order'=>$index+1,
'document_id'=>$document->id,
'person_id'=>$document->pivot->person_id,
]);
}
}

我将determineDocumentUploadDateOrders()连接到各种事件侦听器和模型事件中,因此每当发生关联/解除关联,或文档的上传日期发生变化时,我只需调用具有相应PersondetermineDocumentUploadDateOrders(),这样它就始终与实际保持同步。

充分实施了它,它提供了一致的结果和出色的性能。当然,保持同步会带来一些开销。但尽管如此,它还是在满足要求的同时完成了任务。老实说,我发现这种方法比官方雄辩的关系和类似的替代方案中的一些方法可靠得多。

几年前我也遇到过类似的情况。

在这种情况下,最好的解决方法是使用@staudenmair包急于限制

在两个模型(父模型和相关模型)上加载特征use StaudenmeirEloquentEagerLimitHasEagerLimit;

然后尝试下面的代码

public function firstDocument() {
return $this->documents()->latest()->limit(1);
}
public function documents() {
return $this->belongsToMany(Document::class);
}

需要补充的是,带限制的Eager加载不适用于内置的laravel雄辩器,您必须构建自己的原始查询才能实现它,这可能会变成一场噩梦。staudenmeir的那个热切的限制包应该与laravel源代码合并😆

相关内容

  • 没有找到相关文章

最新更新