Skip to content

Commit 114f918

Browse files
committed
Optimize Model::isDocumentModel
1 parent ef12043 commit 114f918

File tree

4 files changed

+89
-16
lines changed

4 files changed

+89
-16
lines changed

src/Eloquent/Model.php

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
use function array_key_exists;
1010
use function class_uses_recursive;
11+
use function is_object;
1112
use function is_subclass_of;
1213

1314
abstract class Model extends BaseModel
@@ -28,16 +29,36 @@ abstract class Model extends BaseModel
2829
*/
2930
protected $keyType = 'string';
3031

32+
private static $documentModelClasses = [];
33+
3134
/**
3235
* Indicates if the given model class is a MongoDB document model.
3336
* It must be a subclass of {@see BaseModel} and use the
3437
* {@see DocumentModel} trait.
3538
*
36-
* @param class-string|object $related
39+
* @param class-string|object $classOrObject
3740
*/
38-
public static function isDocumentModel(string|object $related): bool
41+
public static function isDocumentModel(string|object $classOrObject): bool
3942
{
40-
return is_subclass_of($related, BaseModel::class)
41-
&& array_key_exists(DocumentModel::class, class_uses_recursive($related));
43+
if (is_object($classOrObject)) {
44+
$classOrObject = $classOrObject::class;
45+
}
46+
47+
if (array_key_exists($classOrObject, self::$documentModelClasses)) {
48+
return self::$documentModelClasses[$classOrObject];
49+
}
50+
51+
// We know all child classes of this class are document models.
52+
if (is_subclass_of($classOrObject, self::class)) {
53+
return self::$documentModelClasses[$classOrObject] = true;
54+
}
55+
56+
// Document models must be subclasses of Laravel's base model class.
57+
if (! is_subclass_of($classOrObject, BaseModel::class)) {
58+
return self::$documentModelClasses[$classOrObject] = false;
59+
}
60+
61+
// Document models must use the DocumentModel trait.
62+
return self::$documentModelClasses[$classOrObject] = array_key_exists(DocumentModel::class, class_uses_recursive($classOrObject));
4263
}
4364
}

tests/Eloquent/ModelTest.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace MongoDB\Laravel\Tests\Eloquent;
4+
5+
use Generator;
6+
use Illuminate\Database\Eloquent\Model as BaseModel;
7+
use MongoDB\Laravel\Auth\User;
8+
use MongoDB\Laravel\Eloquent\DocumentModel;
9+
use MongoDB\Laravel\Eloquent\Model;
10+
use MongoDB\Laravel\Tests\Models\Book;
11+
use MongoDB\Laravel\Tests\Models\Casting;
12+
use MongoDB\Laravel\Tests\Models\SqlBook;
13+
use PHPUnit\Framework\Attributes\DataProvider;
14+
use PHPUnit\Framework\TestCase;
15+
16+
class ModelTest extends TestCase
17+
{
18+
#[DataProvider('provideDocumentModelClasses')]
19+
public function testIsDocumentModel(bool $expected, string|object $classOrObject): void
20+
{
21+
$this->assertSame($expected, Model::isDocumentModel($classOrObject));
22+
}
23+
24+
public static function provideDocumentModelClasses(): Generator
25+
{
26+
// Test classes
27+
yield [false, SqlBook::class];
28+
yield [true, Casting::class];
29+
yield [true, Book::class];
30+
31+
// Provided by the Laravel MongoDB package.
32+
yield [true, User::class];
33+
34+
// Instances of objects
35+
yield [false, new SqlBook()];
36+
yield [true, new Book()];
37+
38+
// Anonymous classes
39+
yield [
40+
true,
41+
new class extends Model {
42+
},
43+
];
44+
yield [
45+
true,
46+
new class extends BaseModel {
47+
use DocumentModel;
48+
},
49+
];
50+
yield [
51+
false,
52+
new class {
53+
use DocumentModel;
54+
},
55+
];
56+
yield [
57+
false,
58+
new class extends BaseModel {
59+
},
60+
];
61+
}
62+
}

tests/Models/CastObjectId.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,11 @@
44

55
namespace MongoDB\Laravel\Tests\Models;
66

7-
use Illuminate\Database\Eloquent\Model;
87
use MongoDB\Laravel\Eloquent\Casts\ObjectId;
9-
use MongoDB\Laravel\Eloquent\DocumentModel;
8+
use MongoDB\Laravel\Eloquent\Model;
109

1110
class CastObjectId extends Model
1211
{
13-
use DocumentModel;
14-
15-
protected $primaryKey = '_id';
16-
protected $keyType = 'string';
1712
protected $connection = 'mongodb';
1813
protected static $unguarded = true;
1914
protected $casts = [

tests/Models/Casting.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,11 @@
44

55
namespace MongoDB\Laravel\Tests\Models;
66

7-
use Illuminate\Database\Eloquent\Model;
87
use MongoDB\Laravel\Eloquent\Casts\BinaryUuid;
9-
use MongoDB\Laravel\Eloquent\DocumentModel;
8+
use MongoDB\Laravel\Eloquent\Model;
109

1110
class Casting extends Model
1211
{
13-
use DocumentModel;
14-
15-
protected $primaryKey = '_id';
16-
protected $keyType = 'string';
1712
protected $connection = 'mongodb';
1813
protected string $collection = 'casting';
1914

0 commit comments

Comments
 (0)