Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/belongs to many support #97

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 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,11 +171,9 @@ 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();
Expand All @@ -191,6 +190,12 @@ protected function performJoin($relations, $leftJoin = null)
$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 +217,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/InvalidRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

class InvalidRelation extends \Exception
{
public $message = 'Package allows only following relations : BelongsTo, HasOne and HasMany.';
public $message = 'Package allows only following relations : BelongsTo, BelongsToMany, HasOne and HasMany.';
}
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');
}
}
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