├── .gitignore
├── .idea
├── php.xml
├── misc.xml
├── vcs.xml
├── .gitignore
├── laravel-mongodb-transactions.iml
├── modules.xml
└── composerJson.xml
├── src
└── Mongodb
│ ├── Eloquent
│ └── Model.php
│ ├── MongodbServiceProvider.php
│ ├── Connection.php
│ └── Query
│ └── Builder.php
├── composer.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/.idea/php.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/laravel-mongodb-transactions.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/composerJson.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Mongodb/Eloquent/Model.php:
--------------------------------------------------------------------------------
1 | getConnection();
15 |
16 | return new QueryBuilder($connection, $connection->getPostProcessor());
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "imanrjb/laravel-mongodb",
3 | "description": "Extend Jenssegers/laravel-mongodb to support transaction function",
4 | "type": "library",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Iman RJB",
9 | "email": "iman.rjb@gmail.com"
10 | }
11 | ],
12 | "minimum-stability": "dev",
13 | "require": {
14 | "jenssegers/mongodb": "^3.8"
15 | },
16 | "autoload": {
17 | "psr-4": {
18 | "ImanRjb\\": "src/"
19 | }
20 | },
21 | "extra": {
22 | "laravel": {
23 | "providers": [
24 | "ImanRjb\\Mongodb\\MongodbServiceProvider"
25 | ]
26 | }
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/Mongodb/MongodbServiceProvider.php:
--------------------------------------------------------------------------------
1 | app['db']);
15 |
16 | Model::setEventDispatcher($this->app['events']);
17 | }
18 |
19 | public function register()
20 | {
21 | // Add database driver.
22 | $this->app->resolving('db', function ($db) {
23 | $db->extend('mongodb', function ($config, $name) {
24 | $config['name'] = $name;
25 | return new Connection($config);
26 | });
27 | });
28 |
29 | // Add connector for queue support.
30 | $this->app->resolving('queue', function ($queue) {
31 | $queue->addConnector('mongodb', function () {
32 | return new MongoConnector($this->app['db']);
33 | });
34 | });
35 | }
36 | }
--------------------------------------------------------------------------------
/src/Mongodb/Connection.php:
--------------------------------------------------------------------------------
1 | getSession()) {
16 | $this->session = $this->getMongoClient()->startSession();
17 | $this->session->startTransaction($options);
18 | }
19 | }
20 |
21 |
22 | public function commit()
23 | {
24 | if ($this->getSession()) {
25 | $this->session->commitTransaction();
26 | $this->clearSession();
27 | }
28 | }
29 |
30 |
31 | public function rollBack($toLevel = null)
32 | {
33 | if ($this->getSession()) {
34 | $this->session->abortTransaction();
35 | $this->clearSession();
36 | }
37 | }
38 |
39 |
40 | protected function clearSession()
41 | {
42 | $this->session = null;
43 | }
44 |
45 | /**
46 | *
47 | * @param string $collection
48 | * @return Builder|\Jenssegers\Mongodb\Query\Builder
49 | * @date 2019-07-22
50 | */
51 | public function collection($collection)
52 | {
53 | $query = new Query\Builder($this, $this->getPostProcessor());
54 |
55 | return $query->from($collection);
56 | }
57 |
58 | public function getSession()
59 | {
60 | return $this->session;
61 | }
62 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Laravel Mongodb (Transactional support)
2 |
3 | ### Introduction
4 |
5 |
6 | Jensseger's laravel-mongodb extension package is very popular among Laravel developers, but it lacks a transactional feature. mongoDB 4.x supports multi-document transactions. Therefore, this package extends [Jenssegers/laravel-mongodb](https://github.com/jenssegers/laravel-mongodb) with transactional support.
7 |
8 | 1. mongoDB transactions are based on the mongoDB4.x replica set environment. [mongoDB](https://docs.mongodb.com/manual/core/transactions)
9 | 2. This package depends on [Jenssegers/laravel-mongodb](https://packagist.org/packages/jenssegers/mongodb), so it needs to be installed first.
10 |
11 | ### Installation
12 |
13 | Regarding the use of packages, it is necessary to replace [Jenssegers/laravel-mongodb](https://packagist.org/packages/jenssegers/mongodb#installation):
14 |
15 | Install by composer
16 | ```bash
17 | composer require imanrjb/laravel-mongodb
18 | ```
19 |
20 | Laravel
21 | ```php
22 | //Jenssegers\Mongodb\MongodbServiceProvider::class,
23 | ImanRjb\Mongodb\MongodbServiceProvider::class
24 | ```
25 |
26 | Lumen
27 | ```php
28 | //$app->register(Jenssegers\Mongodb\MongodbServiceProvider::class);
29 | $app->register(ImanRjb\Mongodb\MongodbServiceProvider::class);
30 |
31 | $app->withEloquent();
32 | ```
33 |
34 | Eloquent
35 | --------
36 | Eloquent only expands on transaction-related content, so it directly replaces [Jenssegers/laravel-mongodb](https://github.com/jenssegers/laravel-mongodb#eloquent)
37 |
38 | ```php
39 | use ImanRjb\Mongodb\Eloquent\Model;
40 |
41 | class User extends Model {}
42 | ```
43 |
44 | ```php
45 | use ImanRjb\Mongodb\Eloquent\Model;
46 |
47 | class MyModel extends Model {
48 |
49 | protected $connection = 'mongodb';
50 |
51 | }
52 | ```
53 |
54 | For more Eloquent documentation see (http://laravel.com/docs/eloquent)
55 |
56 | ### Usage
57 |
58 | ```php
59 | DB::connection('mongodb')->beginTransaction();
60 |
61 | try {
62 | User::insert($userData);
63 | UserInfo::insert($userInfoData);
64 |
65 | DB::connection('mongodb')->commit();
66 | } catch (\Exception $e) {
67 | DB::connection('mongodb')->rollBack();
68 | throw $e;
69 | }
70 | ```
71 |
--------------------------------------------------------------------------------
/src/Mongodb/Query/Builder.php:
--------------------------------------------------------------------------------
1 | grammar = new Grammar;
18 | $this->connection = $connection;
19 | $this->processor = $processor;
20 | }
21 |
22 | /**
23 | * @inheritdoc
24 | */
25 | public function aggregate($function, $columns = [])
26 | {
27 | $this->aggregate = compact('function', 'columns');
28 |
29 | $previousColumns = $this->columns;
30 |
31 | // We will also back up the select bindings since the select clause will be
32 | // removed when performing the aggregate function. Once the query is run
33 | // we will add the bindings back onto this query so they can get used.
34 | $previousSelectBindings = $this->bindings['select'];
35 |
36 | $this->bindings['select'] = [];
37 |
38 | $results = $this->get($columns);
39 |
40 | // Once we have executed the query, we will reset the aggregate property so
41 | // that more select queries can be executed against the database without
42 | // the aggregate value getting in the way when the grammar builds it.
43 | $this->aggregate = null;
44 | $this->columns = $previousColumns;
45 | $this->bindings['select'] = $previousSelectBindings;
46 |
47 | if (isset($results[0])) {
48 | $result = (array) $results[0];
49 |
50 | return $result['aggregate'];
51 | }
52 | }
53 |
54 | /**
55 | * @inheritdoc
56 | */
57 | public function insert(array $values)
58 | {
59 | // Since every insert gets treated like a batch insert, we will have to detect
60 | // if the user is inserting a single document or an array of documents.
61 | $batch = true;
62 |
63 | foreach ($values as $value) {
64 | // As soon as we find a value that is not an array we assume the user is
65 | // inserting a single document.
66 | if (!is_array($value)) {
67 | $batch = false;
68 | break;
69 | }
70 | }
71 |
72 | if (!$batch) {
73 | $values = [$values];
74 | }
75 |
76 | // Batch insert
77 | $options = $this->session();
78 | $result = $this->collection->insertMany($values, $options);
79 |
80 | return (1 == (int) $result->isAcknowledged());
81 | }
82 |
83 | /**
84 | * @inheritdoc
85 | */
86 | public function insertGetId(array $values, $sequence = null)
87 | {
88 | $options = $this->session();
89 | $result = $this->collection->insertOne($values, $options);
90 |
91 | if (1 == (int) $result->isAcknowledged()) {
92 | if (is_null($sequence)) {
93 | $sequence = '_id';
94 | }
95 |
96 | // Return id
97 | return $sequence == '_id' ? $result->getInsertedId() : $values[$sequence];
98 | }
99 | }
100 |
101 | /**
102 | * @inheritdoc
103 | */
104 | public function update(array $values, array $options = [])
105 | {
106 | // Use $set as default operator.
107 | if (!Str::startsWith(key($values), '$')) {
108 | $values = ['$set' => $values];
109 | }
110 | $options = $this->session($options);
111 | return $this->performUpdate($values, $options);
112 | }
113 |
114 | /**
115 | * @inheritdoc
116 | */
117 | public function increment($column, $amount = 1, array $extra = [], array $options = [])
118 | {
119 | $query = ['$inc' => [$column => $amount]];
120 |
121 | if (!empty($extra)) {
122 | $query['$set'] = $extra;
123 | }
124 |
125 | // Protect
126 | $this->where(function ($query) use ($column) {
127 | $query->where($column, 'exists', false);
128 |
129 | $query->orWhereNotNull($column);
130 | });
131 |
132 | return $this->performUpdate($query, $options);
133 | }
134 |
135 | /**
136 | * @inheritdoc
137 | */
138 | public function decrement($column, $amount = 1, array $extra = [], array $options = [])
139 | {
140 | return $this->increment($column, -1 * $amount, $extra, $options);
141 | }
142 |
143 | /**
144 | * @inheritdoc
145 | */
146 | public function delete($id = null)
147 | {
148 | // If an ID is passed to the method, we will set the where clause to check
149 | // the ID to allow developers to simply and quickly remove a single row
150 | // from their database without manually specifying the where clauses.
151 | if (!is_null($id)) {
152 | $this->where('_id', '=', $id);
153 | }
154 |
155 | $options = $this->session();
156 | $wheres = $this->compileWheres();
157 | $result = $this->collection->DeleteMany($wheres, $options);
158 | if (1 == (int) $result->isAcknowledged()) {
159 | return $result->getDeletedCount();
160 | }
161 |
162 | return 0;
163 | }
164 |
165 | /**
166 | * @inheritdoc
167 | */
168 | public function truncate(): bool
169 | {
170 | $result = $this->collection->drop();
171 |
172 | return (1 == (int) $result->ok);
173 | }
174 |
175 | /**
176 | * Perform an update query.
177 | *
178 | * @param array $query
179 | * @param array $options
180 | * @return int
181 | */
182 | protected function performUpdate($query, array $options = [])
183 | {
184 | // Update multiple items by default.
185 | if (!array_key_exists('multiple', $options)) {
186 | $options['multiple'] = true;
187 | }
188 | $options = $this->session($options);
189 |
190 | $wheres = $this->compileWheres();
191 | $result = $this->collection->UpdateMany($wheres, $query, $options);
192 | if (1 == (int) $result->isAcknowledged()) {
193 | return $result->getModifiedCount() ? $result->getModifiedCount() : $result->getUpsertedCount();
194 | }
195 |
196 | return 0;
197 | }
198 |
199 | protected function session(array $options = [])
200 | {
201 | if ($session = $this->connection->getSession()) {
202 | $options['session'] = $this->connection->getSession();
203 | }
204 |
205 | return $options;
206 | }
207 | }
208 |
--------------------------------------------------------------------------------