Laravel Eloquent Inner Join on Self Referencing Table(自引用表上的 Laravel Eloquent Inner Join)
问题描述
我正在尝试使用雄辩的模型将用户表内部连接到自身.我到处寻找,但似乎无法找到解决此不创建两个查询的解决方案,这正是我目前正在做的事情.
I'm trying to inner join a users table to itself using an eloquent model. I've looked everywhere but can't seem to find a solution to this without creating two queries which is what I am currently doing.
users 表通过 pivot 表 friends
我尝试将 Users::class
内部连接到自身,但失败了.在内部连接中我能得到的最好结果是运行两个查询并查看是否有重叠.因此,一个人联系了另一个人,反之亦然.
I tried and failed inner joining Users::class
to itself. The best I can get at an inner join is by running two queries and seeing if there is an overlap. Thus one person has reached out to the other and vice versa.
friends | users
----------|------
send_id | id
receive_id| name
is_blocked|
样本数据 &预期结果
users.id | name
---------|------
1 | foo
2 | bar
3 | baz
friends
send_id | receive_id | is_blocked
--------|------------|-----------
1 | 2 | 0
2 | 1 | 0
1 | 3 | 0
3 | 1 | 1
2 | 3 | 0
用户应该有一种称为朋友的雄辩关系.它应该是您期望的来自 requestedFriends
或 receivedFriends
刚刚加入的结果.
The user should have an eloquent relationship called friends. It should be what you expect comes out of requestedFriends
or receivedFriends
just joined.
foo->friends
returns `baz`
bar->friends
returns `foo`
baz->friends
returns empty collection
正在使用
// User.php
public function requestedFriends()
{
$left = $this->belongsToMany(User::class, 'friends','send_id','receive_id')
->withPivot('is_blocked')
->wherePivot('is_blocked','=', 0)
->withTimestamps();
return $left;
}
public function receivedFriends()
{
$right = $this->belongsToMany(User::class, 'friends','receive_id','send_id')
->withPivot('is_blocked')
->wherePivot('is_blocked','=', 0)
->withTimestamps();
return $right;
}
public function friends()
{
$reqFriends = $this->requestedFriends()->get();
$recFriends = $this->receivedFriends()->get();
$req = explode(",",$recFriends->implode('id', ', '));
$intersect = $reqFriends->whereIn('id', $req);
return $intersect;
}
目前的研究
Laravel 多对多自引用表只能以一种方式工作 ->老问题,但仍然相关
Research so far
Laravel Many to many self referencing table only works one way -> old question, but still relevant
https://github.com/laravel/framework/issues/441#issuecomment-14213883 ->是的,它有效……但只有一种方式.
https://github.com/laravel/framework/issues/441#issuecomment-14213883 -> yep, it works… but one way.
https://laravel.com/docs/5.8/collections#method-wherein目前我找到的唯一方法是雄辩地做到这一点.
https://laravel.com/docs/5.8/collections#method-wherein currently the only way I have found to do this in eloquent.
https://laravel.com/docs/5.7/queries#joins->理想情况下我会找到一个在自身上使用内连接的解决方案,但无论我用哪种方式放置 ID,我都无法找到解决方案.
https://laravel.com/docs/5.7/queries#joins -> Ideally I would find a solution using an innerjoin onto itself, but no matter which way I put the id's I couldn't get a solution to work.
一个解决方案是在 laravel 5.7 或 5.8 中使用 eloquent 来内部连接自引用表,其中关系仅在 send_id
& 时存在.receive_id
出现在朋友表的多行中.
A solution would inner join a self referencing table using eloquent in laravel 5.7 or 5.8, where a relationship only exists if send_id
& receive_id
are present on multiple rows in the friends table.
或
以某种方式让社区知道这是不可能的.
Somehow let the community know that this can't be done.
提前致谢!
推荐答案
我还没有详细检查这个解决方案,但我已经写了一个ManyToMany"类扩展了 laravel 附带的BelongsToMany"类,它似乎工作.该类基本上只是覆盖了get"方法,复制了原始查询,反转"了它,并对原始查询执行了联合".
I have not checked this solution in every detail yet, but I have written a "ManyToMany" Class extending the "BelongsToMany" Class shipped with laravel, which appears to work. The class basically just overrides the "get" method, duplicating the original query, "inverting" it and just performing a "union" on the original query.
<?php
namespace AppDatabaseEloquentRelations;
use IlluminateDatabaseEloquentRelationsBelongsToMany;
class ManyToMany extends BelongsToMany
{
/**
* Execute the query as a "select" statement.
*
* @param array $columns
* @return IlluminateDatabaseEloquentCollection
*/
public function get($columns = ['*'])
{
// duplicated from "BelongsToMany"
$builder = $this->query->applyScopes();
$columns = $builder->getQuery()->columns ? [] : $columns;
// Adjustments for "Many to Many on self": do not get the resulting models here directly, but rather
// just set the columns to select and do some adjustments to also select the "inverse" records
$builder->addSelect(
$this->shouldSelect($columns)
);
// backup order directives
$orders = $builder->getQuery()->orders;
$builder->getQuery()->orders = [];
// clone the original query
$query2 = clone($this->query);
// determine the columns to select - same as in original query, but with inverted pivot key names
$query2->select(
$this->shouldSelectInverse( $columns )
);
// remove the inner join and build a new one, this time using the "foreign" pivot key
$query2->getQuery()->joins = array();
$baseTable = $this->related->getTable();
$key = $baseTable.'.'.$this->relatedKey;
$query2->join($this->table, $key, '=', $this->getQualifiedForeignPivotKeyName());
// go through all where conditions and "invert" the one relevant for the inner join
foreach( $query2->getQuery()->wheres as &$where ) {
if(
$where['type'] == 'Basic'
&& $where['column'] == $this->getQualifiedForeignPivotKeyName()
&& $where['operator'] == '='
&& $where['value'] == $this->parent->{$this->parentKey}
) {
$where['column'] = $this->getQualifiedRelatedPivotKeyName();
break;
}
}
// add the duplicated and modified and adjusted query to the original query with union
$builder->getQuery()->union($query2);
// reapply orderings so that they are used for the "union" rather than just the individual queries
foreach($orders as $ord)
$builder->getQuery()->orderBy($ord['column'], $ord['direction']);
// back to "normal" - get the models
$models = $builder->getModels();
$this->hydratePivotRelation($models);
// If we actually found models we will also eager load any relationships that
// have been specified as needing to be eager loaded. This will solve the
// n + 1 query problem for the developer and also increase performance.
if (count($models) > 0) {
$models = $builder->eagerLoadRelations($models);
}
return $this->related->newCollection($models);
}
/**
* Get the select columns for the relation query.
*
* @param array $columns
* @return array
*/
protected function shouldSelectInverse(array $columns = ['*'])
{
if ($columns == ['*']) {
$columns = [$this->related->getTable().'.*'];
}
return array_merge($columns, $this->aliasedPivotColumnsInverse());
}
/**
* Get the pivot columns for the relation.
*
* "pivot_" is prefixed ot each column for easy removal later.
*
* @return array
*/
protected function aliasedPivotColumnsInverse()
{
$collection = collect( $this->pivotColumns )->map(function ($column) {
return $this->table.'.'.$column.' as pivot_'.$column;
});
$collection->prepend(
$this->table.'.'.$this->relatedPivotKey.' as pivot_'.$this->foreignPivotKey
);
$collection->prepend(
$this->table.'.'.$this->foreignPivotKey.' as pivot_'.$this->relatedPivotKey
);
return $collection->unique()->all();
}
}
这篇关于自引用表上的 Laravel Eloquent Inner Join的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:自引用表上的 Laravel Eloquent Inner Join
基础教程推荐
- PHP 守护进程/worker 环境 2022-01-01
- 在 CakePHP 2.0 中使用 Html Helper 时未定义的变量 2021-01-01
- 在 yii2 中迁移时出现异常“找不到驱动程序" 2022-01-01
- 找不到类“AppHttpControllersDB",我也无法使用新模型 2022-01-01
- 如何在 XAMPP 上启用 mysqli? 2021-01-01
- Doctrine 2 - 在多对多关系中记录更改 2022-01-01
- 使用 PDO 转义列名 2021-01-01
- phpmyadmin 错误“#1062 - 密钥 1 的重复条目‘1’" 2022-01-01
- HTTP 与 FTP 上传 2021-01-01
- 如何在 Symfony 和 Doctrine 中实现多对多和一对多? 2022-01-01