Skip to content

Commit 4dc4a0e

Browse files
authored
Merge pull request #104 from awssat/fix-postgres-compatibility-eager-loading-1587797120699818730
Fix PostgreSQL compatibility for eager loading visits
2 parents ff9e034 + 13868dc commit 4dc4a0e

File tree

4 files changed

+134
-1
lines changed

4 files changed

+134
-1
lines changed

src/Relations/VisitsHasOne.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace Awssat\Visits\Relations;
4+
5+
use Illuminate\Database\Eloquent\Relations\HasOne;
6+
use Illuminate\Database\Eloquent\Model;
7+
8+
class VisitsHasOne extends HasOne
9+
{
10+
/**
11+
* Get all of the primary keys for an array of models.
12+
*
13+
* @param array $models
14+
* @param string|null $key
15+
* @return array
16+
*/
17+
protected function getKeys(array $models, $key = null)
18+
{
19+
return collect($models)->map(function ($value) use ($key) {
20+
return (string) ($key ? $value->getAttribute($key) : $value->getKey());
21+
})->values()->unique(null, true)->sort()->all();
22+
}
23+
24+
/**
25+
* Get the name of the "where in" method for eager loading.
26+
*
27+
* @param \Illuminate\Database\Eloquent\Model $model
28+
* @param string $key
29+
* @return string
30+
*/
31+
protected function whereInMethod(Model $model, $key)
32+
{
33+
return 'whereIn';
34+
}
35+
}

src/Visits.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Awssat\Visits;
44

55
use Awssat\Visits\Models\Visit;
6+
use Awssat\Visits\Relations\VisitsHasOne;
67
use Awssat\Visits\Traits\{Lists, Periods, Record, Setters};
78
use Illuminate\Database\Eloquent\Model;
89
use Illuminate\Support\Arr;
@@ -271,6 +272,12 @@ public function relation()
271272
{
272273
$prefix = $this->config['keys_prefix'] ?? $this->config['redis_keys_prefix'] ?? 'visits';
273274

274-
return $this->subject->hasOne(Visit::class, 'secondary_key')->where('primary_key', $prefix.':'.$this->keys->visits);
275+
$instance = new Visit;
276+
$foreignKey = 'secondary_key';
277+
$localKey = $this->subject->getKeyName();
278+
279+
$relation = new VisitsHasOne($instance->newQuery(), $this->subject, $instance->getTable().'.'.$foreignKey, $localKey);
280+
281+
return $relation->where('primary_key', $prefix.':'.$this->keys->visits);
275282
}
276283
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
namespace Awssat\Visits\Tests\Feature;
4+
5+
use Awssat\Visits\Tests\TestCase;
6+
use Illuminate\Support\Facades\DB;
7+
use Illuminate\Foundation\Testing\RefreshDatabase;
8+
use Illuminate\Database\Eloquent\Model;
9+
10+
class TestPost extends Model
11+
{
12+
protected $table = 'posts';
13+
protected $guarded = [];
14+
15+
public function visits()
16+
{
17+
return visits($this)->relation();
18+
}
19+
}
20+
21+
class PostgresCompatibilityTest extends TestCase
22+
{
23+
use RefreshDatabase;
24+
25+
public function setUp(): void
26+
{
27+
parent::setUp();
28+
29+
$this->app['config']['visits.engine'] = \Awssat\Visits\DataEngines\EloquentEngine::class;
30+
include_once __DIR__.'/../../database/migrations/create_visits_table.php.stub';
31+
(new \CreateVisitsTable())->up();
32+
}
33+
34+
/** @test */
35+
public function it_generates_correct_query_types_for_postgres()
36+
{
37+
$post1 = TestPost::create(['name' => 'p1']);
38+
$post2 = TestPost::create(['name' => 'p2']);
39+
40+
visits($post1)->increment();
41+
visits($post2)->increment();
42+
43+
DB::enableQueryLog();
44+
45+
$posts = TestPost::with('visits')->get();
46+
47+
$log = DB::getQueryLog();
48+
49+
// Find the query that loads visits
50+
$visitsQuery = null;
51+
foreach ($log as $query) {
52+
if (strpos($query['query'], 'select * from "visits"') !== false) {
53+
$visitsQuery = $query;
54+
break;
55+
}
56+
}
57+
58+
// If not found, look for standard select
59+
if (!$visitsQuery) {
60+
foreach ($log as $query) {
61+
if (strpos($query['query'], 'from "visits"') !== false) {
62+
$visitsQuery = $query;
63+
break;
64+
}
65+
}
66+
}
67+
68+
$this->assertNotNull($visitsQuery, 'Visits query not found');
69+
70+
// Check bindings
71+
$bindings = $visitsQuery['bindings'];
72+
73+
$hasIntegerBindings = false;
74+
$hasStringBindings = false;
75+
76+
foreach ($bindings as $binding) {
77+
if (is_int($binding) && ($binding === $post1->id || $binding === $post2->id)) {
78+
$hasIntegerBindings = true;
79+
}
80+
if (is_string($binding) && ($binding === (string) $post1->id || $binding === (string) $post2->id)) {
81+
$hasStringBindings = true;
82+
}
83+
}
84+
85+
$this->assertFalse($hasIntegerBindings, 'Bindings should not be integers');
86+
$this->assertTrue($hasStringBindings, 'Bindings should be strings');
87+
}
88+
}

tests/TestCase.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ abstract class TestCase extends BaseTestCase
2222
protected $redis;
2323
protected $connection;
2424

25+
/** @var \Illuminate\Testing\TestResponse|null */
26+
protected static $latestResponse = null;
27+
2528
/**
2629
* Setup the test environment.
2730
*

0 commit comments

Comments
 (0)