Skip to content

Commit

Permalink
Add support belongsToMany
Browse files Browse the repository at this point in the history
  • Loading branch information
admin committed Apr 14, 2020
1 parent ae94319 commit e9edd6f
Show file tree
Hide file tree
Showing 13 changed files with 141 additions and 12 deletions.
42 changes: 35 additions & 7 deletions src/EloquentJoinBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Fico7489\Laravel\EloquentJoin\Exceptions\InvalidRelationGlobalScope;
use Fico7489\Laravel\EloquentJoin\Exceptions\InvalidRelationWhere;
use Fico7489\Laravel\EloquentJoin\Relations\BelongsToJoin;
use Fico7489\Laravel\EloquentJoin\Relations\BelongsToManyJoin;
use Fico7489\Laravel\EloquentJoin\Relations\HasManyJoin;
use Fico7489\Laravel\EloquentJoin\Relations\HasOneJoin;
use Illuminate\Database\Eloquent\Builder;
Expand Down Expand Up @@ -170,27 +171,32 @@ protected function performJoin($relations, $leftJoin = null)
$currentTableAlias = $baseTable;

$relationsAccumulated = [];
foreach ($relations as $relation) {
if ($relation == $column) {
//last item in $relations argument is sort|where column
break;
}
for ($i = 0; $i < count($relations) - 1; ++$i) {
$relation = $relations[$i];
$nextRelation = $relations[$i + 1];

/** @var Relation $relatedRelation */
$relatedRelation = $currentModel->$relation();
$relatedModel = $relatedRelation->getRelated();
$relatedPrimaryKey = $relatedModel->getKeyName();
$relatedTable = $relatedModel->getTable();
$relatedTableAlias = $this->useTableAlias ? sha1($relatedTable) : $relatedTable;

$relationsAccumulated[] = $relatedTableAlias;
$relationsAccumulated[] = $relatedTable;
$relationAccumulatedString = implode('_', $relationsAccumulated);

$relatedTableAlias = $this->useTableAlias ? sha1($relatedTable) : $relatedTable;

//relations count
if ($this->appendRelationsCount) {
$this->selectRaw('COUNT('.$relatedTableAlias.'.'.$relatedPrimaryKey.') as '.$relationAccumulatedString.'_count');
}

if ($relatedRelation instanceof BelongsToManyJoin) {
$pivotTable = $relatedRelation->getTable();
$pivotTableAlias = $this->useTableAlias ? sha1($pivotTable) : $pivotTable;
$joinQueryPivot = $pivotTable.($this->useTableAlias ? ' as '.$pivotTableAlias : '');
}

if (!in_array($relationAccumulatedString, $this->joinedTables)) {
$joinQuery = $relatedTable.($this->useTableAlias ? ' as '.$relatedTableAlias : '');
if ($relatedRelation instanceof BelongsToJoin) {
Expand All @@ -212,13 +218,35 @@ protected function performJoin($relations, $leftJoin = null)
$this->$joinMethod($joinQuery, function ($join) use ($relatedRelation, $relatedTableAlias, $relatedKey, $currentTableAlias, $localKey) {
$join->on($relatedTableAlias.'.'.$relatedKey, '=', $currentTableAlias.'.'.$localKey);

$this->joinQuery($join, $relatedRelation, $relatedTableAlias);
});
} elseif ($relatedRelation instanceof BelongsToManyJoin) {
$localPivotKey = $relatedRelation->getForeignPivotKeyName();
$relatedPivotKey = $relatedRelation->getRelatedPivotKeyName();
$localKey = $relatedRelation->getParentKeyName();
$relatedKey = $relatedRelation->getRelatedKeyName();

$this->$joinMethod($joinQueryPivot, function ($join) use ($relatedRelation, $pivotTableAlias, $localPivotKey, $currentTableAlias, $localKey) {
$join->on($pivotTableAlias.'.'.$localPivotKey, '=', $currentTableAlias.'.'.$localKey);

$this->joinQuery($join, $relatedRelation, $pivotTableAlias);
});

$this->$joinMethod($joinQuery, function ($join) use ($relatedRelation, $relatedTableAlias, $relatedKey, $pivotTableAlias, $relatedPivotKey) {
$join->on($relatedTableAlias.'.'.$relatedKey, '=', $pivotTableAlias.'.'.$relatedPivotKey);

$this->joinQuery($join, $relatedRelation, $relatedTableAlias);
});
} else {
throw new InvalidRelation();
}
}

if ('pivot' === $nextRelation) {
$relatedTableAlias = $pivotTableAlias;
++$i;
}

$currentModel = $relatedModel;
$currentTableAlias = $relatedTableAlias;

Expand Down
2 changes: 1 addition & 1 deletion src/Exceptions/InvalidRelationClause.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

class InvalidRelationClause extends \Exception
{
public $message = 'Package allows only following clauses on relation : where, orWhere, withTrashed, onlyTrashed and withoutTrashed.';
public $message = 'Package allows only following clauses on relation : where, orWhere, whereRaw, orderByRaw, withTrashed, onlyTrashed and withoutTrashed.';
}
11 changes: 11 additions & 0 deletions src/Relations/BelongsToManyJoin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Fico7489\Laravel\EloquentJoin\Relations;

use Fico7489\Laravel\EloquentJoin\Traits\JoinRelationTrait;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class BelongsToManyJoin extends BelongsToMany
{
use JoinRelationTrait;
}
7 changes: 7 additions & 0 deletions src/Traits/ExtendRelationsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Fico7489\Laravel\EloquentJoin\Traits;

use Fico7489\Laravel\EloquentJoin\Relations\BelongsToJoin;
use Fico7489\Laravel\EloquentJoin\Relations\BelongsToManyJoin;
use Fico7489\Laravel\EloquentJoin\Relations\HasManyJoin;
use Fico7489\Laravel\EloquentJoin\Relations\HasOneJoin;
use Illuminate\Database\Eloquent\Builder;
Expand All @@ -15,6 +16,12 @@ protected function newBelongsTo(Builder $query, Model $child, $foreignKey, $owne
return new BelongsToJoin($query, $child, $foreignKey, $ownerKey, $relation);
}

protected function newBelongsToMany(Builder $query, Model $parent, $table, $foreignPivotKey, $relatedPivotKey,
$parentKey, $relatedKey, $relationName = null)
{
return new BelongsToManyJoin($query, $parent, $table, $foreignPivotKey, $relatedPivotKey, $parentKey, $relatedKey, $relationName);
}

protected function newHasOne(Builder $query, Model $parent, $foreignKey, $localKey)
{
return new HasOneJoin($query, $parent, $foreignKey, $localKey);
Expand Down
2 changes: 1 addition & 1 deletion tests/Models/City.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function zipCodePrimary()

public function sellers()
{
return $this->belongsToMany(Seller::class, 'locations', 'seller_id', 'city_id');
return $this->belongsToMany(Seller::class, 'locations', 'city_id', 'seller_id');
}

public function zipCodes()
Expand Down
5 changes: 5 additions & 0 deletions tests/Models/Seller.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,9 @@ public function city()
{
return $this->belongsTo(City::class);
}

public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
24 changes: 24 additions & 0 deletions tests/Models/Tag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Fico7489\Laravel\EloquentJoin\Tests\Models;

use Illuminate\Database\Eloquent\SoftDeletes;

class Tag extends BaseModel
{
use SoftDeletes;

protected $table = 'tags';

protected $fillable = ['name'];

public function sellers()
{
return $this->morphedByMany(Seller::class, 'taggable');
}

public function users()
{
return $this->morphedByMany(User::class, 'taggable');
}
}
5 changes: 5 additions & 0 deletions tests/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ class User extends BaseModel
protected $table = 'users';

protected $fillable = ['name'];

public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
2 changes: 1 addition & 1 deletion tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,6 @@ protected function assertQueryMatches($expected, $actual)
$expected = str_replace(['"'], '`', $expected);
}

$this->assertRegExp($expected, $actual);
$this->assertMatchesRegularExpression($expected, $actual);
}
}
3 changes: 1 addition & 2 deletions tests/Tests/ExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Fico7489\Laravel\EloquentJoin\Exceptions\InvalidRelationClause;
use Fico7489\Laravel\EloquentJoin\Exceptions\InvalidRelationGlobalScope;
use Fico7489\Laravel\EloquentJoin\Exceptions\InvalidRelationWhere;
use Fico7489\Laravel\EloquentJoin\Tests\Models\City;
use Fico7489\Laravel\EloquentJoin\Tests\Models\Seller;
use Fico7489\Laravel\EloquentJoin\Tests\TestCase;

