[增添]添加了datasource的setting数据库以及默认值
This commit is contained in:
26
vendor/kirschbaum-development/eloquent-power-joins/src/EloquentJoins.php
vendored
Normal file
26
vendor/kirschbaum-development/eloquent-power-joins/src/EloquentJoins.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Kirschbaum\PowerJoins;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentQueryBuilder;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||
use Kirschbaum\PowerJoins\Mixins\JoinRelationship;
|
||||
use Kirschbaum\PowerJoins\Mixins\QueryBuilderExtraMethods;
|
||||
use Kirschbaum\PowerJoins\Mixins\QueryRelationshipExistence;
|
||||
use Kirschbaum\PowerJoins\Mixins\RelationshipsExtraMethods;
|
||||
|
||||
class EloquentJoins
|
||||
{
|
||||
/**
|
||||
* Register macros with Eloquent.
|
||||
*/
|
||||
public static function registerEloquentMacros()
|
||||
{
|
||||
EloquentQueryBuilder::mixin(new JoinRelationship);
|
||||
EloquentQueryBuilder::mixin(new QueryRelationshipExistence);
|
||||
QueryBuilder::mixin(new QueryBuilderExtraMethods);
|
||||
|
||||
Relation::mixin(new RelationshipsExtraMethods);
|
||||
}
|
||||
}
|
||||
23
vendor/kirschbaum-development/eloquent-power-joins/src/FakeJoinCallback.php
vendored
Normal file
23
vendor/kirschbaum-development/eloquent-power-joins/src/FakeJoinCallback.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Kirschbaum\PowerJoins;
|
||||
|
||||
/**
|
||||
* @method static as(string $alias)
|
||||
*/
|
||||
class FakeJoinCallback extends PowerJoinClause
|
||||
{
|
||||
public function getAlias(): ?string
|
||||
{
|
||||
return $this->alias;
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if ($name === 'as') {
|
||||
$this->alias = $arguments[0];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
122
vendor/kirschbaum-development/eloquent-power-joins/src/JoinsHelper.php
vendored
Normal file
122
vendor/kirschbaum-development/eloquent-power-joins/src/JoinsHelper.php
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Kirschbaum\PowerJoins;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
class JoinsHelper
|
||||
{
|
||||
static array $instances = [];
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static function make(): static
|
||||
{
|
||||
$objects = array_map(fn ($object) => spl_object_id($object), func_get_args());
|
||||
|
||||
return static::$instances[implode('-', $objects)] ??= new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache to not join the same relationship twice.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private array $joinRelationshipCache = [];
|
||||
|
||||
/**
|
||||
* Join method map.
|
||||
*/
|
||||
public static $joinMethodsMap = [
|
||||
'join' => 'powerJoin',
|
||||
'leftJoin' => 'leftPowerJoin',
|
||||
'rightJoin' => 'rightPowerJoin',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Format the join callback.
|
||||
*
|
||||
* @param mixed $callback
|
||||
* @return mixed
|
||||
*/
|
||||
public function formatJoinCallback($callback)
|
||||
{
|
||||
if (is_string($callback)) {
|
||||
return function ($join) use ($callback) {
|
||||
$join->as($callback);
|
||||
};
|
||||
}
|
||||
|
||||
return $callback;
|
||||
}
|
||||
|
||||
public function generateAliasForRelationship(Relation $relation, string $relationName): array|string
|
||||
{
|
||||
if ($relation instanceof BelongsToMany || $relation instanceof HasManyThrough) {
|
||||
return [
|
||||
md5($relationName.'table1'.time()),
|
||||
md5($relationName.'table2'.time()),
|
||||
];
|
||||
}
|
||||
|
||||
return md5($relationName.time());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the join alias name from all the different options.
|
||||
*/
|
||||
public function getAliasName(bool $useAlias, Relation $relation, string $relationName, string $tableName, $callback): null|string|array
|
||||
{
|
||||
if ($callback) {
|
||||
if (is_callable($callback)) {
|
||||
$fakeJoinCallback = new FakeJoinCallback($relation->getBaseQuery(), 'inner', $tableName);
|
||||
$callback($fakeJoinCallback);
|
||||
|
||||
if ($fakeJoinCallback->getAlias()) {
|
||||
return $fakeJoinCallback->getAlias();
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($callback) && isset($callback[$tableName])) {
|
||||
$fakeJoinCallback = new FakeJoinCallback($relation->getBaseQuery(), 'inner', $tableName);
|
||||
$callback[$tableName]($fakeJoinCallback);
|
||||
|
||||
if ($fakeJoinCallback->getAlias()) {
|
||||
return $fakeJoinCallback->getAlias();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $useAlias
|
||||
? $this->generateAliasForRelationship($relation, $relationName)
|
||||
: null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the relationship was already joined.
|
||||
*/
|
||||
public function relationshipAlreadyJoined($model, string $relation): bool
|
||||
{
|
||||
return isset($this->joinRelationshipCache[spl_object_id($model)][$relation]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the relationship as already joined.
|
||||
*/
|
||||
public function markRelationshipAsAlreadyJoined($model, string $relation): void
|
||||
{
|
||||
$this->joinRelationshipCache[spl_object_id($model)][$relation] = true;
|
||||
}
|
||||
|
||||
public function clear(): void
|
||||
{
|
||||
$this->joinRelationshipCache = [];
|
||||
}
|
||||
}
|
||||
560
vendor/kirschbaum-development/eloquent-power-joins/src/Mixins/JoinRelationship.php
vendored
Normal file
560
vendor/kirschbaum-development/eloquent-power-joins/src/Mixins/JoinRelationship.php
vendored
Normal file
@@ -0,0 +1,560 @@
|
||||
<?php
|
||||
|
||||
namespace Kirschbaum\PowerJoins\Mixins;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Kirschbaum\PowerJoins\JoinsHelper;
|
||||
use Kirschbaum\PowerJoins\PowerJoinClause;
|
||||
use Kirschbaum\PowerJoins\StaticCache;
|
||||
|
||||
/**
|
||||
* @mixin Builder
|
||||
* @method \Illuminate\Database\Eloquent\Model getModel()
|
||||
* @property \Illuminate\Database\Eloquent\Builder $query
|
||||
*/
|
||||
class JoinRelationship
|
||||
{
|
||||
/**
|
||||
* New clause for making joins, where we pass the model to the joiner class.
|
||||
*/
|
||||
public function powerJoin(): Closure
|
||||
{
|
||||
return function ($table, $first, $operator = null, $second = null, $type = 'inner', $where = false): static {
|
||||
$model = $operator instanceof Model ? $operator : null;
|
||||
$join = $this->newPowerJoinClause($this->query, $type, $table, $model);
|
||||
|
||||
// If the first "column" of the join is really a Closure instance the developer
|
||||
// is trying to build a join with a complex "on" clause containing more than
|
||||
// one condition, so we'll add the join and call a Closure with the query.
|
||||
if ($first instanceof Closure) {
|
||||
$first($join);
|
||||
|
||||
$this->query->joins[] = $join;
|
||||
|
||||
$this->query->addBinding($join->getBindings(), 'join');
|
||||
}
|
||||
|
||||
// If the column is simply a string, we can assume the join simply has a basic
|
||||
// "on" clause with a single condition. So we will just build the join with
|
||||
// this simple join clauses attached to it. There is not a join callback.
|
||||
else {
|
||||
$method = $where ? 'where' : 'on';
|
||||
|
||||
$this->query->joins[] = $join->$method($first, $operator, $second);
|
||||
|
||||
$this->query->addBinding($join->getBindings(), 'join');
|
||||
}
|
||||
|
||||
return $this;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* New clause for making joins, where we pass the model to the joiner class.
|
||||
*/
|
||||
public function leftPowerJoin(): Closure
|
||||
{
|
||||
return function ($table, $first, $operator = null, $second = null) {
|
||||
return $this->powerJoin($table, $first, $operator, $second, 'left');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* New clause for making joins, where we pass the model to the joiner class.
|
||||
*/
|
||||
public function rightPowerJoin(): Closure
|
||||
{
|
||||
return function ($table, $first, $operator = null, $second = null) {
|
||||
return $this->powerJoin($table, $first, $operator, $second, 'right');
|
||||
};
|
||||
}
|
||||
|
||||
public function newPowerJoinClause(): Closure
|
||||
{
|
||||
return function (QueryBuilder $parentQuery, $type, $table, Model $model = null) {
|
||||
return new PowerJoinClause($parentQuery, $type, $table, $model);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Join the relationship(s).
|
||||
*/
|
||||
public function joinRelationship(): Closure
|
||||
{
|
||||
return function (
|
||||
$relationName,
|
||||
$callback = null,
|
||||
$joinType = 'join',
|
||||
$useAlias = false,
|
||||
bool $disableExtraConditions = false,
|
||||
string $morphable = null
|
||||
) {
|
||||
$joinType = JoinsHelper::$joinMethodsMap[$joinType] ?? $joinType;
|
||||
$useAlias = is_string($callback) ? false : $useAlias;
|
||||
$joinHelper = JoinsHelper::make($this->getModel());
|
||||
$callback = $joinHelper->formatJoinCallback($callback);
|
||||
|
||||
$this->getQuery()->beforeQuery(function () use ($joinHelper) {
|
||||
$joinHelper->clear();
|
||||
});
|
||||
|
||||
if (is_null($this->getSelect())) {
|
||||
$this->select(sprintf('%s.*', $this->getModel()->getTable()));
|
||||
}
|
||||
|
||||
if (Str::contains($relationName, '.')) {
|
||||
$this->joinNestedRelationship($relationName, $callback, $joinType, $useAlias, $disableExtraConditions);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$relationCallback = $callback;
|
||||
if ($callback && is_array($callback) && isset($callback[$relationName]) && is_array($callback[$relationName])) {
|
||||
$relationCallback = $callback[$relationName];
|
||||
}
|
||||
|
||||
$relation = $this->getModel()->{$relationName}();
|
||||
$relationQuery = $relation->getQuery();
|
||||
$alias = $joinHelper->getAliasName(
|
||||
$useAlias,
|
||||
$relation,
|
||||
$relationName,
|
||||
$relationQuery->getModel()->getTable(),
|
||||
$relationCallback
|
||||
);
|
||||
|
||||
if ($relation instanceof BelongsToMany && !is_array($alias)) {
|
||||
$extraAlias = $joinHelper->getAliasName(
|
||||
$useAlias,
|
||||
$relation,
|
||||
$relationName,
|
||||
$relation->getTable(),
|
||||
$relationCallback
|
||||
);
|
||||
$alias = [$extraAlias, $alias];
|
||||
}
|
||||
|
||||
$aliasString = is_array($alias) ? implode('.', $alias) : $alias;
|
||||
$useAlias = $alias ? true : $useAlias;
|
||||
|
||||
$relationJoinCache = $alias
|
||||
? "{$aliasString}.{$relationQuery->getModel()->getTable()}.{$relationName}"
|
||||
: "{$relationQuery->getModel()->getTable()}.{$relationName}";
|
||||
|
||||
if ($joinHelper->relationshipAlreadyJoined($this->getModel(), $relationJoinCache)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($useAlias) {
|
||||
StaticCache::setTableAliasForModel($relation->getModel(), $alias);
|
||||
}
|
||||
|
||||
$joinHelper->markRelationshipAsAlreadyJoined($this->getModel(), $relationJoinCache);
|
||||
StaticCache::clear();
|
||||
|
||||
$relation->performJoinForEloquentPowerJoins(
|
||||
builder: $this,
|
||||
joinType: $joinType,
|
||||
callback: $relationCallback,
|
||||
alias: $alias,
|
||||
disableExtraConditions: $disableExtraConditions,
|
||||
morphable: $morphable,
|
||||
);
|
||||
|
||||
return $this;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Join the relationship(s) using table aliases.
|
||||
*/
|
||||
public function joinRelationshipUsingAlias(): Closure
|
||||
{
|
||||
return function ($relationName, $callback = null, bool $disableExtraConditions = false) {
|
||||
return $this->joinRelationship($relationName, $callback, 'join', true, $disableExtraConditions);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Left join the relationship(s) using table aliases.
|
||||
*/
|
||||
public function leftJoinRelationshipUsingAlias(): Closure
|
||||
{
|
||||
return function ($relationName, $callback = null, bool $disableExtraConditions = false) {
|
||||
return $this->joinRelationship($relationName, $callback, 'leftJoin', true, $disableExtraConditions);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Right join the relationship(s) using table aliases.
|
||||
*/
|
||||
public function rightJoinRelationshipUsingAlias(): Closure
|
||||
{
|
||||
return function ($relationName, $callback = null, bool $disableExtraConditions = false) {
|
||||
return $this->joinRelationship($relationName, $callback, 'rightJoin', true, $disableExtraConditions);
|
||||
};
|
||||
}
|
||||
|
||||
public function joinRelation(): Closure
|
||||
{
|
||||
return function (
|
||||
$relationName,
|
||||
$callback = null,
|
||||
$joinType = 'join',
|
||||
$useAlias = false,
|
||||
bool $disableExtraConditions = false
|
||||
) {
|
||||
return $this->joinRelationship($relationName, $callback, $joinType, $useAlias, $disableExtraConditions);
|
||||
};
|
||||
}
|
||||
|
||||
public function leftJoinRelationship(): Closure
|
||||
{
|
||||
return function ($relation, $callback = null, $useAlias = false, bool $disableExtraConditions = false) {
|
||||
return $this->joinRelationship($relation, $callback, 'leftJoin', $useAlias, $disableExtraConditions);
|
||||
};
|
||||
}
|
||||
|
||||
public function leftJoinRelation(): Closure
|
||||
{
|
||||
return function ($relation, $callback = null, $useAlias = false, bool $disableExtraConditions = false) {
|
||||
return $this->joinRelationship($relation, $callback, 'leftJoin', $useAlias, $disableExtraConditions);
|
||||
};
|
||||
}
|
||||
|
||||
public function rightJoinRelationship(): Closure
|
||||
{
|
||||
return function ($relation, $callback = null, $useAlias = false, bool $disableExtraConditions = false) {
|
||||
return $this->joinRelationship($relation, $callback, 'rightJoin', $useAlias, $disableExtraConditions);
|
||||
};
|
||||
}
|
||||
|
||||
public function rightJoinRelation(): Closure
|
||||
{
|
||||
return function ($relation, $callback = null, $useAlias = false, bool $disableExtraConditions = false) {
|
||||
return $this->joinRelationship($relation, $callback, 'rightJoin', $useAlias, $disableExtraConditions);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Join nested relationships.
|
||||
*/
|
||||
public function joinNestedRelationship(): Closure
|
||||
{
|
||||
return function (
|
||||
string $relationships,
|
||||
$callback = null,
|
||||
$joinType = 'join',
|
||||
$useAlias = false,
|
||||
bool $disableExtraConditions = false
|
||||
) {
|
||||
$relations = explode('.', $relationships);
|
||||
$joinHelper = JoinsHelper::make($this->getModel());
|
||||
/** @var Relation */
|
||||
$latestRelation = null;
|
||||
|
||||
$part = [];
|
||||
foreach ($relations as $relationName) {
|
||||
$part[] = $relationName;
|
||||
$fullRelationName = join(".", $part);
|
||||
|
||||
$currentModel = $latestRelation ? $latestRelation->getModel() : $this->getModel();
|
||||
$relation = $currentModel->{$relationName}();
|
||||
$relationCallback = null;
|
||||
|
||||
if ($callback && is_array($callback) && isset($callback[$relationName])) {
|
||||
$relationCallback = $callback[$relationName];
|
||||
}
|
||||
|
||||
if ($callback && is_array($callback) && isset($callback[$fullRelationName])) {
|
||||
$relationCallback = $callback[$fullRelationName];
|
||||
}
|
||||
|
||||
$alias = $joinHelper->getAliasName(
|
||||
$useAlias,
|
||||
$relation,
|
||||
$relationName,
|
||||
$relation->getQuery()->getModel()->getTable(),
|
||||
$relationCallback
|
||||
);
|
||||
|
||||
if ($alias && $relation instanceof BelongsToMany && !is_array($alias)) {
|
||||
$extraAlias = $joinHelper->getAliasName(
|
||||
$useAlias,
|
||||
$relation,
|
||||
$relationName,
|
||||
$relation->getTable(),
|
||||
$relationCallback
|
||||
);
|
||||
|
||||
$alias = [$extraAlias, $alias];
|
||||
}
|
||||
|
||||
$aliasString = is_array($alias) ? implode('.', $alias) : $alias;
|
||||
$useAlias = $alias ? true : $useAlias;
|
||||
|
||||
if ($alias) {
|
||||
$relationJoinCache = $latestRelation
|
||||
? "{$aliasString}.{$relation->getQuery()->getModel()->getTable()}.{$latestRelation->getModel()->getTable()}.{$relationName}"
|
||||
: "{$aliasString}.{$relation->getQuery()->getModel()->getTable()}.{$relationName}";
|
||||
} else {
|
||||
$relationJoinCache = $latestRelation
|
||||
? "{$relation->getQuery()->getModel()->getTable()}.{$latestRelation->getModel()->getTable()}.{$relationName}"
|
||||
: "{$relation->getQuery()->getModel()->getTable()}.{$relationName}";
|
||||
}
|
||||
|
||||
if ($useAlias) {
|
||||
StaticCache::setTableAliasForModel($relation->getModel(), $alias);
|
||||
}
|
||||
|
||||
|
||||
if ($joinHelper->relationshipAlreadyJoined($this->getModel(), $relationJoinCache)) {
|
||||
$latestRelation = $relation;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$relation->performJoinForEloquentPowerJoins(
|
||||
$this,
|
||||
$joinType,
|
||||
$relationCallback,
|
||||
$alias,
|
||||
$disableExtraConditions
|
||||
);
|
||||
|
||||
$latestRelation = $relation;
|
||||
$joinHelper->markRelationshipAsAlreadyJoined($this->getModel(), $relationJoinCache);
|
||||
}
|
||||
|
||||
StaticCache::clear();
|
||||
return $this;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Order by a field in the defined relationship.
|
||||
*/
|
||||
public function orderByPowerJoins(): Closure
|
||||
{
|
||||
return function ($sort, $direction = 'asc', $aggregation = null, $joinType = 'join') {
|
||||
if (is_array($sort)) {
|
||||
$relationships = explode('.', $sort[0]);
|
||||
$column = $sort[1];
|
||||
$latestRelationshipName = $relationships[count($relationships) - 1];
|
||||
} else {
|
||||
$relationships = explode('.', $sort);
|
||||
$column = array_pop($relationships);
|
||||
$latestRelationshipName = $relationships[count($relationships) - 1];
|
||||
}
|
||||
|
||||
$this->joinRelationship(implode('.', $relationships), null, $joinType);
|
||||
|
||||
$latestRelationshipModel = array_reduce($relationships, function ($model, $relationshipName) {
|
||||
return $model->$relationshipName()->getModel();
|
||||
}, $this->getModel());
|
||||
|
||||
if ($aggregation) {
|
||||
$aliasName = sprintf(
|
||||
'%s_%s_%s',
|
||||
$latestRelationshipModel->getTable(),
|
||||
$column,
|
||||
$aggregation
|
||||
);
|
||||
|
||||
$this->selectRaw(
|
||||
sprintf(
|
||||
'%s(%s.%s) as %s',
|
||||
$aggregation,
|
||||
$latestRelationshipModel->getTable(),
|
||||
$column,
|
||||
$aliasName
|
||||
)
|
||||
)
|
||||
->groupBy(sprintf('%s.%s', $this->getModel()->getTable(), $this->getModel()->getKeyName()))
|
||||
->orderBy(DB::raw(sprintf('%s', $aliasName)), $direction);
|
||||
} else {
|
||||
if ($column instanceof Expression) {
|
||||
$this->orderBy($column, $direction);
|
||||
} else {
|
||||
$this->orderBy(
|
||||
sprintf('%s.%s', $latestRelationshipModel->getTable(), $column),
|
||||
$direction
|
||||
);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
};
|
||||
}
|
||||
|
||||
public function orderByLeftPowerJoins(): Closure
|
||||
{
|
||||
return function ($sort, $direction = 'asc') {
|
||||
return $this->orderByPowerJoins($sort, $direction, null, 'leftJoin');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Order by the COUNT aggregation using joins.
|
||||
*/
|
||||
public function orderByPowerJoinsCount(): Closure
|
||||
{
|
||||
return function ($sort, $direction = 'asc') {
|
||||
return $this->orderByPowerJoins($sort, $direction, 'COUNT');
|
||||
};
|
||||
}
|
||||
|
||||
public function orderByLeftPowerJoinsCount(): Closure
|
||||
{
|
||||
return function ($sort, $direction = 'asc') {
|
||||
return $this->orderByPowerJoins($sort, $direction, 'COUNT', 'leftJoin');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Order by the SUM aggregation using joins.
|
||||
*/
|
||||
public function orderByPowerJoinsSum(): Closure
|
||||
{
|
||||
return function ($sort, $direction = 'asc') {
|
||||
return $this->orderByPowerJoins($sort, $direction, 'SUM');
|
||||
};
|
||||
}
|
||||
|
||||
public function orderByLeftPowerJoinsSum(): Closure
|
||||
{
|
||||
return function ($sort, $direction = 'asc') {
|
||||
return $this->orderByPowerJoins($sort, $direction, 'SUM', 'leftJoin');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Order by the AVG aggregation using joins.
|
||||
*/
|
||||
public function orderByPowerJoinsAvg(): Closure
|
||||
{
|
||||
return function ($sort, $direction = 'asc') {
|
||||
return $this->orderByPowerJoins($sort, $direction, 'AVG');
|
||||
};
|
||||
}
|
||||
|
||||
public function orderByLeftPowerJoinsAvg(): Closure
|
||||
{
|
||||
return function ($sort, $direction = 'asc') {
|
||||
return $this->orderByPowerJoins($sort, $direction, 'AVG', 'leftJoin');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Order by the MIN aggregation using joins.
|
||||
*/
|
||||
public function orderByPowerJoinsMin(): Closure
|
||||
{
|
||||
return function ($sort, $direction = 'asc') {
|
||||
return $this->orderByPowerJoins($sort, $direction, 'MIN');
|
||||
};
|
||||
}
|
||||
|
||||
public function orderByLeftPowerJoinsMin(): Closure
|
||||
{
|
||||
return function ($sort, $direction = 'asc') {
|
||||
return $this->orderByPowerJoins($sort, $direction, 'MIN', 'leftJoin');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Order by the MAX aggregation using joins.
|
||||
*/
|
||||
public function orderByPowerJoinsMax(): Closure
|
||||
{
|
||||
return function ($sort, $direction = 'asc') {
|
||||
return $this->orderByPowerJoins($sort, $direction, 'MAX');
|
||||
};
|
||||
}
|
||||
|
||||
public function orderByLeftPowerJoinsMax(): Closure
|
||||
{
|
||||
return function ($sort, $direction = 'asc') {
|
||||
return $this->orderByPowerJoins($sort, $direction, 'MAX', 'leftJoin');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as Laravel 'has`, but using joins instead of where exists.
|
||||
*/
|
||||
public function powerJoinHas(): Closure
|
||||
{
|
||||
return function ($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure|array $callback = null, string $morphable = null): static {
|
||||
if (is_null($this->getSelect())) {
|
||||
$this->select(sprintf('%s.*', $this->getModel()->getTable()));
|
||||
}
|
||||
|
||||
if (is_null($this->getGroupBy())) {
|
||||
$this->groupBy($this->getModel()->getQualifiedKeyName());
|
||||
}
|
||||
|
||||
if (is_string($relation)) {
|
||||
if (Str::contains($relation, '.')) {
|
||||
$this->hasNestedUsingJoins($relation, $operator, $count, 'and', $callback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$relation = $this->getRelationWithoutConstraintsProxy($relation);
|
||||
}
|
||||
$relation->performJoinForEloquentPowerJoins($this, 'leftPowerJoin', $callback, morphable: $morphable);
|
||||
$relation->performHavingForEloquentPowerJoins($this, $operator, $count, morphable: $morphable);
|
||||
return $this;
|
||||
};
|
||||
}
|
||||
|
||||
public function hasNestedUsingJoins(): Closure
|
||||
{
|
||||
return function ($relations, $operator = '>=', $count = 1, $boolean = 'and', Closure|array $callback = null): static {
|
||||
$relations = explode('.', $relations);
|
||||
|
||||
/** @var Relation */
|
||||
$latestRelation = null;
|
||||
|
||||
foreach ($relations as $index => $relation) {
|
||||
$relationName = $relation;
|
||||
|
||||
if (!$latestRelation) {
|
||||
$relation = $this->getRelationWithoutConstraintsProxy($relation);
|
||||
} else {
|
||||
$relation = $latestRelation->getModel()->query()->getRelationWithoutConstraintsProxy($relation);
|
||||
}
|
||||
|
||||
$relation->performJoinForEloquentPowerJoins($this, 'leftPowerJoin', is_callable($callback) ? $callback : $callback[$relationName] ?? null);
|
||||
|
||||
if (count($relations) === ($index + 1)) {
|
||||
$relation->performHavingForEloquentPowerJoins($this, $operator, $count);
|
||||
}
|
||||
|
||||
$latestRelation = $relation;
|
||||
}
|
||||
return $this;
|
||||
};
|
||||
}
|
||||
|
||||
public function powerJoinDoesntHave(): Closure
|
||||
{
|
||||
return function ($relation, $boolean = 'and', Closure $callback = null) {
|
||||
return $this->powerJoinHas($relation, '<', 1, $boolean, $callback);
|
||||
};
|
||||
}
|
||||
|
||||
public function powerJoinWhereHas(): Closure
|
||||
{
|
||||
return function ($relation, $callback = null, $operator = '>=', $count = 1) {
|
||||
return $this->powerJoinHas($relation, $operator, $count, 'and', $callback);
|
||||
};
|
||||
}
|
||||
}
|
||||
20
vendor/kirschbaum-development/eloquent-power-joins/src/Mixins/QueryBuilderExtraMethods.php
vendored
Normal file
20
vendor/kirschbaum-development/eloquent-power-joins/src/Mixins/QueryBuilderExtraMethods.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Kirschbaum\PowerJoins\Mixins;
|
||||
|
||||
class QueryBuilderExtraMethods
|
||||
{
|
||||
public function getGroupBy()
|
||||
{
|
||||
return function () {
|
||||
return $this->groups;
|
||||
};
|
||||
}
|
||||
|
||||
public function getSelect()
|
||||
{
|
||||
return function () {
|
||||
return $this->columns;
|
||||
};
|
||||
}
|
||||
}
|
||||
31
vendor/kirschbaum-development/eloquent-power-joins/src/Mixins/QueryRelationshipExistence.php
vendored
Normal file
31
vendor/kirschbaum-development/eloquent-power-joins/src/Mixins/QueryRelationshipExistence.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Kirschbaum\PowerJoins\Mixins;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
class QueryRelationshipExistence
|
||||
{
|
||||
public function getGroupBy()
|
||||
{
|
||||
return function () {
|
||||
return $this->getQuery()->getGroupBy();
|
||||
};
|
||||
}
|
||||
|
||||
public function getSelect()
|
||||
{
|
||||
return function () {
|
||||
return $this->getQuery()->getSelect();
|
||||
};
|
||||
}
|
||||
|
||||
protected function getRelationWithoutConstraintsProxy()
|
||||
{
|
||||
return function ($relation) {
|
||||
return Relation::noConstraints(function () use ($relation) {
|
||||
return $this->getModel()->{$relation}();
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
538
vendor/kirschbaum-development/eloquent-power-joins/src/Mixins/RelationshipsExtraMethods.php
vendored
Normal file
538
vendor/kirschbaum-development/eloquent-power-joins/src/Mixins/RelationshipsExtraMethods.php
vendored
Normal file
@@ -0,0 +1,538 @@
|
||||
<?php
|
||||
|
||||
namespace Kirschbaum\PowerJoins\Mixins;
|
||||
|
||||
use Stringable;
|
||||
use Illuminate\Support\Str;
|
||||
use Kirschbaum\PowerJoins\StaticCache;
|
||||
use Kirschbaum\PowerJoins\PowerJoinClause;
|
||||
use Kirschbaum\PowerJoins\Tests\Models\Post;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphOneOrMany;
|
||||
|
||||
/**
|
||||
* @method \Illuminate\Database\Eloquent\Model getModel()
|
||||
* @method string getTable()
|
||||
* @method string getForeignPivotKeyName()
|
||||
* @method string getRelatedPivotKeyName()
|
||||
* @method bool isOneOfMany()
|
||||
* @method \Illuminate\Database\Eloquent\Builder|void getOneOfManySubQuery()
|
||||
* @method \Illuminate\Database\Eloquent\Builder getQuery()
|
||||
* @method \Illuminate\Database\Eloquent\Model getThroughParent()
|
||||
* @method string getForeignKeyName()
|
||||
* @method string getMorphType()
|
||||
* @method string getMorphClass()
|
||||
* @method string getFirstKeyName()
|
||||
* @method string getQualifiedLocalKeyName()
|
||||
* @method string getExistenceCompareKey()
|
||||
* @mixin \Illuminate\Database\Eloquent\Relations\Relation
|
||||
* @mixin \Illuminate\Database\Eloquent\Relations\HasOneOrMany
|
||||
* @mixin \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
* @property \Illuminate\Database\Eloquent\Builder $query
|
||||
* @property \Illuminate\Database\Eloquent\Model $parent
|
||||
* @property \Illuminate\Database\Eloquent\Model $throughParent
|
||||
* @property string $foreignKey
|
||||
* @property string $parentKey
|
||||
* @property string $ownerKey
|
||||
* @property string $localKey
|
||||
* @property string $secondKey
|
||||
* @property string $secondLocalKey
|
||||
* @property \Illuminate\Database\Eloquent\Model $farParent
|
||||
*/
|
||||
class RelationshipsExtraMethods
|
||||
{
|
||||
/**
|
||||
* Perform the JOIN clause for eloquent power joins.
|
||||
*/
|
||||
public function performJoinForEloquentPowerJoins()
|
||||
{
|
||||
return function ($builder, $joinType = 'leftJoin', $callback = null, $alias = null, bool $disableExtraConditions = false, string $morphable = null) {
|
||||
return match (true) {
|
||||
$this instanceof MorphToMany => $this->performJoinForEloquentPowerJoinsForMorphToMany($builder, $joinType, $callback, $alias, $disableExtraConditions),
|
||||
$this instanceof BelongsToMany => $this->performJoinForEloquentPowerJoinsForBelongsToMany($builder, $joinType, $callback, $alias, $disableExtraConditions),
|
||||
$this instanceof MorphOneOrMany => $this->performJoinForEloquentPowerJoinsForMorph($builder, $joinType, $callback, $alias, $disableExtraConditions),
|
||||
$this instanceof HasMany || $this instanceof HasOne => $this->performJoinForEloquentPowerJoinsForHasMany($builder, $joinType, $callback, $alias, $disableExtraConditions),
|
||||
$this instanceof HasManyThrough => $this->performJoinForEloquentPowerJoinsForHasManyThrough($builder, $joinType, $callback, $alias, $disableExtraConditions),
|
||||
$this instanceof MorphTo => $this->performJoinForEloquentPowerJoinsForMorphTo($builder, $joinType, $callback, $alias, $disableExtraConditions, $morphable),
|
||||
default => $this->performJoinForEloquentPowerJoinsForBelongsTo($builder, $joinType, $callback, $alias, $disableExtraConditions),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the JOIN clause for the BelongsTo (or similar) relationships.
|
||||
*/
|
||||
protected function performJoinForEloquentPowerJoinsForBelongsTo()
|
||||
{
|
||||
return function ($query, $joinType, $callback = null, $alias = null, bool $disableExtraConditions = false) {
|
||||
$joinedTable = $this->query->getModel()->getTable();
|
||||
$parentTable = StaticCache::getTableOrAliasForModel($this->parent);
|
||||
|
||||
$query->{$joinType}($joinedTable, function ($join) use ($callback, $joinedTable, $parentTable, $alias, $disableExtraConditions) {
|
||||
if ($alias) {
|
||||
$join->as($alias);
|
||||
}
|
||||
|
||||
$join->on(
|
||||
"{$parentTable}.{$this->foreignKey}",
|
||||
'=',
|
||||
"{$joinedTable}.{$this->ownerKey}"
|
||||
);
|
||||
|
||||
if ($disableExtraConditions === false && $this->usesSoftDeletes($this->query->getModel())) {
|
||||
$join->whereNull("{$joinedTable}.{$this->query->getModel()->getDeletedAtColumn()}");
|
||||
}
|
||||
|
||||
if ($disableExtraConditions === false) {
|
||||
$this->applyExtraConditions($join);
|
||||
}
|
||||
|
||||
if ($callback && is_callable($callback)) {
|
||||
$callback($join);
|
||||
}
|
||||
}, $this->query->getModel());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the JOIN clause for the BelongsToMany (or similar) relationships.
|
||||
*/
|
||||
protected function performJoinForEloquentPowerJoinsForBelongsToMany()
|
||||
{
|
||||
return function ($builder, $joinType, $callback = null, $alias = null, bool $disableExtraConditions = false) {
|
||||
[$alias1, $alias2] = $alias;
|
||||
|
||||
$joinedTable = $alias1 ?: $this->getTable();
|
||||
$parentTable = StaticCache::getTableOrAliasForModel($this->parent);
|
||||
|
||||
$builder->{$joinType}($this->getTable(), function ($join) use ($callback, $joinedTable, $parentTable, $alias1) {
|
||||
if ($alias1) {
|
||||
$join->as($alias1);
|
||||
}
|
||||
|
||||
$join->on(
|
||||
"{$joinedTable}.{$this->getForeignPivotKeyName()}",
|
||||
'=',
|
||||
"{$parentTable}.{$this->parentKey}"
|
||||
);
|
||||
|
||||
if (is_array($callback) && isset($callback[$this->getTable()])) {
|
||||
$callback[$this->getTable()]($join);
|
||||
}
|
||||
});
|
||||
|
||||
$builder->{$joinType}($this->getModel()->getTable(), function ($join) use ($callback, $joinedTable, $alias2, $disableExtraConditions) {
|
||||
if ($alias2) {
|
||||
$join->as($alias2);
|
||||
}
|
||||
|
||||
$join->on(
|
||||
"{$this->getModel()->getTable()}.{$this->getModel()->getKeyName()}",
|
||||
'=',
|
||||
"{$joinedTable}.{$this->getRelatedPivotKeyName()}"
|
||||
);
|
||||
|
||||
if ($disableExtraConditions === false && $this->usesSoftDeletes($this->query->getModel())) {
|
||||
$join->whereNull($this->query->getModel()->getQualifiedDeletedAtColumn());
|
||||
}
|
||||
|
||||
// applying any extra conditions to the belongs to many relationship
|
||||
if ($disableExtraConditions === false) {
|
||||
$this->applyExtraConditions($join);
|
||||
}
|
||||
|
||||
if (is_array($callback) && isset($callback[$this->getModel()->getTable()])) {
|
||||
$callback[$this->getModel()->getTable()]($join);
|
||||
}
|
||||
}, $this->getModel());
|
||||
|
||||
return $this;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the JOIN clause for the MorphToMany (or similar) relationships.
|
||||
*/
|
||||
protected function performJoinForEloquentPowerJoinsForMorphToMany()
|
||||
{
|
||||
return function ($builder, $joinType, $callback = null, $alias = null, bool $disableExtraConditions = false) {
|
||||
[$alias1, $alias2] = $alias;
|
||||
|
||||
$joinedTable = $alias1 ?: $this->getTable();
|
||||
$parentTable = StaticCache::getTableOrAliasForModel($this->parent);
|
||||
|
||||
$builder->{$joinType}($this->getTable(), function ($join) use ($callback, $joinedTable, $parentTable, $alias1, $disableExtraConditions) {
|
||||
if ($alias1) {
|
||||
$join->as($alias1);
|
||||
}
|
||||
|
||||
$join->on(
|
||||
"{$joinedTable}.{$this->getForeignPivotKeyName()}",
|
||||
'=',
|
||||
"{$parentTable}.{$this->parentKey}"
|
||||
);
|
||||
|
||||
// applying any extra conditions to the belongs to many relationship
|
||||
if ($disableExtraConditions === false) {
|
||||
$this->applyExtraConditions($join);
|
||||
}
|
||||
|
||||
if (is_array($callback) && isset($callback[$this->getTable()])) {
|
||||
$callback[$this->getTable()]($join);
|
||||
}
|
||||
});
|
||||
|
||||
$builder->{$joinType}($this->getModel()->getTable(), function ($join) use ($callback, $joinedTable, $alias2, $disableExtraConditions) {
|
||||
if ($alias2) {
|
||||
$join->as($alias2);
|
||||
}
|
||||
|
||||
$join->on(
|
||||
"{$this->getModel()->getTable()}.{$this->getModel()->getKeyName()}",
|
||||
'=',
|
||||
"{$joinedTable}.{$this->getRelatedPivotKeyName()}"
|
||||
);
|
||||
|
||||
if ($disableExtraConditions === false && $this->usesSoftDeletes($this->query->getModel())) {
|
||||
$join->whereNull($this->query->getModel()->getQualifiedDeletedAtColumn());
|
||||
}
|
||||
|
||||
if (is_array($callback) && isset($callback[$this->getModel()->getTable()])) {
|
||||
$callback[$this->getModel()->getTable()]($join);
|
||||
}
|
||||
}, $this->getModel());
|
||||
|
||||
return $this;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the JOIN clause for the Morph (or similar) relationships.
|
||||
*/
|
||||
protected function performJoinForEloquentPowerJoinsForMorph()
|
||||
{
|
||||
return function ($builder, $joinType, $callback = null, $alias = null, bool $disableExtraConditions = false) {
|
||||
$builder->{$joinType}($this->getModel()->getTable(), function ($join) use ($callback, $disableExtraConditions) {
|
||||
$join->on(
|
||||
"{$this->getModel()->getTable()}.{$this->getForeignKeyName()}",
|
||||
'=',
|
||||
"{$this->parent->getTable()}.{$this->localKey}"
|
||||
)->where("{$this->getModel()->getTable()}.{$this->getMorphType()}", '=', $this->getMorphClass());
|
||||
|
||||
if ($disableExtraConditions === false && $this->usesSoftDeletes($this->query->getModel())) {
|
||||
$join->whereNull($this->query->getModel()->getQualifiedDeletedAtColumn());
|
||||
}
|
||||
|
||||
if ($disableExtraConditions === false) {
|
||||
$this->applyExtraConditions($join);
|
||||
}
|
||||
|
||||
if ($callback && is_callable($callback)) {
|
||||
$callback($join);
|
||||
}
|
||||
}, $this->getModel());
|
||||
|
||||
return $this;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the JOIN clause for when calling the morphTo method from the morphable class.
|
||||
*/
|
||||
protected function performJoinForEloquentPowerJoinsForMorphTo()
|
||||
{
|
||||
return function ($builder, $joinType, $callback = null, $alias = null, bool $disableExtraConditions = false, string $morphable = null) {
|
||||
$modelInstance = new $morphable;
|
||||
|
||||
$builder->{$joinType}($modelInstance->getTable(), function ($join) use ($modelInstance, $callback, $disableExtraConditions) {
|
||||
$join->on(
|
||||
"{$this->getModel()->getTable()}.{$this->getForeignKeyName()}",
|
||||
'=',
|
||||
"{$modelInstance->getTable()}.{$modelInstance->getKeyName()}"
|
||||
)->where("{$this->getModel()->getTable()}.{$this->getMorphType()}", '=', $modelInstance->getMorphClass());
|
||||
|
||||
if ($disableExtraConditions === false && $this->usesSoftDeletes($modelInstance)) {
|
||||
$join->whereNull($modelInstance->getQualifiedDeletedAtColumn());
|
||||
}
|
||||
|
||||
if ($disableExtraConditions === false) {
|
||||
$this->applyExtraConditions($join);
|
||||
}
|
||||
|
||||
if ($callback && is_callable($callback)) {
|
||||
$callback($join);
|
||||
}
|
||||
}, $modelInstance);
|
||||
|
||||
return $this;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the JOIN clause for the HasMany (or similar) relationships.
|
||||
*/
|
||||
protected function performJoinForEloquentPowerJoinsForHasMany()
|
||||
{
|
||||
return function ($builder, $joinType, $callback = null, $alias = null, bool $disableExtraConditions = false) {
|
||||
$joinedTable = $alias ?: $this->query->getModel()->getTable();
|
||||
$parentTable = StaticCache::getTableOrAliasForModel($this->parent);
|
||||
$isOneOfMany = method_exists($this, 'isOneOfMany') ? $this->isOneOfMany() : false;
|
||||
|
||||
if ($isOneOfMany) {
|
||||
foreach ($this->getOneOfManySubQuery()->getQuery()->columns as $column) {
|
||||
$builder->addSelect($column);
|
||||
}
|
||||
|
||||
$builder->take(1);
|
||||
}
|
||||
|
||||
$builder->{$joinType}($this->query->getModel()->getTable(), function ($join) use ($callback, $joinedTable, $parentTable, $alias, $disableExtraConditions) {
|
||||
if ($alias) {
|
||||
$join->as($alias);
|
||||
}
|
||||
|
||||
$join->on(
|
||||
$this->foreignKey,
|
||||
'=',
|
||||
"{$parentTable}.{$this->localKey}"
|
||||
);
|
||||
|
||||
if ($disableExtraConditions === false && $this->usesSoftDeletes($this->query->getModel())) {
|
||||
$join->whereNull(
|
||||
"{$joinedTable}.{$this->query->getModel()->getDeletedAtColumn()}"
|
||||
);
|
||||
}
|
||||
|
||||
if ($disableExtraConditions === false) {
|
||||
$this->applyExtraConditions($join);
|
||||
}
|
||||
|
||||
if ($callback && is_callable($callback)) {
|
||||
$callback($join);
|
||||
}
|
||||
}, $this->query->getModel());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the JOIN clause for the HasManyThrough relationships.
|
||||
*/
|
||||
protected function performJoinForEloquentPowerJoinsForHasManyThrough()
|
||||
{
|
||||
return function ($builder, $joinType, $callback = null, $alias = null, bool $disableExtraConditions = false) {
|
||||
[$alias1, $alias2] = $alias;
|
||||
$throughTable = $alias1 ?: $this->getThroughParent()->getTable();
|
||||
$farTable = $alias2 ?: $this->getModel()->getTable();
|
||||
|
||||
$builder->{$joinType}($this->getThroughParent()->getTable(), function (PowerJoinClause $join) use ($callback, $throughTable, $alias1, $disableExtraConditions) {
|
||||
if ($alias1) {
|
||||
$join->as($alias1);
|
||||
}
|
||||
|
||||
$join->on(
|
||||
"{$throughTable}.{$this->getFirstKeyName()}",
|
||||
'=',
|
||||
$this->getQualifiedLocalKeyName()
|
||||
);
|
||||
|
||||
if ($disableExtraConditions === false && $this->usesSoftDeletes($this->getThroughParent())) {
|
||||
$join->whereNull($this->getThroughParent()->getQualifiedDeletedAtColumn());
|
||||
}
|
||||
|
||||
if ($disableExtraConditions === false) {
|
||||
$this->applyExtraConditions($join);
|
||||
}
|
||||
|
||||
if (is_array($callback) && isset($callback[$this->getThroughParent()->getTable()])) {
|
||||
$callback[$this->getThroughParent()->getTable()]($join);
|
||||
}
|
||||
|
||||
if ($callback && is_callable($callback)) {
|
||||
$callback($join);
|
||||
}
|
||||
}, $this->getThroughParent());
|
||||
|
||||
$builder->{$joinType}($this->getModel()->getTable(), function (PowerJoinClause $join) use ($callback, $throughTable, $farTable, $alias1, $alias2) {
|
||||
if ($alias2) {
|
||||
$join->as($alias2);
|
||||
}
|
||||
|
||||
$join->on(
|
||||
"{$farTable}.{$this->secondKey}",
|
||||
'=',
|
||||
"{$throughTable}.{$this->secondLocalKey}"
|
||||
);
|
||||
|
||||
if ($this->usesSoftDeletes($this->getModel())) {
|
||||
$join->whereNull("{$farTable}.{$this->getModel()->getDeletedAtColumn()}");
|
||||
}
|
||||
|
||||
if (is_array($callback) && isset($callback[$this->getModel()->getTable()])) {
|
||||
$callback[$this->getModel()->getTable()]($join);
|
||||
}
|
||||
}, $this->getModel());
|
||||
|
||||
return $this;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the "HAVING" clause for eloquent power joins.
|
||||
*/
|
||||
public function performHavingForEloquentPowerJoins()
|
||||
{
|
||||
return function ($builder, $operator, $count, string $morphable = null) {
|
||||
if ($morphable) {
|
||||
$modelInstance = new $morphable;
|
||||
|
||||
$builder
|
||||
->selectRaw(sprintf('count(%s) as %s_count', $modelInstance->getQualifiedKeyName(), Str::replace('.', '_', $modelInstance->getTable())))
|
||||
->havingRaw(sprintf('count(%s) %s %d', $modelInstance->getQualifiedKeyName(), $operator, $count));
|
||||
} else {
|
||||
$builder
|
||||
->selectRaw(sprintf('count(%s) as %s_count', $this->query->getModel()->getQualifiedKeyName(), Str::replace('.', '_', $this->query->getModel()->getTable())))
|
||||
->havingRaw(sprintf('count(%s) %s %d', $this->query->getModel()->getQualifiedKeyName(), $operator, $count));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the relationship model uses soft deletes.
|
||||
*/
|
||||
public function usesSoftDeletes()
|
||||
{
|
||||
return function ($model) {
|
||||
return in_array(SoftDeletes::class, class_uses_recursive($model));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the throughParent for the HasManyThrough relationship.
|
||||
*/
|
||||
public function getThroughParent()
|
||||
{
|
||||
return function () {
|
||||
return $this->throughParent;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the farParent for the HasManyThrough relationship.
|
||||
*/
|
||||
public function getFarParent()
|
||||
{
|
||||
return function () {
|
||||
return $this->farParent;
|
||||
};
|
||||
}
|
||||
|
||||
public function applyExtraConditions()
|
||||
{
|
||||
return function (PowerJoinClause $join) {
|
||||
foreach ($this->getQuery()->getQuery()->wheres as $condition) {
|
||||
if ($this->shouldNotApplyExtraCondition($condition)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!in_array($condition['type'], ['Basic', 'Null', 'NotNull', 'Nested'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$method = "apply{$condition['type']}Condition";
|
||||
$this->$method($join, $condition);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public function applyBasicCondition()
|
||||
{
|
||||
return function ($join, $condition) {
|
||||
$join->where($condition['column'], $condition['operator'], $condition['value'], $condition['boolean']);
|
||||
};
|
||||
}
|
||||
|
||||
public function applyNullCondition()
|
||||
{
|
||||
return function ($join, $condition) {
|
||||
$join->whereNull($condition['column'], $condition['boolean']);
|
||||
};
|
||||
}
|
||||
|
||||
public function applyNotNullCondition()
|
||||
{
|
||||
return function ($join, $condition) {
|
||||
$join->whereNotNull($condition['column'], $condition['boolean']);
|
||||
};
|
||||
}
|
||||
|
||||
public function applyNestedCondition()
|
||||
{
|
||||
return function ($join, $condition) {
|
||||
$join->where(function ($q) use ($condition) {
|
||||
foreach ($condition['query']->wheres as $condition) {
|
||||
$method = "apply{$condition['type']}Condition";
|
||||
$this->$method($q, $condition);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public function shouldNotApplyExtraCondition()
|
||||
{
|
||||
return function ($condition) {
|
||||
if (isset($condition['column']) && Str::endsWith($condition['column'], '.')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $key = $this->getPowerJoinExistenceCompareKey()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isset($condition['query'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_array($key)) {
|
||||
return in_array($condition['column'], $key);
|
||||
}
|
||||
|
||||
return $condition['column'] === $key;
|
||||
};
|
||||
}
|
||||
|
||||
public function getPowerJoinExistenceCompareKey()
|
||||
{
|
||||
return function () {
|
||||
if ($this instanceof MorphTo) {
|
||||
return [$this->getMorphType(), $this->getForeignKeyName()];
|
||||
}
|
||||
|
||||
if ($this instanceof BelongsTo) {
|
||||
return $this->getQualifiedOwnerKeyName();
|
||||
}
|
||||
|
||||
if ($this instanceof HasMany || $this instanceof HasOne) {
|
||||
return $this->getExistenceCompareKey();
|
||||
}
|
||||
|
||||
if ($this instanceof HasManyThrough) {
|
||||
return $this->getQualifiedFirstKeyName();
|
||||
}
|
||||
|
||||
if ($this instanceof BelongsToMany) {
|
||||
return $this->getExistenceCompareKey();
|
||||
}
|
||||
|
||||
if ($this instanceof MorphOneOrMany) {
|
||||
return [$this->getQualifiedMorphType(), $this->getExistenceCompareKey()];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
260
vendor/kirschbaum-development/eloquent-power-joins/src/PowerJoinClause.php
vendored
Normal file
260
vendor/kirschbaum-development/eloquent-power-joins/src/PowerJoinClause.php
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
|
||||
namespace Kirschbaum\PowerJoins;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Support\Str;
|
||||
use InvalidArgumentException;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
|
||||
class PowerJoinClause extends JoinClause
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Database\Eloquent\Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Table name backup in case an alias is being used.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $tableName;
|
||||
|
||||
/**
|
||||
* Alias name.
|
||||
*/
|
||||
public ?string $alias = null;
|
||||
|
||||
/**
|
||||
* Joined table alias name (mostly for belongs to many aliases).
|
||||
*/
|
||||
public ?string $joinedTableAlias = null;
|
||||
|
||||
/**
|
||||
* Create a new join clause instance.
|
||||
*/
|
||||
public function __construct(Builder $parentQuery, $type, string $table, Model $model = null)
|
||||
{
|
||||
parent::__construct($parentQuery, $type, $table);
|
||||
|
||||
$this->model = $model;
|
||||
$this->tableName = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an alias to the table being joined.
|
||||
*/
|
||||
public function as(string $alias, ?string $joinedTableAlias = null): self
|
||||
{
|
||||
$this->alias = $alias;
|
||||
$this->joinedTableAlias = $joinedTableAlias;
|
||||
$this->table = sprintf('%s as %s', $this->table, $alias);
|
||||
$this->useTableAliasInConditions();
|
||||
|
||||
if ($this->model) {
|
||||
StaticCache::setTableAliasForModel($this->model, $alias);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function on($first, $operator = null, $second = null, $boolean = 'and'): self
|
||||
{
|
||||
parent::on($first, $operator, $second, $boolean);
|
||||
$this->useTableAliasInConditions();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getModel()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the global scopes to the joined query.
|
||||
*/
|
||||
public function withGlobalScopes(): self
|
||||
{
|
||||
if (! $this->model) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
foreach ($this->model->getGlobalScopes() as $scope) {
|
||||
if ($scope instanceof Closure) {
|
||||
$scope->call($this, $this);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($scope instanceof SoftDeletingScope) {
|
||||
continue;
|
||||
}
|
||||
|
||||
(new $scope())->apply($this, $this->model);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the table alias in the existing join conditions.
|
||||
*/
|
||||
protected function useTableAliasInConditions(): self
|
||||
{
|
||||
if (! $this->alias || ! $this->model) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->wheres = collect($this->wheres)->filter(function ($where) {
|
||||
return in_array($where['type'] ?? '', ['Column', 'Basic']);
|
||||
})->map(function ($where) {
|
||||
$key = $this->model->getKeyName();
|
||||
$table = $this->tableName;
|
||||
$replaceMethod = sprintf('useAliasInWhere%sType', ucfirst($where['type']));
|
||||
|
||||
return $this->{$replaceMethod}($where);
|
||||
})->toArray();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function useAliasInWhereColumnType(array $where): array
|
||||
{
|
||||
$key = $this->model->getKeyName();
|
||||
$table = $this->tableName;
|
||||
|
||||
// if it was already replaced, skip
|
||||
if (Str::startsWith($where['first'] . '.', $this->alias . '.') || Str::startsWith($where['second'] . '.', $this->alias . '.')) {
|
||||
return $where;
|
||||
}
|
||||
|
||||
if (Str::contains($where['first'], $table) && Str::contains($where['second'], $table)) {
|
||||
// if joining the same table, only replace the correct table.key pair
|
||||
$where['first'] = str_replace($table . '.' . $key, $this->alias . '.' . $key, $where['first']);
|
||||
$where['second'] = str_replace($table . '.' . $key, $this->alias . '.' . $key, $where['second']);
|
||||
} else {
|
||||
$where['first'] = str_replace($table . '.', $this->alias . '.', $where['first']);
|
||||
$where['second'] = str_replace($table . '.', $this->alias . '.', $where['second']);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function useAliasInWhereBasicType(array $where): array
|
||||
{
|
||||
$table = $this->tableName;
|
||||
|
||||
if (Str::startsWith($where['column'] . '.', $this->alias . '.')) {
|
||||
return $where;
|
||||
}
|
||||
|
||||
if (Str::contains($where['column'], $table)) {
|
||||
// if joining the same table, only replace the correct table.key pair
|
||||
$where['column'] = str_replace($table . '.', $this->alias . '.', $where['column']);
|
||||
} else {
|
||||
$where['column'] = str_replace($table . '.', $this->alias . '.', $where['column']);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
public function whereNull($columns, $boolean = 'and', $not = false)
|
||||
{
|
||||
if ($this->alias && Str::contains($columns, $this->tableName)) {
|
||||
$columns = str_replace("{$this->tableName}.", "{$this->alias}.", $columns);
|
||||
}
|
||||
|
||||
return parent::whereNull($columns, $boolean, $not);
|
||||
}
|
||||
|
||||
public function newQuery(): self
|
||||
{
|
||||
return new static($this->newParentQuery(), $this->type, $this->table, $this->model); // <-- The model param is needed
|
||||
}
|
||||
|
||||
public function where($column, $operator = null, $value = null, $boolean = 'and'): self
|
||||
{
|
||||
if ($this->alias && is_string($column) && Str::contains($column, $this->tableName)) {
|
||||
$column = str_replace("{$this->tableName}.", "{$this->alias}.", $column);
|
||||
} elseif ($this->alias && ! is_callable($column)) {
|
||||
$column = $this->alias . '.' . $column;
|
||||
}
|
||||
|
||||
if (is_callable($column)) {
|
||||
$query = new self($this, $this->type, $this->table, $this->model);
|
||||
$column($query);
|
||||
return $this->addNestedWhereQuery($query);
|
||||
} else {
|
||||
return parent::where($column, $operator, $value, $boolean);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the soft delete condition in case the model implements soft deletes.
|
||||
*/
|
||||
public function withTrashed(): self
|
||||
{
|
||||
if (! $this->getModel() || ! in_array(SoftDeletes::class, class_uses_recursive($this->getModel()))) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->wheres = array_filter($this->wheres, function ($where) {
|
||||
if ($where['type'] === 'Null' && Str::contains($where['column'], $this->getModel()->getDeletedAtColumn())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the soft delete condition in case the model implements soft deletes.
|
||||
*/
|
||||
public function onlyTrashed(): self
|
||||
{
|
||||
if (! $this->getModel() || ! in_array(SoftDeletes::class, class_uses_recursive($this->getModel()))) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->wheres = array_map(function ($where) {
|
||||
if ($where['type'] === 'Null' && Str::contains($where['column'], $this->getModel()->getDeletedAtColumn())) {
|
||||
$where['type'] = 'NotNull';
|
||||
}
|
||||
|
||||
return $where;
|
||||
}, $this->wheres);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
$scope = 'scope' . ucfirst($name);
|
||||
|
||||
if (! $this->getModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (method_exists($this->getModel(), $scope)) {
|
||||
return $this->getModel()->{$scope}($this, ...$arguments);
|
||||
} else {
|
||||
if (static::hasMacro($name)) {
|
||||
return $this->macroCall($name, $arguments);
|
||||
}
|
||||
|
||||
$eloquentBuilder = $this->getModel()->newEloquentBuilder($this);
|
||||
if (method_exists($eloquentBuilder, $name)) {
|
||||
return $eloquentBuilder->{$name}(...$arguments);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('Method %s does not exist in PowerJoinClause class', $name));
|
||||
}
|
||||
}
|
||||
}
|
||||
9
vendor/kirschbaum-development/eloquent-power-joins/src/PowerJoins.php
vendored
Normal file
9
vendor/kirschbaum-development/eloquent-power-joins/src/PowerJoins.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Kirschbaum\PowerJoins;
|
||||
|
||||
|
||||
trait PowerJoins
|
||||
{
|
||||
//
|
||||
}
|
||||
23
vendor/kirschbaum-development/eloquent-power-joins/src/PowerJoinsServiceProvider.php
vendored
Normal file
23
vendor/kirschbaum-development/eloquent-power-joins/src/PowerJoinsServiceProvider.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Kirschbaum\PowerJoins;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class PowerJoinsServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the application services.
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
EloquentJoins::registerEloquentMacros();
|
||||
}
|
||||
}
|
||||
29
vendor/kirschbaum-development/eloquent-power-joins/src/StaticCache.php
vendored
Normal file
29
vendor/kirschbaum-development/eloquent-power-joins/src/StaticCache.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Kirschbaum\PowerJoins;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class StaticCache
|
||||
{
|
||||
/**
|
||||
* Cache to not join the same relationship twice.
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public static array $powerJoinAliasesCache = [];
|
||||
|
||||
public static function getTableOrAliasForModel(Model $model): string
|
||||
{
|
||||
return static::$powerJoinAliasesCache[spl_object_id($model)] ?? $model->getTable();
|
||||
}
|
||||
|
||||
public static function setTableAliasForModel(Model $model, $alias): void
|
||||
{
|
||||
static::$powerJoinAliasesCache[spl_object_id($model)] = $alias;
|
||||
}
|
||||
|
||||
public static function clear(): void
|
||||
{
|
||||
static::$powerJoinAliasesCache = [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user