php 多对多关系的雄辩关系- Laravel

kuhbmx9i  于 2023-10-15  发布在  PHP
关注(0)|答案(2)|浏览(134)

我有这样的表结构:
工作

  • ID
  • 名称

类型

  • ID
  • 名称

job_type(透视表)

  • ID
  • 作业ID
  • 类型ID

任务

  • ID
  • 描述

作业类型任务(透视表)

  • ID
  • 作业类型标识
  • 任务id

备注:
1.作业有多种类型,反之亦然

  1. job_type有多个任务,反之亦然

我的问题是这个表结构,我如何在'job'和'tasks'表之间建立关系,以访问与作业相关的所有任务?

我尝试过很多解决方案,比如使用hasMany或hasManyThrough,但没有一个有效。
我应该改变table的结构吗?或任何解决方案,通过访问任务雄辩模型?

7cwmlq89

7cwmlq891#

没有股票laravel关系为您的情况下,因为您的关系不是直接的,但通过2个实体和laravel只支持1级下降,所以你可以检查这个包,可能会帮助你实现你需要的https://github.com/staudenmeir/eloquent-has-many-deep
还有一个类似的问题,可能与您的用例类似,有助于创建新的自定义关系,因为库存关系不够Custom Laravel Relations?,本文也很好地解释了https://stitcher.io/blog/laravel-custom-relation-classes
这里是自定义关系的实现,以备您需要

<?php

namespace App\Relations;

use App\Models\Job;
use App\Models\Task;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\Relation;

class JobTaskRelation extends Relation
{
    /** @var Task|Builder */
    protected $query;

    /** @var Job */
    protected $parent;

    public function __construct(Job $parent)
    {
        parent::__construct(Task::query(), $parent);
    }

    /**
     * @inheritDoc
     */
    public function addConstraints()
    {
        $this->query->join('job_type_task', 'job_type_task.task_id', '=', 'tasks.id')
            ->join('job_type', 'job_type.id', '=', 'job_type_task.job_type_id');
        if (static::$constraints) {
            $this->query->where('job_type.job_id', '=', $this->parent->getKey());
        }
    }

    /**
     * @inheritDoc
     */
    public function addEagerConstraints(array $models)
    {
        $this->query
            ->whereIn('job_type.job_id', $this->getKeys($models))
            ->with('jobTypes')
            ->select('tasks.*');
    }

    /**
     * @inheritDoc
     */
    public function initRelation(array $models, $relation)
    {
        foreach ($models as $model) {
            $model->setRelation(
                $relation,
                $this->related->newCollection()
            );
        }

        return $models;
    }

    /**
     * @inheritDoc
     */
    public function match(array $models, Collection $results, $relation)
    {
        $results = $results->unique('id');
        if ($results->isEmpty()) {
            return $models;
        }

        foreach ($models as $model) {
            $model->setRelation(
                $relation,
                $results->filter(function (Task $task) use ($model) {
                    return $task->jobTypes->pluck('job_id')->contains($model->id);
                })
            );
        }

        return $models;
    }

    /**
     * @inheritDoc
     */
    public function getResults()
    {
        return $this->query->get();
    }
}

现在你的工作模式会是这样的

<?php

namespace App\Models;

use App\Relations\JobTaskRelation;
use Illuminate\Database\Eloquent\Model;

class Job extends Model
{
    public function types()
    {
        return $this->belongsToMany('App\Models\Type');
    }

    public function jobTypes()
    {
        return $this->hasMany('App\Models\JobType');
    }

    public function tasks()
    {
        return new JobTaskRelation($this);
    }
}

任务模型将是

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    public function types()
    {
        return $this->belongsToMany('App\Models\Type');
    }

    public function jobTypes()
    {
        return $this->belongsToMany('App\Models\JobType');
    }
}

注意事项:
由于laravel假设当我们有一个模型时,表是复数的,而你的方案将pivot表定义为单数,所以当你定义pivot模型时,将$table设置为单数表名是很重要的,这样就可以了。
现在您可以使用Job::find(1)->tasksJob::with('task')->get()
您可能需要根据将来的需要添加更多功能,并且需要维护这些功能

使用访问器

还有另一个解决方案,来到我的脑海里,你考虑访问器?您可以在作业模型中定义一个访问器,以便它执行所需的查询来获取数据(如果您只对阅读数据感兴趣),这样会简单得多

// app/Models/Job.php
    public function tasksData(): Attribute
    {
        return Attribute::make(
            get: function () {
                $tasks = collect();
                foreach ($this->types as $type) {
                    $tasks = $tasks->merge($type->tasks);
                }
                return $tasks->unique('id');
            },
        );
    }

如果你总是想要它,你可以把它添加到模型追加,这样laravel就会评估它是否加载数据。

protected $appends = ['tasksData'];
rdlzhqv9

rdlzhqv92#

jobs:            id,  name
jobs_types:  job_id, type_id
types_tasks:         type_id, task_id
tasks:                             id, description

SELECT j.name,
       k.description
    FROM jobs        AS j
    JOIN jobs_types  AS jy   ON jy.job_id = j.id
    JOIN types_tasks AS yk   ON yk.type_id = jy.type_id
    JOIN tasks       AS k    ON k.id = yk.task_id
    WHERE j.name = "XYZ"

注意:在多对多表中不需要id,但需要索引,如:

jobs_types:
    PRIMARY KEY(job_id, type_id)
    INDEX(type_id, job_id)

(我不知道如何将其翻译成Laravel。

相关问题