【问题标题】:Laravel, merge two queries in oneLaravel,将两个查询合二为一
【发布时间】:2019-10-09 04:10:11
【问题描述】:

我怎么能写出比这个更好的代码:

$data1 = UploadsPois::where('estado_carga', Util::UPLOAD_POIS_CARGA_INGRESADA)
    ->where('schema_country', $schema_country)
    ->orderBy('id', 'asc')
    ->get();

foreach ($data1 as $carga) {
    $carga->UserResponsable = User::findOrFail($carga->responsable);
    $carga->Pois            = Pois::where('upload_pois_id', $carga->id)->where('pois_validate', Util::POIS_INGRESADO)->orderBy('id', 'asc')->get();
    $carga->Log = LogsPois::where('upload_pois_id', $carga->id)
        ->where('schema_country', $schema_country)
        ->whereNull('address_id')
        ->orderBy('id', 'desc')
        ->first();
}
$tareas['data1'] = $data1;

// All this bucle takes ~13000 miliseconds

$data2 = UploadsPois::where('estado_carga', Util::UPLOAD_POIS_CARGA_DEVUELTA_REVISION)
    ->where('schema_country', $schema_country)
    ->where('revisado_por', \Auth::user()->id)
    ->orderBy('id', 'asc')
    ->get();

foreach ($data2 as $carga) {
    $carga->UserResponsable = User::findOrFail($carga->responsable);
    $carga->UserValidador   = User::findOrFail($carga->validado_por);
    $carga->Pois            = Pois::where('upload_pois_id', $carga->id)->where('pois_validate', Util::POIS_INGRESADO)->orderBy('id', 'asc')->get();
    $carga->Log             = LogsPois::where('upload_pois_id', $carga->id)
        ->where('schema_country', $schema_country)
        ->whereNull('address_id')
        ->orderBy('id', 'desc')
        ->first();
}
$tareas['data2'] = $data2;

// And this one takes ~ 20 or 50 miliseconds

那些 bucles 几乎一样,我如何合并一个 foreach 和 1 个对 UploadsPois 模型的调用? 我不确定如何在同一进程中设置$tareas['data1']$tareas['data2']

【问题讨论】:

  • 您在 foreach() 循环中执行了 3 个附加查询...如果您已正确设置所有内容,$carga->UserResponable$carga->Pois$carga->Log 都可能是关系,并且您可以简单地调用 UploadsPois::with(["UserResponable", "Pois", "Log"]) 并完全跳过 foreach。不过老实说,这对于 Stackoverflow 来说有点过于宽泛了。
  • @TimLewis InUploadPois 模型我有这个关系:public function User(){ return $this->belongsTo('App\User');} 那么我是否也应该添加一个属于 tu App\User 的 UserResponsable
  • 是的;为什么不?您实际上是在循环中做了很长的路,findOrFail($carga->responsable),但问题是,如果它是一个关系,您可以急切地加载它以加快查询速度。
  • 试图弄清楚这些关系,谢谢伙计。
  • Laravel 中的所有关系都需要额外的参数来指定表名和列,所以在任何使用 $carga->whatever 的地方,你都会在 UploadsPois 模型中替换为 $this->whatever$schema_country 必须替换为 $this->schema_country,但除此之外,它们应该没有什么奇怪的地方。

标签: php laravel


【解决方案1】:

查看这段代码,我可以看出有 4 个重要模型:UploadsPoisUserPoisLogPois

您可以设置关系以加载所有这些数据,而无需循环。

Eloquent RelationshipsEloquent Relationships: Eager Loading

# UploadPois model
namespace App;

use Illuminate\Database\Eloquent\Model;
use User;
use Pois;
use LogsPois;

class UploadsPois extends Model
{
    public function user_responsable()
    {
        return $this->belongsTo(User::class, 'responsable');
    }

    public function user_validador()
    {
        return $this->belongsTo(User::class, 'validado_por');
    }

    public function pois()
    {
        return $this->hasMany(Pois::class, 'upload_pois_id');
    }

    public function log()
    {
        return $this->hasMany(LogsPois::class, 'upload_pois_id');
    }
}

您还可以按如下方式定义逆关系。

# User model
namespace App;

// Usually User model extends this instead of base model.
use Illuminate\Foundation\Auth\User as Authenticatable;
use UploadsPois;

class User extends Authenticatable
{
    public function responsable_uploads_pois()
    {
        return $this->hasMany(UploadsPois::class, 'responsable');
    }