Expand All @@ -17,7 +16,7 @@ class ExceptionTest extends TestCase
public function testInvalidRelation()
{
try {
City::whereJoin('sellers.id', '=', 'test')->get();
Seller::whereJoin('tags.name', '=', 'test')->get();
} catch (InvalidRelation $e) {
$this->assertEquals((new InvalidRelation())->message, $e->getMessage());

Expand Down
22 changes: 22 additions & 0 deletions tests/Tests/Relations/BelongToMany.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Fico7489\Laravel\EloquentJoin\Tests\Tests\Relations;

use Fico7489\Laravel\EloquentJoin\Tests\Models\City;
use Fico7489\Laravel\EloquentJoin\Tests\TestCase;

class BelongToManyTest extends TestCase
{
public function testBelongsToMany()
{
City::joinRelations('sellers')->get();

$queryTest = 'select cities.* from "cities"
left join "locations" on "locations"."city_id" = "cities"."id"
left join "sellers" on "sellers"."id" = "locations"."seller_id"
where "cities"."deleted_at" is null
group by "cities"."id"';

$this->assertQueryMatches($queryTest, $this->fetchQuery());
}
}
15 changes: 15 additions & 0 deletions tests/Tests/Relations/HasManyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Fico7489\Laravel\EloquentJoin\Tests\Tests\Relations;

use Fico7489\Laravel\EloquentJoin\Tests\Models\Seller;
use Fico7489\Laravel\EloquentJoin\Tests\Models\State;
use Fico7489\Laravel\EloquentJoin\Tests\TestCase;

class HasManyTest extends TestCase
Expand Down Expand Up @@ -48,4 +49,18 @@ public function testHasManyBelongsTo()

$this->assertQueryMatches($queryTest, $this->fetchQuery());
}

public function testHasManyBelongsToMany()
{
State::joinRelations('cities.sellers')->get();

$queryTest = 'select states.* from "states"
left join "cities" on "cities"."state_id" = "states"."id" and "cities"."deleted_at" is null
left join "locations" on "locations"."city_id" = "cities"."id"
left join "sellers" on "sellers"."id" = "locations"."seller_id"
where "states"."deleted_at" is null
group by "states"."id"';

$this->assertQueryMatches($queryTest, $this->fetchQuery());
}
}
13 changes: 13 additions & 0 deletions tests/database/migrations/2017_11_04_163552_create_database.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,19 @@ public function up()
$table->unsignedInteger('id_seller_foreign')->nullable();
$table->foreign('id_seller_foreign')->references('id')->on('sellers');
});

Schema::create('tags', function (Blueprint $table) {
$table->increments('id_location_primary');

$table->string('name');
});

Schema::create('taggables', function (Blueprint $table) {
$table->increments('tag_id');

$table->unsignedInteger('taggable_id');
$table->string('taggable_type');
});
}

/**
Expand Down

0 comments on commit e9edd6f

Please sign in to comment.