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

Apply conditions/scopes to related model query #536

Merged
merged 26 commits into from
Nov 20, 2020
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4b23c80
override Laravel Relation sync() method
mjauvin Oct 26, 2020
9e19b66
fix code quality
mjauvin Oct 26, 2020
624461a
fix comment
mjauvin Oct 26, 2020
9775ab5
fix detach method to only detach related ids, honoring the conditions…
mjauvin Oct 28, 2020
7fee28b
Update src/Database/Relations/BelongsToMany.php
LukeTowers Oct 29, 2020
8db8f62
rewrite getCurrentlyAttachedPivots() to take conditions and scopes in…
mjauvin Oct 31, 2020
4943e24
simplify code a bit
mjauvin Oct 31, 2020
e561aa9
remove unused Pivot class
mjauvin Oct 31, 2020
c613535
no need to complicate things here...
mjauvin Oct 31, 2020
560bc08
add initial relations tests
mjauvin Nov 3, 2020
cef177d
remove empty line before closing brace
mjauvin Nov 3, 2020
246337d
add DbTestCase and use it for tests needing database
mjauvin Nov 3, 2020
1965c62
add actual tests that prove this PR resolves the issue
mjauvin Nov 3, 2020
22ccd13
complet tests
mjauvin Nov 3, 2020
4d1887d
flush event listeners on models after each test
mjauvin Nov 4, 2020
b57d1b9
add tearDown method to DbTestCase to flush event listeners on instanc…
mjauvin Nov 4, 2020
2b8c815
fix method names
mjauvin Nov 5, 2020
957cda2
rework DbTestCase class handling
mjauvin Nov 7, 2020
9187bf4
call parent::attach() instead of duplicating code here
mjauvin Nov 8, 2020
8a205f8
Revert "call parent::attach() instead of duplicating code here"
mjauvin Nov 8, 2020
83fa7db
add spaces around => operator
mjauvin Nov 10, 2020
c86f3e4
Merge branch 'develop' into fix-sync-relations
mjauvin Nov 10, 2020
4ba0258
Merge branch 'fix-sync-relations' of github.com:mjauvin/library into …
mjauvin Nov 10, 2020
4e0f857
add multiple records sync and single record detach
mjauvin Nov 10, 2020
1c17771
find the last category type term and detach it
mjauvin Nov 11, 2020
e213586
flush cache after query if needed
mjauvin Nov 12, 2020
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
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
"tests/TestCase.php",
"tests/DbTestCase.php"
]
},
"scripts": {
Expand Down
16 changes: 13 additions & 3 deletions src/Database/Relations/BelongsToMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public function detach($ids = null, $touch = true)
{
$attachedIdList = $this->parseIds($ids);
if (empty($attachedIdList)) {
$attachedIdList = $this->newPivotQuery()->lists($this->relatedPivotKey);
$attachedIdList = $this->allRelatedIds()->all();
}

/**
Expand All @@ -188,7 +188,7 @@ public function detach($ids = null, $touch = true)
/*
* See Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithPivotTable
mjauvin marked this conversation as resolved.
Show resolved Hide resolved
*/
parent::detach($ids, $touch);
parent::detach($attachedIdList, $touch);

/**
* @event model.relation.afterDetach
Expand Down Expand Up @@ -406,7 +406,17 @@ public function getOtherKey()
*/
public function getRelatedIds($sessionKey = null)
{
traceLog('Method BelongsToMany::allRelatedIds has been deprecated, use BelongsToMany::allRelatedIds instead.');
traceLog('Method BelongsToMany::getRelatedIds has been deprecated, use BelongsToMany::allRelatedIds instead.');
return $this->allRelatedIds($sessionKey)->all();
}

/**
* Get the pivot models that are currently attached (taking conditions & scopes into account).
*
* @return \Illuminate\Support\Collection
*/
protected function getCurrentlyAttachedPivots()
{
return $this->getQuery()->get();
}
}
161 changes: 161 additions & 0 deletions tests/Database/RelationsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?php

class RelationsTest extends DbTestCase
{
public function setUp(): void
{
parent::setUp();

$this->createTables();
$this->seedTables();
}

public function createTables()
{
$this->db->schema()->create('posts', function ($table) {
$table->increments('id');
$table->string('title')->default('');
$table->timestamps();
});

$this->db->schema()->create('terms', function ($table) {
$table->increments('id');
$table->string('type');
$table->string('name');
$table->timestamps();
});

$this->db->schema()->create('posts_terms', function ($table) {
$table->increments('id');
$table->unsignedInteger('post_id');
$table->unsignedInteger('term_id');
});
}

public function seedTables()
{
$post = Post::create([
'title' => 'A Post',
]);

$post->tags()->create(['type'=>'tag', 'name'=>'A Tag']);
$post->tags()->create(['type'=>'tag', 'name'=>'Second Tag']);
$post->categories()->create(['type'=>'category', 'name'=>'A Category']);
$post->categories()->create(['type'=>'category', 'name'=>'Second Category']);
mjauvin marked this conversation as resolved.
Show resolved Hide resolved
}

public function testTablesExist()
{
$this->assertTrue($this->db->schema()->hasTable('posts'));
$this->assertTrue($this->db->schema()->hasTable('terms'));
$this->assertTrue($this->db->schema()->hasTable('posts_terms'));
}

public function testTablesProperlySeeded()
{
$this->assertEquals(1, Post::count());
$this->assertEquals(4, Term::count());
$this->assertEquals(2, Term::where('type', 'tag')->count());
$this->assertEquals(2, Term::where('type', 'category')->count());
}

public function testBelongsToManyCount()
{
$post = Post::first();
$this->assertEquals(2, $post->tags->count());
$this->assertEquals(2, $post->categories->count());
}

public function testBelongsToManySyncAll()
{
$post = Post::first();

$id = $post->categories()->first()->id;
$post->categories()->sync([$id]);
$this->assertEquals(1, $post->categories->count());
$this->assertEquals($id, $post->categories()->first()->id);

$id = $post->tags()->first()->id;
$post->tags()->sync([$id]);
$this->assertEquals(1, $post->tags->count());
$this->assertEquals($id, $post->tags()->first()->id);
}

public function testBelongsToManySyncTags()
{
$post = Post::first();

$post->categories()->detach();
$this->assertEquals(0, $post->categories->count());

$id = $post->tags()->first()->id;
$post->tags()->sync([$id]);
$this->assertEquals(1, $post->tags->count());
$this->assertEquals($id, $post->tags()->first()->id);
}

public function testBelongsToManySyncCategories()
{
$post = Post::first();

$id = $post->categories()->first()->id;
$post->categories()->sync([$id]);
$this->assertEquals(1, $post->categories->count());
$this->assertEquals($id, $post->categories()->first()->id);

$post->tags()->detach();
$this->assertEquals(0, $post->tags->count());
}

public function testBelongsToManyDetach()
{
$post = Post::first();

$post->categories()->detach();
$this->assertEquals(0, $post->categories->count());

$post->tags()->detach();
$this->assertEquals(0, $post->tags->count());
}
}

class Post extends \October\Rain\Database\Model
{
public $table = 'posts';

public $fillable = ['title'];

public $belongsToMany = [
'tags' => [
Term::class,
'table' => 'posts_terms',
'key' => 'post_id',
'otherKey' => 'term_id',
'conditions' => 'type = "tag"'
],
'categories' => [
Term::class,
'table' => 'posts_terms',
'key' => 'post_id',
'otherKey' => 'term_id',
'conditions' => 'type = "category"'
],
];
}

class Term extends \October\Rain\Database\Model
{
public $table = 'terms';

public $fillable = ['type', 'name'];

public $belongsToMany = [
'posts' => [
'Post',
'table' => 'posts_terms',
'key' => 'term_id',
'otherKey' => 'post_id',
'conditions' => 'type = "post"'
],
];
}
14 changes: 3 additions & 11 deletions tests/Database/Traits/EncryptableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use Illuminate\Encryption\Encrypter;

class EncryptableTest extends TestCase
class EncryptableTest extends DbTestCase
{
const TEST_CRYPT_KEY = 'gBmM1S5bxZ5ePRj5';

Expand All @@ -13,17 +13,9 @@ class EncryptableTest extends TestCase

public function setUp(): void
{
$capsule = new Illuminate\Database\Capsule\Manager;
$capsule->addConnection([
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => ''
]);
parent::setUp();

$capsule->setAsGlobal();
$capsule->bootEloquent();

$capsule->schema()->create('secrets', function ($table) {
$this->db->schema()->create('secrets', function ($table) {
$table->increments('id');
$table->string('secret');
$table->timestamps();
Expand Down
56 changes: 56 additions & 0 deletions tests/DbTestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

use October\Rain\Database\Model;
use October\Rain\Database\Pivot;
use Illuminate\Database\Capsule\Manager as CapsuleManager;

class DbTestCase extends TestCase
{
public function setUp(): void
{
$this->db = new CapsuleManager;
$this->db->addConnection([
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => ''
]);

$this->db->setAsGlobal();
$this->db->bootEloquent();
}

public function tearDown() : void
{
$this->flushModelEventListeners();
parent::tearDown();
unset($this->db);
}

/**
* The models in October use a static property to store their events, these
* will need to be targeted and reset ready for a new test cycle.
* Pivot models are an exception since they are internally managed.
* @return void
*/
protected function flushModelEventListeners()
{
foreach (get_declared_classes() as $class) {
if ($class === Pivot::class) {
continue;
}

$reflectClass = new ReflectionClass($class);
if (
!$reflectClass->isInstantiable() ||
!$reflectClass->isSubclassOf(Model::class) ||
$reflectClass->isSubclassOf(Pivot::class)
) {
continue;
}

$class::flushEventListeners();
}

Model::flushEventListeners();
}
}