1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\ODM\MongoDB\Id; |
6
|
|
|
|
7
|
|
|
use Doctrine\ODM\MongoDB\DocumentManager; |
8
|
|
|
use MongoDB\Operation\FindOneAndUpdate; |
9
|
|
|
use function get_class; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* IncrementGenerator is responsible for generating auto increment identifiers. It uses |
13
|
|
|
* a collection and generates the next id by using $inc on a field named "current_id". |
14
|
|
|
* |
15
|
|
|
* The 'collection' property determines which collection name is used to store the |
16
|
|
|
* id values. If not specified it defaults to 'doctrine_increment_ids'. |
17
|
|
|
* |
18
|
|
|
* The 'key' property determines the document ID used to store the id values in the |
19
|
|
|
* collection. If not specified it defaults to the name of the collection for the |
20
|
|
|
* document. |
21
|
|
|
*/ |
22
|
|
|
class IncrementGenerator extends AbstractIdGenerator |
23
|
|
|
{ |
24
|
|
|
/** @var string|null */ |
25
|
|
|
protected $collection = null; |
26
|
|
|
|
27
|
|
|
/** @var string|null */ |
28
|
|
|
protected $key = null; |
29
|
|
|
|
30
|
|
|
/** @var int */ |
31
|
|
|
protected $startingId = 1; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @param string $collection |
35
|
|
|
*/ |
36
|
|
|
public function setCollection($collection) |
37
|
|
|
{ |
38
|
|
|
$this->collection = $collection; |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
public function setKey(string $key) : void |
42
|
|
|
{ |
43
|
|
|
$this->key = $key; |
44
|
|
|
} |
45
|
|
|
|
46
|
2 |
|
public function setStartingId(int $startingId) : void |
47
|
|
|
{ |
48
|
2 |
|
$this->startingId = $startingId; |
49
|
2 |
|
} |
50
|
|
|
|
51
|
|
|
/** @inheritDoc */ |
52
|
11 |
|
public function generate(DocumentManager $dm, object $document) |
53
|
|
|
{ |
54
|
11 |
|
$className = get_class($document); |
55
|
11 |
|
$db = $dm->getDocumentDatabase($className); |
56
|
|
|
|
57
|
11 |
|
$key = $this->key ?: $dm->getDocumentCollection($className)->getCollectionName(); |
58
|
11 |
|
$collectionName = $this->collection ?: 'doctrine_increment_ids'; |
59
|
11 |
|
$collection = $db->selectCollection($collectionName); |
60
|
|
|
|
61
|
|
|
/* |
62
|
|
|
* Unable to use '$inc' and '$setOnInsert' together due to known bug. |
63
|
|
|
* @see https://jira.mongodb.org/browse/SERVER-10711 |
64
|
|
|
* Results in error: Cannot update 'current_id' and 'current_id' at the same time |
65
|
|
|
*/ |
66
|
11 |
|
$query = ['_id' => $key, 'current_id' => ['$exists' => true]]; |
67
|
11 |
|
$update = ['$inc' => ['current_id' => 1]]; |
68
|
11 |
|
$options = ['upsert' => false, 'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER]; |
69
|
11 |
|
$result = $collection->findOneAndUpdate($query, $update, $options); |
70
|
|
|
|
71
|
|
|
/* |
72
|
|
|
* Updated nothing - counter doesn't exist, creating new counter. |
73
|
|
|
* Not bothering with {$exists: false} in the criteria as that won't avoid |
74
|
|
|
* an exception during a possible race condition. |
75
|
|
|
*/ |
76
|
11 |
|
if ($result === null) { |
77
|
11 |
|
$query = ['_id' => $key]; |
78
|
11 |
|
$update = ['$inc' => ['current_id' => $this->startingId]]; |
79
|
11 |
|
$options = ['upsert' => true, 'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER]; |
80
|
11 |
|
$collection->findOneAndUpdate($query, $update, $options); |
81
|
|
|
|
82
|
11 |
|
return $this->startingId; |
83
|
|
|
} |
84
|
|
|
|
85
|
5 |
|
return $result['current_id'] ?? $this->startingId; |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
|