diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index 2affc132c..6f57f015d 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -30,19 +30,19 @@ jobs: build: runs-on: ${{ matrix.os }} - name: PHP v${{ matrix.php }} with Mongo v${{ matrix.mongodb }} - continue-on-error: ${{ matrix.experimental }} + name: PHP v${{ matrix.php }} with MongoDB ${{ matrix.mongodb }} strategy: matrix: - include: - - { os: ubuntu-latest, php: 8.0, mongodb: '4.0', experimental: false } - - { os: ubuntu-latest, php: 8.0, mongodb: 4.2, experimental: false } - - { os: ubuntu-latest, php: 8.0, mongodb: 4.4, experimental: false } - - { os: ubuntu-latest, php: 8.0, mongodb: '5.0', experimental: false } - - { os: ubuntu-latest, php: 8.1, mongodb: '4.0', experimental: false } - - { os: ubuntu-latest, php: 8.1, mongodb: 4.2, experimental: false } - - { os: ubuntu-latest, php: 8.1, mongodb: 4.4, experimental: false } - - { os: ubuntu-latest, php: 8.1, mongodb: '5.0', experimental: false } + os: + - ubuntu-latest + mongodb: + - '4.0' + - '4.2' + - '4.4' + - '5.0' + php: + - '8.0' + - '8.1' services: mongo: image: mongo:${{ matrix.mongodb }} @@ -88,7 +88,7 @@ jobs: run: | ./vendor/bin/phpunit --coverage-clover coverage.xml env: - MONGO_HOST: 0.0.0.0 + MONGODB_URI: 'mongodb://127.0.0.1/' MYSQL_HOST: 0.0.0.0 MYSQL_PORT: 3307 - uses: codecov/codecov-action@v1 diff --git a/README.md b/README.md index 8ede587ec..06531dcb1 100644 --- a/README.md +++ b/README.md @@ -143,47 +143,36 @@ Keep in mind that these traits are not yet supported: Configuration ------------- -You can use MongoDB either as the main database, either as a side database. To do so, add a new `mongodb` connection to `config/database.php`: + +To configure a new MongoDB connection, add a new connection entry to `config/database.php`: ```php 'mongodb' => [ 'driver' => 'mongodb', - 'host' => env('DB_HOST', '127.0.0.1'), - 'port' => env('DB_PORT', 27017), + 'dsn' => env('DB_DSN'), 'database' => env('DB_DATABASE', 'homestead'), - 'username' => env('DB_USERNAME', 'homestead'), - 'password' => env('DB_PASSWORD', 'secret'), - 'options' => [ - // here you can pass more settings to the Mongo Driver Manager - // see https://www.php.net/manual/en/mongodb-driver-manager.construct.php under "Uri Options" for a list of complete parameters that you can use - - 'database' => env('DB_AUTHENTICATION_DATABASE', 'admin'), // required with Mongo 3+ - ], ], ``` -For multiple servers or replica set configurations, set the host to an array and specify each server host: +The `dsn` key contains the connection string used to connect to your MongoDB deployment. The format and available options are documented in the [MongoDB documentation](https://docs.mongodb.com/manual/reference/connection-string/). + +Instead of using a connection string, you can also use the `host` and `port` configuration options to have the connection string created for you. ```php 'mongodb' => [ 'driver' => 'mongodb', - 'host' => ['server1', 'server2', ...], - ... + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', 27017), + 'database' => env('DB_DATABASE', 'homestead'), + 'username' => env('DB_USERNAME', 'homestead'), + 'password' => env('DB_PASSWORD', 'secret'), 'options' => [ - 'replicaSet' => 'rs0', + 'appname' => 'homestead', ], ], ``` -If you wish to use a connection string instead of full key-value params, you can set it so. Check the documentation on MongoDB's URI format: https://docs.mongodb.com/manual/reference/connection-string/ - -```php -'mongodb' => [ - 'driver' => 'mongodb', - 'dsn' => env('DB_DSN'), - 'database' => env('DB_DATABASE', 'homestead'), -], -``` +The `options` key in the connection configuration corresponds to the [`uriOptions` parameter](https://www.php.net/manual/en/mongodb-driver-manager.construct.php#mongodb-driver-manager.construct-urioptions). Eloquent -------- @@ -223,7 +212,7 @@ class Book extends Model protected $primaryKey = 'id'; } -// Mongo will also create _id, but the 'id' property will be used for primary key actions like find(). +// MongoDB will also create _id, but the 'id' property will be used for primary key actions like find(). Book::create(['id' => 1, 'title' => 'The Fault in Our Stars']); ``` @@ -238,7 +227,7 @@ class Book extends Model } ``` -### Extending the Authenticable base model +### Extending the Authenticatable base model This package includes a MongoDB Authenticatable Eloquent class `Jenssegers\Mongodb\Auth\User` that you can use to replace the default Authenticatable class `Illuminate\Foundation\Auth\User` for your `User` model. ```php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4dc18cb41..15601b8dc 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -35,9 +35,8 @@ - + - diff --git a/src/Eloquent/Builder.php b/src/Eloquent/Builder.php index bfa3d634a..84e93b83f 100644 --- a/src/Eloquent/Builder.php +++ b/src/Eloquent/Builder.php @@ -174,7 +174,7 @@ public function raw($expression = null) $results = iterator_to_array($results, false); return $this->model->hydrate($results); - } // Convert Mongo BSONDocument to a single object. + } // Convert MongoDB BSONDocument to a single object. elseif ($results instanceof BSONDocument) { $results = $results->getArrayCopy(); @@ -192,7 +192,8 @@ public function raw($expression = null) * TODO Remove if https://github.com/laravel/framework/commit/6484744326531829341e1ff886cc9b628b20d73e * wiil be reverted * Issue in laravel frawework https://github.com/laravel/framework/issues/27791. - * @param array $values + * + * @param array $values * @return array */ protected function addUpdatedAtColumn(array $values) diff --git a/src/Eloquent/Model.php b/src/Eloquent/Model.php index 5836cf83d..b6c700d29 100644 --- a/src/Eloquent/Model.php +++ b/src/Eloquent/Model.php @@ -50,12 +50,12 @@ abstract class Model extends BaseModel /** * Custom accessor for the model's id. * - * @param mixed $value + * @param mixed $value * @return mixed */ public function getIdAttribute($value = null) { - // If we don't have a value for 'id', we will use the Mongo '_id' value. + // If we don't have a value for 'id', we will use the MongoDB '_id' value. // This allows us to work with models in a more sql-like way. if (! $value && array_key_exists('_id', $this->attributes)) { $value = $this->attributes['_id']; @@ -279,7 +279,7 @@ public function originalIsEquivalent($key) /** * Remove one or more fields. * - * @param mixed $columns + * @param mixed $columns * @return int */ public function drop($columns) @@ -325,8 +325,8 @@ public function push() /** * Remove one or more values from an array. * - * @param string $column - * @param mixed $values + * @param string $column + * @param mixed $values * @return mixed */ public function pull($column, $values) @@ -344,9 +344,9 @@ public function pull($column, $values) /** * Append one or more values to the underlying attribute value and sync with original. * - * @param string $column - * @param array $values - * @param bool $unique + * @param string $column + * @param array $values + * @param bool $unique */ protected function pushAttributeValues($column, array $values, $unique = false) { @@ -369,8 +369,8 @@ protected function pushAttributeValues($column, array $values, $unique = false) /** * Remove one or more values to the underlying attribute value and sync with original. * - * @param string $column - * @param array $values + * @param string $column + * @param array $values */ protected function pullAttributeValues($column, array $values) { @@ -402,7 +402,7 @@ public function getForeignKey() /** * Set the parent relation. * - * @param \Illuminate\Database\Eloquent\Relations\Relation $relation + * @param \Illuminate\Database\Eloquent\Relations\Relation $relation */ public function setParentRelation(Relation $relation) { @@ -495,7 +495,7 @@ protected function getRelationsWithoutParent() * Checks if column exists on a table. As this is a document model, just return true. This also * prevents calls to non-existent function Grammar::compileColumnListing(). * - * @param string $key + * @param string $key * @return bool */ protected function isGuardableColumn($key) diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index ed73010ea..f7b8fda82 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -37,11 +37,101 @@ public function testDb() $this->assertInstanceOf(Client::class, $connection->getMongoClient()); } - public function testDsnDb() + public function dataConnectionConfig(): Generator { - $connection = DB::connection('dsn_mongodb_db'); - $this->assertInstanceOf(Database::class, $connection->getMongoDB()); - $this->assertInstanceOf(Client::class, $connection->getMongoClient()); + yield 'Single host' => [ + 'expectedUri' => 'mongodb://some-host', + 'expectedDatabaseName' => 'tests', + 'config' => [ + 'host' => 'some-host', + 'database' => 'tests', + ], + ]; + + yield 'Host and port' => [ + 'expectedUri' => 'mongodb://some-host:12345', + 'expectedDatabaseName' => 'tests', + 'config' => [ + 'host' => 'some-host', + 'port' => 12345, + 'database' => 'tests', + ], + ]; + + yield 'Port in host name takes precedence' => [ + 'expectedUri' => 'mongodb://some-host:12345', + 'expectedDatabaseName' => 'tests', + 'config' => [ + 'host' => 'some-host:12345', + 'port' => 54321, + 'database' => 'tests', + ], + ]; + + yield 'Multiple hosts' => [ + 'expectedUri' => 'mongodb://host-1,host-2', + 'expectedDatabaseName' => 'tests', + 'config' => [ + 'host' => ['host-1', 'host-2'], + 'database' => 'tests', + ], + ]; + + yield 'Multiple hosts with same port' => [ + 'expectedUri' => 'mongodb://host-1:12345,host-2:12345', + 'expectedDatabaseName' => 'tests', + 'config' => [ + 'host' => ['host-1', 'host-2'], + 'port' => 12345, + 'database' => 'tests', + ], + ]; + + yield 'Multiple hosts with port' => [ + 'expectedUri' => 'mongodb://host-1:12345,host-2:54321', + 'expectedDatabaseName' => 'tests', + 'config' => [ + 'host' => ['host-1:12345', 'host-2:54321'], + 'database' => 'tests', + ], + ]; + + yield 'DSN takes precedence over host/port config' => [ + 'expectedUri' => 'mongodb://some-host:12345/auth-database', + 'expectedDatabaseName' => 'tests', + 'config' => [ + 'dsn' => 'mongodb://some-host:12345/auth-database', + 'host' => 'wrong-host', + 'port' => 54321, + 'database' => 'tests', + ], + ]; + + yield 'Database is extracted from DSN if not specified' => [ + 'expectedUri' => 'mongodb://some-host:12345/tests', + 'expectedDatabaseName' => 'tests', + 'config' => [ + 'dsn' => 'mongodb://some-host:12345/tests', + ], + ]; + } + + /** @dataProvider dataConnectionConfig */ + public function testConnectionConfig(string $expectedUri, string $expectedDatabaseName, array $config): void + { + $connection = new Connection($config); + $client = $connection->getMongoClient(); + + $this->assertSame($expectedUri, (string) $client); + $this->assertSame($expectedDatabaseName, $connection->getMongoDB()->getDatabaseName()); + } + + public function testConnectionWithoutConfiguredDatabase(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Database is not properly configured.'); + + new Connection(['dsn' => 'mongodb://some-host']); } public function testCollection() @@ -89,33 +179,4 @@ public function testDriverName() $driver = DB::connection('mongodb')->getDriverName(); $this->assertEquals('mongodb', $driver); } - - public function testAuth() - { - $host = Config::get('database.connections.mongodb.host'); - Config::set('database.connections.mongodb.username', 'foo'); - Config::set('database.connections.mongodb.password', 'bar'); - Config::set('database.connections.mongodb.options.database', 'custom'); - - $connection = DB::connection('mongodb'); - $this->assertEquals('mongodb://'.$host.'/custom', (string) $connection->getMongoClient()); - } - - public function testCustomHostAndPort() - { - Config::set('database.connections.mongodb.host', 'db1'); - Config::set('database.connections.mongodb.port', 27000); - - $connection = DB::connection('mongodb'); - $this->assertEquals('mongodb://db1:27000', (string) $connection->getMongoClient()); - } - - public function testHostWithPorts() - { - Config::set('database.connections.mongodb.port', 27000); - Config::set('database.connections.mongodb.host', ['db1:27001', 'db2:27002', 'db3:27000']); - - $connection = DB::connection('mongodb'); - $this->assertEquals('mongodb://db1:27001,db2:27002,db3:27000', (string) $connection->getMongoClient()); - } } diff --git a/tests/DsnTest.php b/tests/DsnTest.php deleted file mode 100644 index 85230f852..000000000 --- a/tests/DsnTest.php +++ /dev/null @@ -1,16 +0,0 @@ -assertInstanceOf(\Illuminate\Database\Eloquent\Collection::class, DsnAddress::all()); - } -} - -class DsnAddress extends Address -{ - protected $connection = 'dsn_mongodb'; -} diff --git a/tests/TestCase.php b/tests/TestCase.php index 584ff82de..dbe8c97c0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -9,7 +9,7 @@ class TestCase extends Orchestra\Testbench\TestCase /** * Get application providers. * - * @param \Illuminate\Foundation\Application $app + * @param \Illuminate\Foundation\Application $app * @return array */ protected function getApplicationProviders($app) @@ -24,7 +24,7 @@ protected function getApplicationProviders($app) /** * Get package providers. * - * @param \Illuminate\Foundation\Application $app + * @param \Illuminate\Foundation\Application $app * @return array */ protected function getPackageProviders($app) @@ -40,7 +40,7 @@ protected function getPackageProviders($app) /** * Define environment setup. * - * @param Illuminate\Foundation\Application $app + * @param Illuminate\Foundation\Application $app * @return void */ protected function getEnvironmentSetUp($app) @@ -56,8 +56,6 @@ protected function getEnvironmentSetUp($app) $app['config']->set('database.connections.mysql', $config['connections']['mysql']); $app['config']->set('database.connections.mongodb', $config['connections']['mongodb']); $app['config']->set('database.connections.mongodb2', $config['connections']['mongodb']); - $app['config']->set('database.connections.dsn_mongodb', $config['connections']['dsn_mongodb']); - $app['config']->set('database.connections.dsn_mongodb_db', $config['connections']['dsn_mongodb_db']); $app['config']->set('auth.model', 'User'); $app['config']->set('auth.providers.users.model', 'User'); diff --git a/tests/config/database.php b/tests/config/database.php index 5f45066a8..73f3d8697 100644 --- a/tests/config/database.php +++ b/tests/config/database.php @@ -1,35 +1,18 @@ [ - 'mongodb' => [ 'name' => 'mongodb', 'driver' => 'mongodb', - 'host' => $mongoHost, + 'dsn' => env('MONGODB_URI', 'mongodb://127.0.0.1/'), 'database' => env('MONGO_DATABASE', 'unittest'), ], - 'dsn_mongodb' => [ - 'driver' => 'mongodb', - 'dsn' => "mongodb://$mongoHost:$mongoPort", - 'database' => env('MONGO_DATABASE', 'unittest'), - ], - - 'dsn_mongodb_db' => [ - 'driver' => 'mongodb', - 'dsn' => "mongodb://$mongoHost:$mongoPort/".env('MONGO_DATABASE', 'unittest'), - ], - 'mysql' => [ 'driver' => 'mysql', 'host' => env('MYSQL_HOST', 'mysql'), - 'port' => $mysqlPort, + 'port' => env('MYSQL_PORT') ? (int) env('MYSQL_PORT') : 3306, 'database' => env('MYSQL_DATABASE', 'unittest'), 'username' => env('MYSQL_USERNAME', 'root'), 'password' => env('MYSQL_PASSWORD', ''), @@ -38,5 +21,4 @@ 'prefix' => '', ], ], - ];