1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Spiral Framework. |
4
|
|
|
* |
5
|
|
|
* @license MIT |
6
|
|
|
* @author Anton Titov (Wolfy-J) |
7
|
|
|
*/ |
8
|
|
|
namespace Spiral\ORM; |
9
|
|
|
|
10
|
|
|
use Spiral\Database\Exceptions\QueryException; |
11
|
|
|
use Spiral\Models\ActiveEntityInterface; |
12
|
|
|
use Spiral\Models\Events\EntityEvent; |
13
|
|
|
use Spiral\ORM\Exceptions\RecordException; |
14
|
|
|
use Spiral\ORM\Traits\FindTrait; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Entity with ability to be saved and direct access to source. Record behaviour are defined in |
18
|
|
|
* protected or private properties such as schema, defaults and etc. |
19
|
|
|
* |
20
|
|
|
* Example: |
21
|
|
|
* |
22
|
|
|
* class User extends Record |
23
|
|
|
* { |
24
|
|
|
* protected $schema = [ |
25
|
|
|
* 'id' => 'primary', |
26
|
|
|
* 'name' => 'string', |
27
|
|
|
* 'biography' => 'text' |
28
|
|
|
* ]; |
29
|
|
|
* } |
30
|
|
|
* |
31
|
|
|
* You can pass additional options for some of your columns: |
32
|
|
|
* protected $schema = [ |
33
|
|
|
* 'pinCode' => 'string(128)', //String length |
34
|
|
|
* 'status' => 'enum(active, hidden)', //Enum values |
35
|
|
|
* 'balance' => 'decimal(10, 2)' //Decimal size and precision |
36
|
|
|
* ]; |
37
|
|
|
* |
38
|
|
|
* Every created column will be stated as NOT NULL with forced default value, if you want to |
39
|
|
|
* have nullable columns, specify special data key: protected $schema = [ |
40
|
|
|
* 'name' => 'string, nullable' |
41
|
|
|
* ]; |
42
|
|
|
* |
43
|
|
|
* You can easily combine table and relations definition in one schema: |
44
|
|
|
* protected $schema = [ |
45
|
|
|
* |
46
|
|
|
* //Table schema |
47
|
|
|
* 'id' => 'bigPrimary', |
48
|
|
|
* 'name' => 'string', |
49
|
|
|
* 'email' => 'string', |
50
|
|
|
* 'phoneNumber' => 'string(32)', |
51
|
|
|
* |
52
|
|
|
* //Relations |
53
|
|
|
* 'profile' => [ |
54
|
|
|
* self::HAS_ONE => 'Records\Profile', |
55
|
|
|
* self::INVERSE => 'user' |
56
|
|
|
* ], |
57
|
|
|
* 'roles' => [ |
58
|
|
|
* self::MANY_TO_MANY => 'Records\Role', |
59
|
|
|
* self::INVERSE => 'users' |
60
|
|
|
* ] |
61
|
|
|
* ]; |
62
|
|
|
* |
63
|
|
|
* Set of indexes to be created for associated record table, indexes only created when record is |
64
|
|
|
* not abstract and has active schema set to true. |
65
|
|
|
* |
66
|
|
|
* Use constants INDEX and UNIQUE to describe indexes, you can also create compound indexes: |
67
|
|
|
* protected $indexes = [ |
68
|
|
|
* [self::UNIQUE, 'email'], |
69
|
|
|
* [self::INDEX, 'board_id'], |
70
|
|
|
* [self::INDEX, 'board_id', 'check_id'] |
71
|
|
|
* ]; |
72
|
|
|
* |
73
|
|
|
* Configuration properties: |
74
|
|
|
* - schema |
75
|
|
|
* - defaults |
76
|
|
|
* - secured (* by default) |
77
|
|
|
* - fillable |
78
|
|
|
* - validates |
79
|
|
|
* - database |
80
|
|
|
* - table |
81
|
|
|
* - indexes |
82
|
|
|
*/ |
83
|
|
|
class Record extends RecordEntity implements ActiveEntityInterface |
84
|
|
|
{ |
85
|
|
|
use FindTrait; |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* {@inheritdoc} |
89
|
|
|
* |
90
|
|
|
* Create or update record data in database. Record will validate all EMBEDDED and loaded |
91
|
|
|
* relations. |
92
|
|
|
* |
93
|
|
|
* @return bool |
94
|
|
|
* |
95
|
|
|
* @throws RecordException |
96
|
|
|
* @throws QueryException |
97
|
|
|
* |
98
|
|
|
* @event saving() |
99
|
|
|
* @event saved() |
100
|
|
|
* @event updating() |
101
|
|
|
* @event updated() |
102
|
|
|
*/ |
103
|
|
|
public function save(): int |
104
|
|
|
{ |
105
|
|
|
//Associated mapper |
106
|
|
|
$mapper = $this->orm->mapper(static::class); |
107
|
|
|
|
108
|
|
|
if (!$this->isLoaded()) { |
109
|
|
|
$this->dispatch('saving', new EntityEvent($this)); |
110
|
|
|
|
111
|
|
|
//Primary key field name (if any) |
112
|
|
|
$primaryKey = $this->ormSchema[ORMInterface::M_PRIMARY_KEY]; |
113
|
|
|
|
114
|
|
|
//Inserting |
115
|
|
|
$lastID = $mapper->insert($this->serializeData()); |
116
|
|
|
|
117
|
|
|
if (!empty($primaryKey)) { |
118
|
|
|
//Updating record primary key |
119
|
|
|
$this->setField($primaryKey, $lastID); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
$this->loadedState(true)->dispatch('saved', new EntityEvent($this)); |
123
|
|
|
|
124
|
|
View Code Duplication |
} elseif ($this->isSolid() || $this->hasUpdates()) { |
|
|
|
|
125
|
|
|
$this->dispatch('updating', new EntityEvent($this)); |
126
|
|
|
|
127
|
|
|
//Performing update using associated mapper |
128
|
|
|
$mapper->update($this->stateCriteria(), $this->compileUpdates()); |
129
|
|
|
|
130
|
|
|
$this->dispatch('updated', new EntityEvent($this)); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
$this->flushUpdates(); |
134
|
|
|
$this->saveRelations(); |
135
|
|
|
|
136
|
|
|
return true; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* {@inheritdoc} |
141
|
|
|
* |
142
|
|
|
* @event deleting() |
143
|
|
|
* @event deleted() |
144
|
|
|
*/ |
145
|
|
|
public function delete() |
146
|
|
|
{ |
147
|
|
|
$this->dispatch('deleting', new EntityEvent($this)); |
148
|
|
|
|
149
|
|
|
if ($this->isLoaded()) { |
150
|
|
|
$this->orm->mapper(static::class)->delete($this->stateCriteria()); |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
$this->loadedState(self::DELETED)->dispatch('deleted', new EntityEvent($this)); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Save embedded relations. |
158
|
|
|
*/ |
159
|
|
|
protected function saveRelations() |
160
|
|
|
{ |
161
|
|
|
foreach ($this->relations as $name => $relation) { |
162
|
|
|
if (!$relation instanceof RelationInterface || !$this->isEmbedded($name)) { |
163
|
|
|
//Not constructed |
164
|
|
|
continue; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
$relation->saveRelated(); |
168
|
|
|
} |
169
|
|
|
} |
170
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.