Document::save()   B
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 34
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 17
nc 3
nop 0
1
<?php
2
/**
3
 * Spiral, Core Components
4
 *
5
 * @author Wolfy-J
6
 */
7
8
namespace Spiral\ODM;
9
10
use MongoDB\BSON\ObjectID;
11
use Spiral\Models\ActiveEntityInterface;
12
use Spiral\ODM\Events\DocumentEvent;
13
14
/**
15
 * DocumentEntity with added ActiveRecord methods and ability to connect to associated source.
16
 * Document also provides an ability to specify aggregations using it's schema:
17
 *
18
 * const SCHEMA = [
19
 *     ...,
20
 *     'outer' => [
21
 *          self::ONE => Outer::class, [ //Reference to outer document using internal
22
 *              '_id' => 'self::outerID' //outerID value
23
 *          ]
24
 *      ],
25
 *     'many' => [
26
 *          self::MANY => Outer::class, [ //Reference to many outer document using
27
 *              'innerID' => 'self::_id'  //document primary key
28
 *          ]
29
 *     ]
30
 * ];
31
 *
32
 * Note: self::{name} construction will be replaced with document value in resulted query, even
33
 * in case of arrays ;) You can also use dot notation to get value from nested document.
34
 *
35
 * Attention, document will be linked to default database and named collection by default, use
36
 * properties database and collection to define your own custom database and collection.
37
 *
38
 * You can use property "index" to declare needed document indexes:
39
 *
40
 * Set of indexes to be created for associated collection. Use self::INDEX_OPTIONS or "@options"
41
 * for additional parameters.
42
 *
43
 * Example:
44
 * const INDEXES = [
45
 *      ['email' => 1, '@options' => ['unique' => true]],
46
 *      ['name' => 1]
47
 * ];
48
 *
49
 * @link http://php.net/manual/en/mongocollection.ensureindex.php
50
 *
51
 * Configuration properties:
52
 * - database
53
 * - collection
54
 * - schema
55
 * - indexes
56
 * - defaults
57
 * - secured (* by default)
58
 * - fillable
59
 */
60
abstract class Document extends DocumentEntity implements ActiveEntityInterface
61
{
62
    /**
63
     * Associated collection and database names, by default will be resolved based on a class name.
64
     */
65
    const DATABASE   = null;
66
    const COLLECTION = null;
67
68
    /**
69
     * Set of indexes to be created for associated collection. Use "@options" for additional
70
     * index options.
71
     *
72
     * Example:
73
     * const INDEXES = [
74
     *      ['email' => 1, '@options' => ['unique' => true]],
75
     *      ['name' => 1]
76
     * ];
77
     *
78
     * @link http://php.net/manual/en/mongocollection.ensureindex.php
79
     * @var array
80
     */
81
    const INDEXES = [];
82
83
    /**
84
     * Documents must ALWAYS have _id field.
85
     */
86
    const SCHEMA = [
87
        '_id' => ObjectID::class
88
    ];
89
90
    /**
91
     * _id is always nullable.
92
     */
93
    const DEFAULTS = [
94
        '_id' => null
95
    ];
96
97
    /**
98
     * {@inheritdoc}
99
     */
100
    public function __construct(array $data = [], ODMInterface $odm = null, array $schema = null)
101
    {
102
        parent::__construct($data, $odm, $schema);
103
104
        if (!$this->isLoaded()) {
105
            //Automatically force solidState for newly created documents
106
            $this->solidState(true);
107
        }
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function isLoaded(): bool
114
    {
115
        return !is_null($this->primaryKey());
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function primaryKey()
122
    {
123
        return $this->getField('_id', null, false);
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     *
129
     * @event create(DocumentEvent)
130
     * @event created(DocumentEvent)
131
     * @event update(DocumentEvent)
132
     * @event updated(DocumentEvent)
133
     */
134
    public function save(): int
135
    {
136
        if (!$this->isLoaded()) {
137
            $this->dispatch('create', new DocumentEvent($this));
138
139
            //Performing creation
140
            $result = $this->odm->collection(static::class)->insertOne($this->packValue(false));
141
            $this->setField('_id', $result->getInsertedId());
142
            $this->flushChanges();
143
            //Done with creation
144
145
            $this->dispatch('created', new DocumentEvent($this));
146
147
            return self::CREATED;
148
        }
149
150
        if ($this->isSolid() || $this->hasChanges()) {
151
            $this->dispatch('update', new DocumentEvent($this));
152
153
            //Performing an update
154
            $this->odm->collection(static::class)->updateOne(
155
                ['_id' => $this->primaryKey()],
156
                $this->buildAtomics()
157
            );
158
            $this->flushChanges();
159
            //Done with update
160
161
            $this->dispatch('updated', new DocumentEvent($this));
162
163
            return self::UPDATED;
164
        }
165
166
        return self::UNCHANGED;
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     *
172
     * @event delete(DocumentEvent)
173
     * @event deleted(DocumentEvent)
174
     */
175
    public function delete()
176
    {
177
        if (!$this->isLoaded()) {
178
            //Nothing to do, do we need an exception here?
179
            return;
180
        }
181
182
        $this->dispatch('delete', new DocumentEvent($this));
183
184
        //Performing deletion
185
        $this->odm->collection(static::class)->deleteOne(['_id' => $this->primaryKey()]);
186
        $this->setField('_id', null, false);
187
        //Done with deletion
188
189
        $this->dispatch('deleted', new DocumentEvent($this));
190
    }
191
}