1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Spiral Framework. |
4
|
|
|
* |
5
|
|
|
* @license MIT |
6
|
|
|
* @author Anton Titov (Wolfy-J) |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace Spiral\ORM; |
10
|
|
|
|
11
|
|
|
use Spiral\Database\Exceptions\QueryException; |
12
|
|
|
use Spiral\Models\ActiveEntityInterface; |
13
|
|
|
use Spiral\Models\Events\EntityEvent; |
14
|
|
|
use Spiral\ORM\Entities\RecordSource; |
15
|
|
|
use Spiral\ORM\Exceptions\ORMException; |
16
|
|
|
use Spiral\ORM\Exceptions\RecordException; |
17
|
|
|
use Spiral\ORM\Traits\FindTrait; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Entity with ability to be saved and direct access to source. |
21
|
|
|
*/ |
22
|
|
|
class Record extends RecordEntity implements ActiveEntityInterface |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* Static find methods. |
26
|
|
|
*/ |
27
|
|
|
use FindTrait; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Indication that save methods must be validated by default, can be altered by calling save |
31
|
|
|
* method with user arguments. |
32
|
|
|
*/ |
33
|
|
|
const VALIDATE_SAVE = true; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* {@inheritdoc} |
37
|
|
|
* |
38
|
|
|
* Create or update record data in database. Record will validate all EMBEDDED and loaded |
39
|
|
|
* relations. |
40
|
|
|
* |
41
|
|
|
* @see sourceTable() |
42
|
|
|
* @see updateChriteria() |
43
|
|
|
* @param bool|null $validate Overwrite default option declared in VALIDATE_SAVE to force or |
44
|
|
|
* disable validation before saving. |
45
|
|
|
* @return bool |
46
|
|
|
* @throws RecordException |
47
|
|
|
* @throws QueryException |
48
|
|
|
* @event saving() |
49
|
|
|
* @event saved() |
50
|
|
|
* @event updating() |
51
|
|
|
* @event updated() |
52
|
|
|
*/ |
53
|
|
|
public function save($validate = null) |
54
|
|
|
{ |
55
|
|
|
if (is_null($validate)) { |
56
|
|
|
//Using default model behaviour |
57
|
|
|
$validate = static::VALIDATE_SAVE; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
if ($validate && !$this->isValid()) { |
61
|
|
|
return false; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
if (!$this->isLoaded()) { |
65
|
|
|
$this->dispatch('saving', new EntityEvent($this)); |
66
|
|
|
|
67
|
|
|
//Primary key field name (if any) |
68
|
|
|
$primaryKey = $this->ormSchema()[ORM::M_PRIMARY_KEY]; |
69
|
|
|
|
70
|
|
|
//We will need to support records with multiple primary keys in future |
71
|
|
|
unset($this->fields[$primaryKey]); |
72
|
|
|
|
73
|
|
|
//Creating |
74
|
|
|
$lastID = $this->sourceTable()->insert($this->fields = $this->serializeData()); |
75
|
|
|
if (!empty($primaryKey)) { |
76
|
|
|
//Updating record primary key |
77
|
|
|
$this->fields[$primaryKey] = $lastID; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
$this->loadedState(true)->dispatch('saved', new EntityEvent($this)); |
81
|
|
|
|
82
|
|
|
//Saving record to entity cache if we have space for that |
83
|
|
|
$this->orm->rememberEntity($this, false); |
84
|
|
|
|
85
|
|
View Code Duplication |
} elseif ($this->isSolid() || $this->hasUpdates()) { |
|
|
|
|
86
|
|
|
$this->dispatch('updating', new EntityEvent($this)); |
87
|
|
|
|
88
|
|
|
//Updating changed/all field based on model criteria (in usual case primaryKey) |
89
|
|
|
$this->sourceTable()->update( |
90
|
|
|
$this->compileUpdates(), |
91
|
|
|
$this->stateCriteria() |
92
|
|
|
)->run(); |
93
|
|
|
|
94
|
|
|
$this->dispatch('updated', new EntityEvent($this)); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
$this->flushUpdates(); |
98
|
|
|
$this->saveRelations($validate); |
99
|
|
|
|
100
|
|
|
return true; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* {@inheritdoc} |
105
|
|
|
* |
106
|
|
|
* @event deleting() |
107
|
|
|
* @event deleted() |
108
|
|
|
*/ |
109
|
|
|
public function delete() |
110
|
|
|
{ |
111
|
|
|
$this->dispatch('deleting', new EntityEvent($this)); |
112
|
|
|
|
113
|
|
|
if ($this->isLoaded()) { |
114
|
|
|
$this->sourceTable()->delete($this->stateCriteria())->run(); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
//We don't really need to delete embedded or loaded relations, |
118
|
|
|
//we have foreign keys for that |
119
|
|
|
|
120
|
|
|
$this->fields = $this->ormSchema()[ORM::M_COLUMNS]; |
121
|
|
|
$this->loadedState(self::DELETED)->dispatch('deleted', new EntityEvent($this)); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Instance of ORM Selector associated with specific document. |
126
|
|
|
* |
127
|
|
|
* @see Component::staticContainer() |
128
|
|
|
* @param ORM $orm ORM component, global container will be called if not instance provided. |
129
|
|
|
* @return RecordSource |
130
|
|
|
* @throws ORMException |
131
|
|
|
*/ |
132
|
|
View Code Duplication |
public static function source(ORM $orm = null) |
|
|
|
|
133
|
|
|
{ |
134
|
|
|
/** |
135
|
|
|
* Using global container as fallback. |
136
|
|
|
* |
137
|
|
|
* @var ORM $orm |
138
|
|
|
*/ |
139
|
|
|
if (empty($orm)) { |
140
|
|
|
//Using global container as fallback |
141
|
|
|
$orm = self::staticContainer()->get(ORM::class); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
return $orm->source(static::class); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Save embedded relations. |
149
|
|
|
* |
150
|
|
|
* @param bool $validate |
151
|
|
|
*/ |
152
|
|
|
private function saveRelations($validate) |
153
|
|
|
{ |
154
|
|
|
foreach ($this->relations as $name => $relation) { |
155
|
|
|
if (!$relation instanceof RelationInterface) { |
156
|
|
|
//Was never constructed |
157
|
|
|
continue; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
if ($this->isEmbedded($name) && !$relation->saveAssociation($validate)) { |
161
|
|
|
throw new RecordException("Unable to save relation '{$name}'."); |
162
|
|
|
} |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
} |
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.