diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index 068415f37..2f991c76b 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -50,20 +50,9 @@ jobs: - name: "Install dependencies with Composer" uses: "ramsey/composer-install@3.1.0" - with: - composer-options: "--no-suggest" - - - name: "Format the code" - continue-on-error: true - run: | - mkdir .cache - ./vendor/bin/phpcbf # The -q option is required until phpcs v4 is released - name: "Run PHP_CodeSniffer" - run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr" - - - name: "Commit the changes" - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: "apply phpcbf formatting" + run: | + mkdir .cache + vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr diff --git a/docs/cache.txt b/docs/cache.txt index d3fd0f6e6..629065f09 100644 --- a/docs/cache.txt +++ b/docs/cache.txt @@ -14,7 +14,7 @@ Cache and Locks Configuration ------------- -To use MongoDB as a backend for `Laravel Cache and Locks `__, +To use MongoDB as a back end for `Laravel Cache and Locks `__, add a store configuration by specifying the ``mongodb`` driver in ``config/cache.php``: .. code-block:: php diff --git a/docs/compatibility.txt b/docs/compatibility.txt index 5197deab7..55971c9dd 100644 --- a/docs/compatibility.txt +++ b/docs/compatibility.txt @@ -15,7 +15,7 @@ Compatibility :class: singlecol .. meta:: - :keywords: laravel 9, laravel 10, laravel 11, laravel 12, 4.0, 4.1, 4.2, 5.0, 5.1, 5.2, 5.3 + :keywords: laravel 9, laravel 10, laravel 11, laravel 12, 4.0, 4.1, 4.2, 5.0, 5.1, 5.2, 5.3, 5.4 Laravel Compatibility --------------------- diff --git a/docs/database-collection.txt b/docs/database-collection.txt index be081c97b..e8d97711a 100644 --- a/docs/database-collection.txt +++ b/docs/database-collection.txt @@ -213,8 +213,8 @@ methods in your application: .. note:: - MongoDB is a schemaless database, so the preceding schema builder methods - query the database data rather than the schema. + MongoDB is a schema-flexible database, so the preceding schema + builder methods query the database data rather than the schema. Example ``````` @@ -269,9 +269,10 @@ collection fields: - ``Schema::hasColumns(string $, string[] $)``: checks if each specified field exists in at least one document -MongoDB is a schemaless database, so the preceding methods query the collection -data rather than the database schema. If the specified collection doesn't exist -or is empty, these methods return a value of ``false``. +MongoDB is a schema-flexible database, so the preceding methods query +the collection data rather than the database schema. If the specified +collection doesn't exist or is empty, these methods return a value of +``false``. .. note:: id Alias diff --git a/docs/feature-compatibility.txt b/docs/feature-compatibility.txt index 707f12c70..cce0932cc 100644 --- a/docs/feature-compatibility.txt +++ b/docs/feature-compatibility.txt @@ -201,7 +201,7 @@ Migration Features ------------------ The {+odm-short+} supports all Laravel migration features, but the -implementation is specific to MongoDB's schemaless model. +implementation is specific to MongoDB's schema-flexible model. Seeding Features ---------------- diff --git a/docs/fundamentals/atlas-search.txt b/docs/fundamentals/atlas-search.txt index ab957f9fa..a41385fda 100644 --- a/docs/fundamentals/atlas-search.txt +++ b/docs/fundamentals/atlas-search.txt @@ -179,7 +179,7 @@ following optional parameters to ``search()``: * - ``returnStoredSource`` - ``bool`` - Specifies whether to perform a full document lookup on the - backend database or return only stored source fields directly + back end database or return only stored source fields directly from Atlas Search * - ``tracking`` diff --git a/docs/fundamentals/read-operations.txt b/docs/fundamentals/read-operations.txt index 367e2d38d..674615ffb 100644 --- a/docs/fundamentals/read-operations.txt +++ b/docs/fundamentals/read-operations.txt @@ -17,7 +17,8 @@ Read Operations Retrieve Data Search Text Modify Query Results - Set Read Preference + Read Preference + Query Logging .. contents:: On this page :local: diff --git a/docs/fundamentals/read-operations/query-logging.txt b/docs/fundamentals/read-operations/query-logging.txt new file mode 100644 index 000000000..27816b298 --- /dev/null +++ b/docs/fundamentals/read-operations/query-logging.txt @@ -0,0 +1,82 @@ +.. _laravel-query-logging: + +==================== +Enable Query Logging +==================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: monitoring, CRUD, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to enable query logging in +{+odm-long+}. Query logging can help you debug your queries and monitor +database interactions. + +.. include:: /includes/fundamentals/read-operations/before-you-get-started.rst + +Enable Logs On a Connection +--------------------------- + +To enable logs on a connection, you can use the ``enableQueryLog()`` +method on the ``DB`` facade. This method enables MongoDB command logging +on any queries that you perform on the database connection. + +After you enable query logging, any queries you perform are stored in +memory. To retrieve the logs, use one of the following methods: + +- ``getQueryLog()``: Returns a log of MongoDB queries +- ``getRawQueryLog()``: Returns a log of raw MongoDB queries + +The following example enables query logging, performs some queries, then +prints the query log: + +.. io-code-block:: + :copyable: true + + .. input:: /includes/fundamentals/read-operations/ReadOperationsTest.php + :language: php + :dedent: + :start-after: start-query-log + :end-before: end-query-log + :emphasize-lines: 1, 7 + + .. output:: + :language: json + :visible: false + + { + "query": "{ \"find\" : \"movies\", \"filter\" : { \"title\" : \"Carrie\" } }", + "bindings": [], + "time": 29476 + } + { + "query": "{ \"find\" : \"movies\", \"filter\" : { \"year\" : { \"$lt\" : { \"$numberInt\" : \"2005\" } } } }", + "bindings": [], + "time": 29861 + } + { + "query": "{ \"find\" : \"movies\", \"filter\" : { \"imdb.rating\" : { \"$gt\" : { \"$numberDouble\" : \"8.5\" } } } }", + "bindings": [], + "time": 27251 + } + +Additional Information +---------------------- + +To learn more about connecting to MongoDB, see the +:ref:`laravel-connect-to-mongodb`. + +To learn how to retrieve data based on filter criteria, see the +:ref:`laravel-fundamentals-read-retrieve` guide. diff --git a/docs/includes/framework-compatibility-laravel.rst b/docs/includes/framework-compatibility-laravel.rst index e8d59469d..4b0055692 100644 --- a/docs/includes/framework-compatibility-laravel.rst +++ b/docs/includes/framework-compatibility-laravel.rst @@ -8,7 +8,7 @@ - Laravel 10.x - Laravel 9.x - * - 5.2 to 5.3 + * - 5.2 to 5.4 - ✓ - ✓ - ✓ diff --git a/docs/includes/fundamentals/read-operations/ReadOperationsTest.php b/docs/includes/fundamentals/read-operations/ReadOperationsTest.php index 207fd442e..414b21d31 100644 --- a/docs/includes/fundamentals/read-operations/ReadOperationsTest.php +++ b/docs/includes/fundamentals/read-operations/ReadOperationsTest.php @@ -9,6 +9,13 @@ use MongoDB\Driver\ReadPreference; use MongoDB\Laravel\Tests\TestCase; +use function json_encode; +use function ob_get_flush; +use function ob_start; + +use const JSON_PRETTY_PRINT; +use const PHP_EOL; + class ReadOperationsTest extends TestCase { protected function setUp(): void @@ -183,4 +190,37 @@ public function testReadPreference(): void $this->assertNotNull($movies); $this->assertCount(2, $movies); } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testQueryLog(): void + { + $output = ''; + ob_start(function (string $buffer) use (&$output) { + $output .= $buffer; + }); + // start-query-log + DB::connection('mongodb')->enableQueryLog(); + + Movie::where('title', 'Carrie')->get(); + Movie::where('year', '<', 2005)->get(); + Movie::where('imdb.rating', '>', 8.5)->get(); + + $logs = DB::connection('mongodb')->getQueryLog(); + foreach ($logs as $log) { + echo json_encode($log, JSON_PRETTY_PRINT) . PHP_EOL; + } + + // end-query-log + $output = ob_get_flush(); + $this->assertNotNull($logs); + $this->assertNotEmpty($output); + + $this->assertStringContainsString('"query": "{ \"find\" : \"movies\", \"filter\" : { \"title\" : \"Carrie\" } }"', $output); + $this->assertStringContainsString('"query": "{ \"find\" : \"movies\", \"filter\" : { \"imdb.rating\" : { \"$gt\" : { \"$numberDouble\" : \"8.5\" } } } }"', $output); + $this->assertStringContainsString('"query": "{ \"find\" : \"movies\", \"filter\" : { \"imdb.rating\" : { \"$gt\" : { \"$numberDouble\" : \"8.5\" } } } }"', $output); + $this->assertMatchesRegularExpression('/"time": \d+/', $output); + } } diff --git a/docs/index.txt b/docs/index.txt index 1eb1d8657..0b01b7349 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -1,3 +1,5 @@ +.. _laravel-docs-landing: + =============== {+odm-long+} =============== diff --git a/docs/quick-start.txt b/docs/quick-start.txt index ebfcb7348..5bd04f353 100644 --- a/docs/quick-start.txt +++ b/docs/quick-start.txt @@ -26,6 +26,7 @@ Quick Start View Data Write Data Next Steps + Tutorial: Build a Back End Overview -------- diff --git a/docs/quick-start/backend-service-tutorial.txt b/docs/quick-start/backend-service-tutorial.txt new file mode 100644 index 000000000..9236c698a --- /dev/null +++ b/docs/quick-start/backend-service-tutorial.txt @@ -0,0 +1,481 @@ +.. _laravel-tutorial-backend-service: + +========================================================== +Tutorial: Build a Back End Service by Using {+odm-long+} +========================================================== + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: php framework, odm, code example, crud + :description: Learn how to set up a back end and perform CRUD operations by using Laravel MongoDB. + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this tutorial, you create a simple REST back end for a front-end app +by using {+odm-long+}. The tutorial uses Laravel's built-in API routing +features. + +Prerequisites +------------- + +Before you can start this tutorial, you need the following software +installed in your development environment: + +- MongoDB Atlas cluster with sample data loaded. To learn how to create + a cluster, see the :ref:`laravel-quick-start-create-deployment` step + of the Quick Start guide. +- `PHP `__. +- `Composer `__. +- `{+php-extension+} `__. +- A terminal app and shell. For MacOS users, use Terminal or a similar app. + For Windows users, use PowerShell. + +Steps +----- + +.. procedure:: + :style: connected + + .. step:: Create a Laravel project. + + First, create a Laravel project directory. Then, run the following + command to create a new Laravel project called ``laraproject``: + + .. code-block:: bash + + composer create-project laravel/laravel laraproject + + .. step:: Install {+odm-long+}. + + To check that {+odm-long+} is running in the web server, + add a webpage to your Laravel website. In your project, + navigate to ``/routes/web.php`` and add the following route: + + .. code-block:: php + + Route::get('/info', function () { + phpinfo(); + }); + + Then, run the following command in your shell to start + your application: + + .. code-block:: bash + + php artisan serve + + After the application begins running, navigate to + http://127.0.0.1:8000/info to view the PHPinfo page. Scroll down + to or search for the **mongodb** entry to verify that + the {+php-extension+} is installed. + + Run the following command in your shell to install {+odm-long+}: + + .. code-block:: bash + + composer require mongodb/laravel-mongodb:^{+package-version+} + + .. step:: Configure your MongoDB connection. + + Open your project's ``config/database.php`` file and update the + ``connections`` array as shown in the following code: + + .. code-block:: php + + 'connections' => [ + 'mongodb' => [ + 'driver' => 'mongodb', + 'dsn' => '', + 'database' => 'db', + ], + + Ensure that you replace the connection string placeholder + in the preceding code with your connection string before you run + your application. To learn how to locate your connection string, see + :ref:`laravel-quick-start-connection-string` in the Quick Start + guide. + + You can also set the default database connection. At the top of + the ``config/database.php`` file, change 'default' to the following: + + .. code-block:: php + + 'default' => 'mongodb', + + The Laravel application can now connect to the ``db`` database in + your MongoDB cluster. + + .. step:: Create an endpoint to ping your deployment. + + Run the following shell command to set up API routing: + + .. code-block:: bash + + php artisan install:api + + In the newly created ``routes/api.php`` file, add the following + route: + + .. code-block:: php + + // Add the DB use statement to the top of the file. + use Illuminate\Support\Facades\DB; + + Route::get('/ping', function (Request $request) { + $connection = DB::connection('mongodb'); + $msg = 'MongoDB is accessible!'; + try { + $connection->command(['ping' => 1]); + } catch (\Exception $e) { + $msg = 'MongoDB is not accessible. Error: ' . $e->getMessage(); + } + return ['msg' => $msg]; + }); + + Reload the application, then verify that + http://127.0.0.1:8000/api/ping shows the succesful ping message. + + .. step:: Create Eloquent models. + + Laravel is integrated with Eloquent, an ORM that abstracts the + database back end so that you can connect to different databases by + using a common interface. + + Eloquent provides a ``Model`` class to serve as the interface + between your code and a specific collection. Instances of the + ``Model`` classes represent rows of tables in relational + databases. In MongoDB, they are documents in the collection. + + .. tip:: + + You can define fillable fields in your Eloquent models + to enforce a document schema in your application and prevent + errors such as name typos. To learn more, see the + :ref:`laravel-model-mass-assignment` section of the Eloquent + Model Class guide. + + Create an Eloquent model called ``CustomerMongoDB`` by running + the following command from the project root: + + .. code-block:: bash + + php artisan make:model CustomerMongoDB + + Laravel creates the ``CustomerMongoDB`` class in the ``/models`` + directory. By default, models use the ``default`` database + connection, but you can specify which connection to use by adding + the ``$connection`` member to the class. You can also + specify the collection name by adding the ``$collection`` member. + + Ensure you include the ``use`` statement for the MongoDB Eloquent + model. This is necessary to set ``_id`` as the primary key. + + Replace the contents of the ``CustomerMongoDB.php`` file with the + following code: + + .. code-block:: php + + use MongoDB\Laravel\Eloquent\Model; + + class CustomerMongoDB extends Model + { + // the selected database as defined in /config/database.php + protected $connection = 'mongodb'; + + // equivalent to $table for MySQL + protected $collection = 'laracoll'; + + // defines the schema for top-level properties (optional). + protected $fillable = ['guid', 'first_name', 'family_name', 'email', 'address']; + } + + .. step:: Perform CRUD operations. + + After you create your models, you can perform data operations. + + Create the following route in your ``api.php`` file: + + .. code-block:: php + + Route::get('/create_eloquent_mongo/', function (Request $request) { + $success = CustomerMongoDB::create([ + 'guid'=> 'cust_1111', + 'first_name'=> 'John', + 'family_name' => 'Doe', + 'email' => 'j.doe@gmail.com', + 'address' => '123 my street, my city, zip, state, country' + ]); + }); + + After you insert the document, you can retrieve it by using the + ``where()`` method as shown in the following code: + + .. code-block:: php + + Route::get('/find_eloquent/', function (Request $request) { + $customer = CustomerMongoDB::where('guid', 'cust_1111')->get(); + }); + + Eloquent allows you to find data by using complex queries with + multiple matching conditions. + + You can also update and delete data shown in the following routes: + + .. code-block:: php + + Route::get('/update_eloquent/', function (Request $request) { + $result = CustomerMongoDB::where('guid', 'cust_1111')->update( ['first_name' => 'Jimmy'] ); + }); + + Route::get('/delete_eloquent/', function (Request $request) { + $result = CustomerMongoDB::where('guid', 'cust_1111')->delete(); + }); + + At this point, your MongoDB-connected back-end service is + running, but MongoDB provides more functionality to support your + operations. + + .. step:: Perform operations on nested data. + + {+odm-long+} offers MongoDB-specific operations for nested data. + However, adding nested data is also intuitive without using + the ``embedsMany()`` and ``embedsOne()`` methods. + + As shown in the preceding step, you can define top-level schema + attributes. However, it is more complicated when to define these + attribute if your documents include arrays and embedded documents. + + You can create the model's data structures in PHP. In the + following example, the ``address`` field is an object type. + The ``email`` field is an array of strings: + + .. code-block:: php + + Route::get('/create_nested/', function (Request $request) { + $message = "executed"; + $success = null; + + $address = new stdClass; + $address->street = '123 my street name'; + $address->city = 'my city'; + $address->zip= '12345'; + $emails = ['j.doe@gmail.com', 'j.doe@work.com']; + + try { + $customer = new CustomerMongoDB(); + $customer->guid = 'cust_2222'; + $customer->first_name = 'John'; + $customer->family_name= 'Doe'; + $customer->email= $emails; + $customer->address= $address; + $success = $customer->save(); // save() returns 1 or 0 + } + catch (\Exception $e) { + $message = $e->getMessage(); + } + return ['msg' => $message, 'data' => $success]; + }); + + When you access the ``/api/create_nested/`` endpoint, it creates a + document in MongoDB: + + .. code-block:: json + + { + "_id": {...}, + "guid": "cust_2222", + "first_name": "John", + "family_name": "Doe", + "email": [ + "j.doe@gmail.com", + "j.doe@work.com" + ], + "address": { + "street": "123 my street name", + "city": "my city", + "zip": "12345" + }, + "updated_at": { + "$date": "2025-05-27T17:38:28.793Z" + }, + "created_at": { + "$date": "2025-05-27T17:38:28.793Z" + } + } + + .. step:: Use the MongoDB Query API. + + MongoDB provides the Query API for optimized queries. + + You can begin to build a query by using a ``collection`` object. + Eloquent exposes the full capabilities of the underlying database + by using "raw queries," which Laravel sends to the database + without any processing from the Eloquent Query Builder. + + You can perform a raw native MongoDB query from the model as shown + in the following code: + + .. code-block:: php + + $mongodbquery = ['guid' => 'cust_1111']; + + // returns a "Illuminate\Database\Eloquent\Collection" Object + $results = CustomerMongoDB::whereRaw( $mongodbquery )->get(); + + You can also access the native MongoDB collection object and + perform a query that returns objects such as native MongoDB + documents or cursors: + + .. code-block:: php + + $mongodbquery = ['guid' => 'cust_1111', ]; + $mongodb_native_collection = DB::connection('mongodb')->getCollection('laracoll'); + $document = $mongodb_native_collection->findOne( $mongodbquery ); + $cursor = $mongodb_native_collection->find( $mongodbquery ); + + The following code demonstrates multiple ways to perform queries: + + .. code-block:: php + + Route::get('/find_native/', function (Request $request) { + // a simple MongoDB query that looks for a customer based on the guid + $mongodbquery = ['guid' => 'cust_2222']; + + // Option #1 + // ========= + // use Eloquent's whereRaw() function + // returns a "Illuminate\Database\Eloquent\Collection" Object + + $results = CustomerMongoDB::whereRaw( $mongodbquery )->get(); + + // Option #2 & #3 + // ============== + // use the native MongoDB driver Collection object and the Query API + + $mdb_collection = DB::connection('mongodb')->getCollection('laracoll'); + + // find the first document that matches the query + $mdb_bsondoc = $mdb_collection->findOne( $mongodbquery ); // returns a "MongoDB\Model\BSONDocument" Object + + // to convert the MongoDB Document to a Laravel Model, use the Model's newFromBuilder() method + $cust = new CustomerMongoDB(); + $one_doc = $cust->newFromBuilder((array) $mdb_bsondoc); + + // find all documents because you pass an empty query + $mdb_cursor = $mdb_collection->find(); // returns a "MongoDB\Driver\Cursor" object + $cust_array = array(); + foreach ($mdb_cursor->toArray() as $bson) { + $cust_array[] = $cust->newFromBuilder( $bson ); + } + + return ['msg' => 'executed', 'whereraw' => $results, 'document' => $one_doc, 'cursor_array' => $cust_array]; + }); + + The following code demonstrates how to use the ``updateOne()`` + method to update documents: + + .. code-block:: php + + Route::get('/update_native/', function (Request $request) { + $mdb_collection = DB::connection('mongodb')->getCollection('laracoll'); + $match = ['guid' => 'cust_2222']; + $update = ['$set' => ['first_name' => 'Henry', 'address.street' => '777 new street name'] ]; + $result = $mdb_collection->updateOne($match, $update ); + return ['msg' => 'executed', 'matched_docs' => $result->getMatchedCount(), 'modified_docs' => $result->getModifiedCount()]; + }); + + The following code demonstrates how to use the ``deleteOne()`` + method to delete documents: + + .. code-block:: php + + Route::get('/delete_native/', function (Request $request) { + $mdb_collection = DB::connection('mongodb')->getCollection('laracoll'); + $match = ['guid' => 'cust_2222']; + $result = $mdb_collection->deleteOne($match ); + return ['msg' => 'executed', 'deleted_docs' => + $result->getDeletedCount() ]; + }); + + To learn more about how to perform CRUD operations, see the + :ref:`laravel-fundamentals-write-ops` and + :ref:`laravel-fundamentals-read-ops` guides. + + .. step:: Use the aggregation framework. + + An aggregation pipeline is a task in MongoDB's aggregation + framework. You can use the aggregation framework to perform + various tasks such as real-time dashboards and big data analysis. + + An aggregation pipeline consists of multiple stages in which the + output of each stage is the input of the following stage. + This step uses the ``sample_mflix`` from the :atlas:`Atlas sample + datasets `. Laravel allows you to access multiple + MongoDB databases in the same app, so add the ``sample_mflix`` + database connection to ``database.php``: + + .. code-block:: php + + 'mongodb_mflix' => [ + 'driver' => 'mongodb', + 'dsn' => env('DB_URI'), + 'database' => 'sample_mflix', + ], + + Next, create the ``/aggregate/`` API endpoint and define an + aggregation pipeline to retrieve data from the ``movies`` + collection, compute the average movie rating for each genre, and + return a list. + + .. code-block:: php + + Route::get('/aggregate/', function (Request $request) { + $mdb_collection = DB::connection('mongodb_mflix')->getCollection('movies'); + + $stage0 = ['$unwind' => ['path' => '$genres']]; + $stage1 = ['$group' => ['_id' => '$genres', 'averageGenreRating' => ['$avg' => '$imdb.rating']]]; + $stage2 = ['$sort' => ['averageGenreRating' => -1]]; + $aggregation = [$stage0, $stage1, $stage2]; + + $mdb_cursor = $mdb_collection->aggregate( $aggregation ); + + return ['msg' => 'executed', 'data' => $mdb_cursor->toArray() ]; + }); + + {+odm-long+} provides the :ref:`laravel-aggregation-builder` to + build type-safe aggregation pipelines directly from your models. + We recommend using the aggregation builder to perform + aggregations. + + .. step:: Use indexes to optimize query performance. + + You can create indexes to support your queries and improve + performance. To learn more about how to create indexes + programmatically, see the :ref:`laravel-eloquent-indexes` section + of the Schema Builder guide. + +Conclusion +---------- + +In this tutorial, you learned how to create a back-end service by using +Laravel and MongoDB for a front-end web application. +This tutorial also showed how you can use the document model to improve +database efficiency and scalability. You can use the document model with the +MongoDB Query API to create better apps with less downtime. + +You can access the full code for this tutorial in the +:github:`laravel-mongodb-tutorial +` repository on GitHub. + +Navigate through the rest of the :ref:`laravel-docs-landing` +documentation to learn more about {+odm-long+}'s features. diff --git a/docs/quick-start/download-and-install.txt b/docs/quick-start/download-and-install.txt index 293425791..f6a44a264 100644 --- a/docs/quick-start/download-and-install.txt +++ b/docs/quick-start/download-and-install.txt @@ -47,9 +47,9 @@ to a Laravel web application. .. step:: Install the {+php-extension+} {+odm-long+} requires the {+php-extension+} to manage MongoDB - connections and commands. - Follow the :php:`Installing the MongoDB PHP Driver with PECL - ` guide to install the {+php-extension+}. + connections and commands. To learn how to install the {+php-extension+}, see + the `Install the MongoDB PHP extension `__ + step of the Get Started with the PHP Library guide. .. step:: Install Laravel diff --git a/docs/quick-start/next-steps.txt b/docs/quick-start/next-steps.txt index 2853777fb..dc155326f 100644 --- a/docs/quick-start/next-steps.txt +++ b/docs/quick-start/next-steps.txt @@ -21,7 +21,11 @@ You can download the web application project by cloning the `laravel-quickstart `__ GitHub repository. -.. tip:: Build a Full Stack Application +.. tip:: Tutorials + + Learn how to implement more CRUD functionality in a {+odm-long+} + application by following the :ref:`Build a Back End Service by Using + {+odm-long+} ` tutorial. Learn how to build a full stack application that uses {+odm-long+} by following along with the `Full Stack Instagram Clone with Laravel and diff --git a/docs/quick-start/view-data.txt b/docs/quick-start/view-data.txt index f29b2bd12..34be94e9e 100644 --- a/docs/quick-start/view-data.txt +++ b/docs/quick-start/view-data.txt @@ -105,9 +105,9 @@ View MongoDB Data .. code-block:: none :copyable: false - INFO View [resources/views/browse_movie.blade.php] created successfully. + INFO View [resources/views/browse_movies.blade.php] created successfully. - Open the ``browse_movie.blade.php`` view file in the ``resources/views`` + Open the ``browse_movies.blade.php`` view file in the ``resources/views`` directory. Replace the contents with the following code and save the changes: @@ -141,7 +141,7 @@ View MongoDB Data .. step:: Optionally, view your results as JSON documents - Rather than generating a view and editing the ``browse_movie.blade.php`` file, you can + Rather than generating a view and editing the ``browse_movies.blade.php`` file, you can use the ``toJson()`` method to display your results in JSON format. Replace the ``show()`` function with the following code to retrieve results and diff --git a/docs/transactions.txt b/docs/transactions.txt index b4a7827ba..d42151d41 100644 --- a/docs/transactions.txt +++ b/docs/transactions.txt @@ -60,20 +60,11 @@ This guide contains the following sections: Requirements and Limitations ---------------------------- -To perform transactions in MongoDB, you must use the following MongoDB -version and topology: - -- MongoDB version 4.0 or later -- A replica set deployment or sharded cluster - MongoDB Server and the {+odm-short+} have the following limitations: -- In MongoDB versions 4.2 and earlier, write operations performed within a - transaction must be on existing collections. In MongoDB versions 4.4 and - later, the server automatically creates collections as necessary when - you perform write operations in a transaction. To learn more about this - limitation, see :manual:`Create Collections and Indexes in a Transaction ` - in the {+server-docs-name+}. +- MongoDB standalone deployments do not support transactions. To use + transactions, your deployment must be a multiple node replica set or + sharded cluster. - MongoDB does not support nested transactions. If you attempt to start a transaction within another one, the extension raises a ``RuntimeException``. diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 6823998fd..321672e6f 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -745,7 +745,10 @@ public function insert(array $values) $values = [$values]; } - $values = $this->aliasIdForQuery($values); + $values = array_map( + $this->aliasIdForQuery(...), + $values, + ); $options = $this->inheritConnectionOptions(); diff --git a/tests/ModelTest.php b/tests/ModelTest.php index ecfcb2b6a..88bd27e44 100644 --- a/tests/ModelTest.php +++ b/tests/ModelTest.php @@ -25,6 +25,7 @@ use MongoDB\Laravel\Tests\Models\IdIsString; use MongoDB\Laravel\Tests\Models\Item; use MongoDB\Laravel\Tests\Models\MemberStatus; +use MongoDB\Laravel\Tests\Models\NonIncrementing; use MongoDB\Laravel\Tests\Models\Soft; use MongoDB\Laravel\Tests\Models\SqlUser; use MongoDB\Laravel\Tests\Models\User; @@ -56,6 +57,7 @@ public function tearDown(): void Book::truncate(); Item::truncate(); Guarded::truncate(); + NonIncrementing::truncate(); parent::tearDown(); } @@ -106,6 +108,26 @@ public function testInsert(): void $this->assertEquals(35, $user->age); } + public function testInsertNonIncrementable(): void + { + $connection = DB::connection('mongodb'); + $connection->setRenameEmbeddedIdField(false); + + $nonIncrementing = new NonIncrementing(); + $nonIncrementing->id = '12345'; + $nonIncrementing->name = 'John Doe'; + + $nonIncrementing->save(); + + $this->assertTrue($nonIncrementing->exists); + $this->assertEquals(1, NonIncrementing::count()); + + $check = NonIncrementing::find($nonIncrementing->id); + $this->assertInstanceOf(NonIncrementing::class, $check); + $this->assertSame('12345', $check->id); + $this->assertEquals('John Doe', $check->name); + } + public function testUpdate(): void { $user = new User(); diff --git a/tests/Models/NonIncrementing.php b/tests/Models/NonIncrementing.php new file mode 100644 index 000000000..8a9ab31e2 --- /dev/null +++ b/tests/Models/NonIncrementing.php @@ -0,0 +1,26 @@ +assertIsArray($user->tags); } + #[TestWith([true])] + #[TestWith([false])] + public function testInsertWithCustomId(bool $renameEmbeddedIdField) + { + $connection = DB::connection('mongodb'); + $connection->setRenameEmbeddedIdField($renameEmbeddedIdField); + + $data = ['id' => 'abcdef', 'name' => 'John Doe']; + + DB::table('users')->insert($data); + + $user = User::find('abcdef'); + $this->assertInstanceOf(User::class, $user); + $this->assertSame('abcdef', $user->id); + } + public function testInsertGetId() { $id = DB::table('users')->insertGetId(['name' => 'John Doe']); diff --git a/tests/QueueTest.php b/tests/QueueTest.php index efc8f07ff..4de63391d 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -15,7 +15,7 @@ use MongoDB\Laravel\Queue\MongoQueue; use function app; -use function json_encode; +use function json_decode; class QueueTest extends TestCase { @@ -42,17 +42,16 @@ public function testQueueJobLifeCycle(): void $job = Queue::pop('test'); $this->assertInstanceOf(MongoJob::class, $job); $this->assertEquals(1, $job->isReserved()); - $this->assertEquals(json_encode([ - 'uuid' => $uuid, - 'displayName' => 'test', - 'job' => 'test', - 'maxTries' => null, - 'maxExceptions' => null, - 'failOnTimeout' => false, - 'backoff' => null, - 'timeout' => null, - 'data' => ['action' => 'QueueJobLifeCycle'], - ]), $job->getRawBody()); + $payload = json_decode($job->getRawBody(), true); + $this->assertEquals($uuid, $payload['uuid']); + $this->assertEquals('test', $payload['displayName']); + $this->assertEquals('test', $payload['job']); + $this->assertNull($payload['maxTries']); + $this->assertNull($payload['maxExceptions']); + $this->assertFalse($payload['failOnTimeout']); + $this->assertNull($payload['backoff']); + $this->assertNull($payload['timeout']); + $this->assertEquals(['action' => 'QueueJobLifeCycle'], $payload['data']); // Remove reserved job $job->delete();