diff --git a/src/Jenssegers/Mongodb/Helpers/QueriesRelationships.php b/src/Jenssegers/Mongodb/Helpers/QueriesRelationships.php
index 1f24d136d798133054f945c72c960290fabe4c2d..cb74a7d079337d2d4f13c882208470b85882dbb8 100644
--- a/src/Jenssegers/Mongodb/Helpers/QueriesRelationships.php
+++ b/src/Jenssegers/Mongodb/Helpers/QueriesRelationships.php
@@ -5,8 +5,7 @@ namespace Jenssegers\Mongodb\Helpers;
use Closure;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
-use Illuminate\Database\Eloquent\Relations\Relation;
-use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
+use Jenssegers\Mongodb\Eloquent\Model;
trait QueriesRelationships
{
@@ -28,9 +27,9 @@ trait QueriesRelationships
$relation = $this->getRelationWithoutConstraints($relation);
- // If this is a hybrid relation then we can not use an existence query
+ // If this is a hybrid relation then we can not use a normal whereExists() query that relies on a subquery
// We need to use a `whereIn` query
- if ($relation->getParent()->getConnectionName() !== $relation->getRelated()->getConnectionName()) {
+ if ($this->getModel() instanceof Model || $this->isAcrossConnections($relation)) {
return $this->addHybridHas($relation, $operator, $count, $boolean, $callback);
}
@@ -57,6 +56,15 @@ trait QueriesRelationships
);
}
+ /**
+ * @param $relation
+ * @return bool
+ */
+ protected function isAcrossConnections($relation)
+ {
+ return $relation->getParent()->getConnectionName() !== $relation->getRelated()->getConnectionName();
+ }
+
/**
* Compare across databases
* @param $relation
@@ -65,6 +73,7 @@ trait QueriesRelationships
* @param string $boolean
* @param Closure|null $callback
* @return mixed
+ * @throws \Exception
*/
public function addHybridHas($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
{
@@ -73,15 +82,23 @@ trait QueriesRelationships
$hasQuery->callScope($callback);
}
+ // If the operator is <, <= or !=, we will use whereNotIn.
+ $not = in_array($operator, ['<', '<=', '!=']);
+ // If we are comparing to 0, we need an additional $not flip.
+ if ($count == 0) {
+ $not = ! $not;
+ }
+
$relations = $hasQuery->pluck($this->getHasCompareKey($relation));
- $constraintKey = $this->getRelatedConstraintKey($relation);
- return $this->addRelatedCountConstraint($constraintKey, $relations, $operator, $count, $boolean);
+ $relatedIds = $this->getConstrainedRelatedIds($relations, $operator, $count);
+
+ return $this->whereIn($this->getRelatedConstraintKey($relation), $relatedIds, $boolean, $not);
}
/**
- * Returns key we are constraining this parent model's query witth
+ * Returns key we are constraining this parent model's query with
* @param $relation
* @return string
* @throws \Exception
@@ -89,7 +106,7 @@ trait QueriesRelationships
protected function getRelatedConstraintKey($relation)
{
if ($relation instanceof HasOneOrMany) {
- return $relation->getQualifiedParentKeyName();
+ return $this->model->getKeyName();
}
if ($relation instanceof BelongsTo) {
@@ -105,47 +122,20 @@ trait QueriesRelationships
*/
protected function getHasCompareKey($relation)
{
- if ($relation instanceof HasOneOrMany) {
- return $relation->getForeignKeyName();
+ if (method_exists($relation, 'getHasCompareKey')) {
+ return $relation->getHasCompareKey();
}
- $keyMethods = ['getOwnerKey', 'getHasCompareKey'];
- foreach ($keyMethods as $method) {
- if (method_exists($relation, $method)) {
- return $relation->$method();
- }
- }
+ return $relation instanceof HasOneOrMany ? $relation->getForeignKeyName() : $relation->getOwnerKey();
}
/**
- * Add the "has" condition where clause to the query.
- *
- * @param \Illuminate\Database\Eloquent\Builder $hasQuery
- * @param \Illuminate\Database\Eloquent\Relations\Relation $relation
- * @param string $operator
- * @param int $count
- * @param string $boolean
- * @return \Illuminate\Database\Eloquent\Builder|static
- */
- protected function addHasWhere(EloquentBuilder $hasQuery, Relation $relation, $operator, $count, $boolean)
- {
- $query = $hasQuery->getQuery();
- // Get the number of related objects for each possible parent.
- $relations = $query->pluck($relation->getHasCompareKey());
-
- return $this->addRelatedCountConstraint($this->model->getKeyName(), $relations, $operator, $count, $boolean);
- }
-
- /**
- * Consta
- * @param $key
* @param $relations
* @param $operator
* @param $count
- * @param $boolean
- * @return mixed
+ * @return array
*/
- protected function addRelatedCountConstraint($key, $relations, $operator, $count, $boolean)
+ protected function getConstrainedRelatedIds($relations, $operator, $count)
{
$relationCount = array_count_values(array_map(function ($id) {
return (string)$id; // Convert Back ObjectIds to Strings
@@ -169,16 +159,7 @@ trait QueriesRelationships
}
});
- // If the operator is <, <= or !=, we will use whereNotIn.
- $not = in_array($operator, ['<', '<=', '!=']);
- // If we are comparing to 0, we need an additional $not flip.
- if ($count == 0) {
- $not = ! $not;
- }
// All related ids.
- $relatedIds = array_keys($relationCount);
-
- // Add whereIn to the query.
- return $this->whereIn($key, $relatedIds, $boolean, $not);
+ return array_keys($relationCount);
}
}
\ No newline at end of file
diff --git a/tests/HybridRelationsTest.php b/tests/HybridRelationsTest.php
index c6b0e558ad47dc713e8163fcd6986dc09639bb87..6eb6bb828f4512cd345bd9bf885c911bd227df58 100644
--- a/tests/HybridRelationsTest.php
+++ b/tests/HybridRelationsTest.php
@@ -76,7 +76,7 @@ class HybridRelationsTest extends TestCase
}
- public function testRelationConstraints()
+ public function testHybridWhereHas()
{
$user = new MysqlUser;
$otherUser = new MysqlUser;
@@ -129,4 +129,66 @@ class HybridRelationsTest extends TestCase
$this->assertEquals(2, $books->count());
}
+
+ public function testHybridWith()
+ {
+ $user = new MysqlUser;
+ $otherUser = new MysqlUser;
+ $this->assertInstanceOf('MysqlUser', $user);
+ $this->assertInstanceOf('Illuminate\Database\MySqlConnection', $user->getConnection());
+ $this->assertInstanceOf('MysqlUser', $otherUser);
+ $this->assertInstanceOf('Illuminate\Database\MySqlConnection', $otherUser->getConnection());
+
+ //MySql User
+ $user->name = "John Doe";
+ $user->id = 2;
+ $user->save();
+ // Other user
+ $otherUser->name = 'Other User';
+ $otherUser->id = 3;
+ $otherUser->save();
+ // Make sure they are created
+ $this->assertTrue(is_int($user->id));
+ $this->assertTrue(is_int($otherUser->id));
+ // Clear to start
+ Book::truncate();
+ MysqlBook::truncate();
+ // Create books
+ // Mysql relation
+ $user->mysqlBooks()->saveMany([
+ new MysqlBook(['title' => 'Game of Thrones']),
+ new MysqlBook(['title' => 'Harry Potter']),
+ ]);
+
+ $otherUser->mysqlBooks()->saveMany([
+ new MysqlBook(['title' => 'Harry Plants']),
+ new MysqlBook(['title' => 'Harveys']),
+ new MysqlBook(['title' => 'Harry Planter']),
+ ]);
+ // SQL has many Hybrid
+ $user->books()->saveMany([
+ new Book(['title' => 'Game of Thrones']),
+ new Book(['title' => 'Harry Potter']),
+ ]);
+
+ $otherUser->books()->saveMany([
+ new Book(['title' => 'Harry Plants']),
+ new Book(['title' => 'Harveys']),
+ new Book(['title' => 'Harry Planter']),
+ ]);
+
+ MysqlUser::with('books')->get()
+ ->each(function ($user) {
+ $this->assertEquals($user->id, $user->books->count());
+ });
+
+ MysqlUser::whereHas('mysqlBooks', function ($query) {
+ return $query->where('title', 'LIKE', 'Harry%');
+ })
+ ->with('books')
+ ->get()
+ ->each(function ($user) {
+ $this->assertEquals($user->id, $user->books->count());
+ });
+ }
}
diff --git a/tests/models/MysqlBook.php b/tests/models/MysqlBook.php
index 5f79346eee6f5b0a274ef4cd919a133fd300bf48..7e755f7a6f45ef50113fe43db17fb96ffe19607f 100644
--- a/tests/models/MysqlBook.php
+++ b/tests/models/MysqlBook.php
@@ -27,7 +27,8 @@ class MysqlBook extends Eloquent
if (!$schema->hasTable('books')) {
Schema::connection('mysql')->create('books', function ($table) {
$table->string('title');
- $table->string('author_id');
+ $table->string('author_id')->nullable();
+ $table->integer('mysql_user_id')->unsigned()->nullable();
$table->timestamps();
});
}
diff --git a/tests/models/MysqlUser.php b/tests/models/MysqlUser.php
index 7f2563891919bb1411873ed5d10c14c49d775550..ca15c53ff8560a98253ad32310d3349dbf700034 100644
--- a/tests/models/MysqlUser.php
+++ b/tests/models/MysqlUser.php
@@ -21,6 +21,11 @@ class MysqlUser extends Eloquent
return $this->hasOne('Role');
}
+ public function mysqlBooks()
+ {
+ return $this->hasMany(MysqlBook::class);
+ }
+
/**
* Check if we need to run the schema.
*/