1
1
<?php
2
+
2
3
/*
3
4
* This file is part of the Orbitale DoctrineTools package.
4
5
*
18
19
use Doctrine \ORM \EntityRepository ;
19
20
use Doctrine \ORM \Mapping \ClassMetadata ;
20
21
use Symfony \Component \PropertyAccess \PropertyAccess ;
22
+ use Symfony \Component \PropertyAccess \PropertyAccessor ;
21
23
22
24
/**
23
25
* This class is used mostly for inserting "fixed" datas, especially with their primary keys forced on insert.
24
26
* Two methods are mandatory to insert new datas, and you can create them both as indexed array or as objects.
25
27
* Objects are directly persisted to the database, and once they're all, the EntityManager is flushed with all objects.
26
28
* You can override the `getOrder` and `getReferencePrefix` for more flexibility on how to link fixtures together.
29
+ * Other methods can be overriden, notably `flushEveryXIterations` and `searchForMatchingIds`. See their docs to know more.
27
30
*
28
31
* @package Orbitale\Component\DoctrineTools
29
32
*/
30
33
abstract class AbstractFixture extends BaseAbstractFixture implements OrderedFixtureInterface
31
34
{
32
-
33
35
/**
34
36
* @var EntityManager
35
37
*/
@@ -40,28 +42,83 @@ abstract class AbstractFixture extends BaseAbstractFixture implements OrderedFix
40
42
*/
41
43
private $ repo ;
42
44
45
+ /**
46
+ * @var int
47
+ */
48
+ private $ order ;
49
+
50
+ /**
51
+ * @var string
52
+ */
53
+ private $ entityClass ;
54
+
55
+ /**
56
+ * @var PropertyAccessor
57
+ */
58
+ private $ propertyAccessor ;
59
+
60
+ /**
61
+ * @var null|string
62
+ */
63
+ private $ referencePrefix ;
64
+
65
+ /**
66
+ * @var bool
67
+ */
68
+ private $ searchForMatchingIds = false ;
69
+
70
+ /**
71
+ * @var int
72
+ */
73
+ private $ numberOfIteratedObjects = 0 ;
74
+
75
+ /**
76
+ * @var int
77
+ */
78
+ private $ flushEveryXIterations = 0 ;
79
+
80
+ public function __construct ()
81
+ {
82
+ $ this ->order = $ this ->getOrder ();
83
+ $ this ->flushEveryXIterations = $ this ->flushEveryXIterations ();
84
+ $ this ->searchForMatchingIds = $ this ->searchForMatchingIds ();
85
+ $ this ->entityClass = $ this ->getEntityClass ();
86
+ $ this ->referencePrefix = $ this ->getReferencePrefix ();
87
+ $ this ->propertyAccessor = class_exists ('Symfony\Component\PropertyAccess\PropertyAccess ' ) ? PropertyAccess::createPropertyAccessor () : null ;
88
+ }
89
+
43
90
/**
44
91
* {@inheritdoc}
45
92
*/
46
93
public function load (ObjectManager $ manager )
47
94
{
48
95
$ this ->manager = $ manager ;
49
96
97
+ if ($ this ->disableLogger ()) {
98
+ $ this ->manager ->getConnection ()->getConfiguration ()->setSQLLogger (null );
99
+ }
100
+
50
101
$ this ->repo = $ this ->manager ->getRepository ($ this ->getEntityClass ());
51
102
103
+ $ this ->numberOfIteratedObjects = 0 ;
52
104
foreach ($ this ->getObjects () as $ data ) {
53
105
$ this ->fixtureObject ($ data );
106
+ $ this ->numberOfIteratedObjects ++;
54
107
}
108
+ $ this ->numberOfIteratedObjects = 0 ;
55
109
56
- $ this ->manager ->flush ();
110
+ if (!$ this ->flushEveryXIterations ) {
111
+ $ this ->manager ->flush ();
112
+ $ this ->manager ->clear ();
113
+ }
57
114
}
58
115
59
116
/**
60
117
* Creates the object and persist it in database.
61
118
*
62
119
* @param array|object $datas
63
120
*/
64
- protected function fixtureObject ($ datas )
121
+ private function fixtureObject ($ datas )
65
122
{
66
123
// The ID is taken in account to force its use in the database.
67
124
$ id = (is_object ($ datas ) && method_exists ($ datas , 'getId ' ) && $ datas ->getId ())
@@ -72,15 +129,15 @@ protected function fixtureObject($datas)
72
129
$ newObject = false ;
73
130
$ addRef = false ;
74
131
75
- // If the user specifies an ID
76
- if ($ id ) {
77
- // Checks that the object ID exists in database
132
+ // If the user specifies an ID and the fixture class wants it to be merged, we search for an object.
133
+ if ($ id && $ this -> searchForMatchingIds ) {
134
+ // Checks that the object ID exists in database.
78
135
$ obj = $ this ->repo ->find ($ id );
79
136
if ($ obj ) {
80
- // If so, the object is not overwritten
137
+ // If so, the object is not overwritten.
81
138
$ addRef = true ;
82
139
} else {
83
- // Else, we create a new object
140
+ // Else, we create a new object.
84
141
$ newObject = true ;
85
142
}
86
143
} else {
@@ -89,23 +146,21 @@ protected function fixtureObject($datas)
89
146
90
147
if ($ newObject === true ) {
91
148
92
- $ accessor = class_exists ('Symfony\Component\PropertyAccess\PropertyAccess ' ) ? PropertyAccess::createPropertyAccessor () : null ;
93
-
94
- // If the datas are in an array, we instanciate a new object
149
+ // If the datas are in an array, we instanciate a new object.
95
150
if (is_array ($ datas )) {
96
- $ class = $ this ->getEntityClass () ;
151
+ $ class = $ this ->entityClass ;
97
152
$ obj = new $ class ;
98
153
foreach ($ datas as $ field => $ value ) {
99
- if ($ accessor ) {
100
- $ accessor ->setValue ($ obj , $ field , $ value );
154
+ if ($ this -> propertyAccessor ) {
155
+ $ this -> propertyAccessor ->setValue ($ obj , $ field , $ value );
101
156
} else {
102
- // Force the use of a setter if accessor is not available
157
+ // Force the use of a setter if accessor is not available.
103
158
$ obj ->{'set ' .ucfirst ($ field )}($ value );
104
159
}
105
160
}
106
161
}
107
162
108
- // If the ID is set, we tell Doctrine to force the insertion of it
163
+ // If the ID is set, we tell Doctrine to force the insertion of it.
109
164
if ($ id ) {
110
165
/** @var ClassMetadata $metadata */
111
166
$ metadata = $ this ->manager ->getClassMetaData (get_class ($ obj ));
@@ -114,47 +169,94 @@ protected function fixtureObject($datas)
114
169
115
170
// And finally we persist the item
116
171
$ this ->manager ->persist ($ obj );
172
+
173
+ // If we need to flush it, then we do it too.
174
+ if (
175
+ $ this ->flushEveryXIterations
176
+ && $ this ->numberOfIteratedObjects
177
+ && $ this ->numberOfIteratedObjects % $ this ->flushEveryXIterations === 0
178
+ ) {
179
+ $ this ->manager ->flush ();
180
+ $ this ->manager ->clear ();
181
+ }
117
182
$ addRef = true ;
118
183
}
119
184
120
- // If we have to add a reference, we set it
121
- if ($ addRef === true && $ obj && $ this ->getReferencePrefix () ) {
122
- $ this ->addReference ($ this ->getReferencePrefix () .($ id ?: (string ) $ obj ), $ obj );
185
+ // If we have to add a reference, we do it
186
+ if ($ addRef === true && $ obj && $ this ->referencePrefix ) {
187
+ $ this ->addReference ($ this ->referencePrefix .($ id ?: (string ) $ obj ), $ obj );
123
188
}
189
+
190
+ $ obj = null ;
191
+ }
192
+
193
+ /**
194
+ * Get the order of this fixture.
195
+ * Default null means 0, so the fixture will be run at the beginning in order of appearance.
196
+ * Is to be overriden if used.
197
+ *
198
+ * @return int
199
+ */
200
+ public function getOrder () {
201
+ return $ this ->order ;
202
+ }
203
+
204
+ /**
205
+ * If true, the SQL logger will be disabled, and therefore will avoid memory leaks and save memory during execution.
206
+ * Very useful for big batches of entities.
207
+ *
208
+ * @return bool
209
+ */
210
+ protected function disableLogger ()
211
+ {
212
+ return false ;
124
213
}
125
214
126
215
/**
127
216
* Returns the prefix used to create fixtures reference.
128
217
* If returns `null`, no reference will be created for the object.
129
218
* NOTE: To create references of an object, it must have an ID, and if not, implement __toString(), because
130
219
* each object is referenced BEFORE flushing the database.
131
- * Is to be overriden if used .
220
+ * NOTE2: If you specified a "flushEveryXIterations" value, then the object will be provided with an ID every time .
132
221
*
133
222
* @return string|null
134
223
*/
135
224
protected function getReferencePrefix () {
136
- return null ;
225
+ return $ this -> referencePrefix ;
137
226
}
138
227
139
228
/**
140
- * Get the order of this fixture .
141
- * Is to be overriden if used .
229
+ * If specified, the entity manager will be flushed every X times, depending on your specified values .
230
+ * Default is null, so the database is flushed only at the end of all persists .
142
231
*
143
- * @return int
232
+ * @return bool
144
233
*/
145
- public function getOrder () {
146
- return 0 ;
234
+ protected function flushEveryXIterations ()
235
+ {
236
+ return $ this ->flushEveryXIterations ;
237
+ }
238
+
239
+ /**
240
+ * If true and an ID is specified, will execute a $manager->find($id) in the database.
241
+ * By default this var is true.
242
+ * Be careful, if you set it to false you may have "duplicate entry" errors if your database is already populated.
243
+ *
244
+ * @return bool
245
+ */
246
+ protected function searchForMatchingIds ()
247
+ {
248
+ return $ this ->searchForMatchingIds ;
147
249
}
148
250
149
251
/**
150
- * Returns the class of the entity you're managing
252
+ * Returns the class of the entity you're managing.
151
253
*
152
254
* @return string
153
255
*/
154
256
protected abstract function getEntityClass ();
155
257
156
258
/**
157
- * Returns a list of objects to
259
+ * Returns a list of objects to insert in the database.
158
260
*
159
261
* @return ArrayCollection|object[]
160
262
*/
0 commit comments