    public function validador_uploads_pois()
    {
        return $this->hasMany(UploadsPois::class, 'validado_por');
    }
}
# Pois model
namespace App;

use Illuminate\Database\Eloquent\Model;
use UploadsPois;

class Pois extends Model
{
    public function uploads_pois()
    {
        return $this->belongsTo(UploadsPois::class, 'upload_pois_id');
    }
}
# LogPois model
namespace App;

use Illuminate\Database\Eloquent\Model;
use UploadsPois;

class LogPois extends Model
{
    public function uploads_pois()
    {
        return $this->belongsTo(UploadsPois::class, 'upload_pois_id');
    }
}

现在我们已经定义了所有的关系,你的$data1变量可以得到如下:

UploadsPois::where([
    ['estado_carga', Util::UPLOAD_POIS_CARGA_INGRESADA],
    ['schema_country', $schema_country]
])
->with([
    'user_responsable',
    'pois' => function ($pois) {
        $pois->where('pois_validate', Util::POIS_INGRESADO);
    },
    'log' => function ($log) use ($schema_country) {
        $log->where('schema_country', $schema_country)
        ->whereNull('address_id')
        ->orderBy('id', 'desc');
    }
])
->orderBy('id', 'asc')
->get();

至于$data2

UploadsPois::where([
    ['estado_carga', Util::UPLOAD_POIS_CARGA_DEVUELTA_REVISION],
    ['schema_country', $schema_country],
    ['revisado_por', auth()->id()] //Same as \Auth::id(), same as \Auth::user()->id
])
->with([
    'user_responsable',
    'user_validador',
    'pois' => function ($pois) {
        $pois->where('pois_validate', Util::POIS_INGRESADO)
    },
    'log' => function ($log) use ($schema_country) {
        $log->where('schema_country', $schema_country)
        ->whereNull('address_id')
        ->orderBy('id', 'desc');
    }
])
->orderBy('id', 'asc')
->get();

Laravel 命名约定规定你的关系方法必须是蛇形大小写。

关于组合这些查询。我看到的唯一区别如下:

  • $data1 具有 estado_carga 等于 Util::UPLOAD_POIS_CARGA_INGRESADA$data2 具有 estado_carga 等于 Util::UPLOAD_POIS_CARGA_DEVUELTA_REVISION
  • $data2 有一个额外的过滤器(validado_por 等于经过身份验证的用户的 id
  • $data2 加载了一个附加关系 (user_validador)

如果您真的想组合查询,最初可以不按这两个条件进行过滤。

$data = UploadsPois::where('schema_country', $schema_country)
->with([
    'user_responsable',
    'user_validador',
    'pois' => function ($pois) {
        $pois->where('pois_validate', Util::POIS_INGRESADO)
    },
    'log' => function ($log) use ($schema_country) {
        $log->where('schema_country', $schema_country)
        ->whereNull('address_id')
        ->orderBy('id', 'desc');
    }
])
->orderBy('id', 'asc')
->get();

这会返回一个集合,然后您可以使用各种方法(wherefirstWherefilterreject 等)对其进行过滤

# data1
$data->where('estado_carga', Util::UPLOAD_POIS_CARGA_INGRESADA);
# data2
$data->where('estado_carga', Util::UPLOAD_POIS_CARGA_DEVUELTA_REVISION)->where('validado_por', auth()->id());

Collections: Available Methods

【讨论】:

  • 非常非常感谢(你们俩和@Tim Lewis。我有一个问题,接收此数据的刀片视图在某个点上期望这一点:$carga->Log->comentario 之前正在工作,因为在我正在定义的 foreach 循环:$carga->Log = LogsPois::wh... etc,但现在它说:Ùndefined property: Illuminate\Database\Eloquent\Collection::$Log. I thought that would be the name of the relation, I rename it as Log`(不是按照您的代码记录)但我仍然有问题。
  • 没错,这就是我在两个版本中得到的:i.imgur.com/Sau8Sjd.png
  • 嗯,我的问题是因为我在'Log' => function ($log) 中放了一个->first(),但我怎样才能只检索集合的first 元素?我不想get他们所有人
  • 您可以将->first() 放在"log" => function($log){ $log->...->first(); } 中,或者直接使用$carga->log->first()->comentario。但是如果$carga->log->first() 返回null,那将是一个问题(在你的截图中,log 关系是一个空的Collection,所以$carga->log->first()null。)
猜你喜欢
  • 1970-01-01
  • 2014-06-23
  • 2012-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多