1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Victoire\Bundle\WidgetBundle\Model; |
4
|
|
|
|
5
|
|
|
use Doctrine\ORM\EntityManager; |
6
|
|
|
use Doctrine\ORM\Mapping\ClassMetadataInfo; |
7
|
|
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; |
8
|
|
|
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; |
9
|
|
|
use Symfony\Component\HttpFoundation\Request; |
10
|
|
|
use Symfony\Component\PropertyAccess\PropertyAccess; |
11
|
|
|
use Victoire\Bundle\BusinessEntityBundle\Entity\BusinessEntity; |
12
|
|
|
use Victoire\Bundle\BusinessEntityBundle\Reader\BusinessEntityCacheReader; |
13
|
|
|
use Victoire\Bundle\BusinessPageBundle\Entity\VirtualBusinessPage; |
14
|
|
|
use Victoire\Bundle\BusinessPageBundle\Transformer\VirtualToBusinessPageTransformer; |
15
|
|
|
use Victoire\Bundle\CoreBundle\Entity\View; |
16
|
|
|
use Victoire\Bundle\FormBundle\Helper\FormErrorHelper; |
17
|
|
|
use Victoire\Bundle\PageBundle\Helper\PageHelper; |
18
|
|
|
use Victoire\Bundle\WidgetBundle\Builder\WidgetFormBuilder; |
19
|
|
|
use Victoire\Bundle\WidgetBundle\Helper\WidgetHelper; |
20
|
|
|
use Victoire\Bundle\WidgetBundle\Renderer\WidgetRenderer; |
21
|
|
|
use Victoire\Bundle\WidgetBundle\Resolver\WidgetContentResolver; |
22
|
|
|
use Victoire\Bundle\WidgetMapBundle\Builder\WidgetMapBuilder; |
23
|
|
|
use Victoire\Bundle\WidgetMapBundle\Entity\WidgetMap; |
24
|
|
|
use Victoire\Bundle\WidgetMapBundle\Helper\WidgetMapHelper; |
25
|
|
|
use Victoire\Bundle\WidgetMapBundle\Manager\WidgetMapManager; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* This manager handles crud operations on a Widget. |
29
|
|
|
*/ |
30
|
|
|
class WidgetManager |
31
|
|
|
{ |
32
|
|
|
protected $widgetFormBuilder; |
33
|
|
|
protected $widgetHelper; |
34
|
|
|
protected $widgetContentResolver; |
35
|
|
|
protected $entityManager; |
36
|
|
|
protected $formErrorHelper; // @victoire_form.error_helper |
37
|
|
|
protected $request; // @request |
38
|
|
|
protected $widgetMapManager; |
39
|
|
|
protected $cacheReader; // @victoire_business_entity.cache_reader |
40
|
|
|
protected $templating; |
41
|
|
|
protected $pageHelper; |
42
|
|
|
protected $slots; // %victoire_core.slots% |
43
|
|
|
protected $virtualToBpTransformer; // %victoire_core.slots% |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* construct. |
47
|
|
|
* |
48
|
|
|
* @param WidgetHelper $widgetHelper |
49
|
|
|
* @param WidgetFormBuilder $widgetFormBuilder |
50
|
|
|
* @param WidgetContentResolver $widgetContentResolver |
51
|
|
|
* @param WidgetRenderer $widgetRenderer |
52
|
|
|
* @param EntityManager $entityManager |
53
|
|
|
* @param FormErrorHelper $formErrorHelper |
54
|
|
|
* @param Request $request |
55
|
|
|
* @param WidgetMapManager $widgetMapManager |
56
|
|
|
* @param WidgetMapBuilder $widgetMapBuilder |
57
|
|
|
* @param BusinessEntityCacheReader $cacheReader |
58
|
|
|
* @param EngineInterface $templating |
59
|
|
|
* @param PageHelper $pageHelper |
60
|
|
|
* @param array $slots |
61
|
|
|
*/ |
62
|
|
|
public function __construct( |
63
|
|
|
WidgetHelper $widgetHelper, |
64
|
|
|
WidgetFormBuilder $widgetFormBuilder, |
65
|
|
|
WidgetContentResolver $widgetContentResolver, |
66
|
|
|
WidgetRenderer $widgetRenderer, |
67
|
|
|
EntityManager $entityManager, |
|
|
|
|
68
|
|
|
FormErrorHelper $formErrorHelper, |
69
|
|
|
Request $request, |
|
|
|
|
70
|
|
|
WidgetMapManager $widgetMapManager, |
71
|
|
|
WidgetMapBuilder $widgetMapBuilder, |
72
|
|
|
BusinessEntityCacheReader $cacheReader, |
73
|
|
|
EngineInterface $templating, |
74
|
|
|
PageHelper $pageHelper, |
75
|
|
|
$slots, |
76
|
|
|
VirtualToBusinessPageTransformer $virtualToBpTransformer |
77
|
|
|
) { |
78
|
|
|
$this->widgetFormBuilder = $widgetFormBuilder; |
79
|
|
|
$this->widgetHelper = $widgetHelper; |
80
|
|
|
$this->widgetContentResolver = $widgetContentResolver; |
81
|
|
|
$this->widgetRenderer = $widgetRenderer; |
|
|
|
|
82
|
|
|
$this->entityManager = $entityManager; |
83
|
|
|
$this->formErrorHelper = $formErrorHelper; |
84
|
|
|
$this->request = $request; |
85
|
|
|
$this->widgetMapManager = $widgetMapManager; |
86
|
|
|
$this->widgetMapBuilder = $widgetMapBuilder; |
|
|
|
|
87
|
|
|
$this->cacheReader = $cacheReader; |
88
|
|
|
$this->templating = $templating; |
89
|
|
|
$this->pageHelper = $pageHelper; |
90
|
|
|
$this->slots = $slots; |
91
|
|
|
$this->virtualToBpTransformer = $virtualToBpTransformer; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* new widget. |
96
|
|
|
* |
97
|
|
|
* @param string $type |
98
|
|
|
* @param string $slot |
99
|
|
|
* @param View $view |
100
|
|
|
* @param int $position |
101
|
|
|
* |
102
|
|
|
* @return array |
103
|
|
|
*/ |
104
|
|
|
public function newWidget($mode, $type, $slot, $view, $position, $parentWidgetMap, $quantum) |
105
|
|
|
{ |
106
|
|
|
$widget = $this->widgetHelper->newWidgetInstance($type, $view, $slot, $mode); |
107
|
|
|
$widgets = ['static' => $widget]; |
108
|
|
|
|
109
|
|
|
/** @var BusinessEntity[] $classes */ |
110
|
|
|
$classes = $this->cacheReader->getBusinessClassesForWidget($widget); |
111
|
|
|
$forms = $this->widgetFormBuilder->renderNewQuantumForms($slot, $view, $widgets, $widget, $classes, $position, $parentWidgetMap, $quantum); |
112
|
|
|
|
113
|
|
|
return [ |
114
|
|
|
'widget' => $widget, |
115
|
|
|
'html' => $this->templating->render( |
116
|
|
|
'VictoireCoreBundle:Widget:Form/new.html.twig', |
117
|
|
|
[ |
118
|
|
|
'id' => time(), |
119
|
|
|
'view' => $view, |
120
|
|
|
'slot' => $slot, |
121
|
|
|
'position' => $position, |
122
|
|
|
'parentWidgetMap' => $parentWidgetMap, |
123
|
|
|
'classes' => $classes, |
124
|
|
|
'widgets' => $widgets, |
125
|
|
|
'widget' => $widget, |
126
|
|
|
'forms' => $forms, |
127
|
|
|
'quantum' => $quantum, |
128
|
|
|
] |
129
|
|
|
), |
130
|
|
|
]; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* Create a widget. |
135
|
|
|
* |
136
|
|
|
* @param string $mode |
137
|
|
|
* @param string $type |
138
|
|
|
* @param string $slotId |
139
|
|
|
* @param View $view |
140
|
|
|
* @param string $entity |
141
|
|
|
* @param string $type |
142
|
|
|
* |
143
|
|
|
* @throws \Exception |
144
|
|
|
* |
145
|
|
|
* @return Template |
146
|
|
|
*/ |
147
|
|
|
public function createWidget($mode, $type, $slotId, View $view, $entity, $position, $widgetReference, $quantum) |
148
|
|
|
{ |
149
|
|
|
//services |
150
|
|
|
$formErrorHelper = $this->formErrorHelper; |
151
|
|
|
$request = $this->request; |
152
|
|
|
|
153
|
|
|
if ($view instanceof VirtualBusinessPage) { |
154
|
|
|
$this->virtualToBpTransformer->transform($view); |
155
|
|
|
} |
156
|
|
|
//create a new widget |
157
|
|
|
$widget = $this->widgetHelper->newWidgetInstance($type, $view, $slotId, $mode); |
158
|
|
|
|
159
|
|
|
$form = $this->widgetFormBuilder->callBuildFormSwitchParameters($widget, $view, $entity, $position, $widgetReference, $slotId, $quantum); |
160
|
|
|
|
161
|
|
|
$noValidate = $request->query->get('novalidate', false); |
162
|
|
|
|
163
|
|
|
$form->handleRequest($request); |
164
|
|
|
if ($noValidate === false && $form->isValid()) { |
165
|
|
|
if (!$view->getId()) { |
166
|
|
|
//create a view for the business entity instance if we are currently on a virtual one |
167
|
|
|
$this->entityManager->persist($view); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
//get the widget from the form |
171
|
|
|
$widget = $form->getData(); |
172
|
|
|
|
173
|
|
|
//update fields of the widget |
174
|
|
|
$widget->setBusinessEntityId($entity); |
175
|
|
|
|
176
|
|
|
//persist the widget |
177
|
|
|
$this->entityManager->persist($widget); |
178
|
|
|
$this->entityManager->flush(); |
179
|
|
|
|
180
|
|
|
$this->widgetMapManager->insert($widget, $view, $slotId, $position, $widgetReference); |
181
|
|
|
|
182
|
|
|
$this->entityManager->persist($view); |
183
|
|
|
$this->entityManager->flush(); |
184
|
|
|
|
185
|
|
|
$widget->setCurrentView($view); |
186
|
|
|
|
187
|
|
|
$this->widgetMapBuilder->build($view); |
188
|
|
|
|
189
|
|
|
//get the html for the widget |
190
|
|
|
$htmlWidget = $this->widgetRenderer->renderContainer($widget, $view); |
191
|
|
|
|
192
|
|
|
$response = [ |
193
|
|
|
'success' => true, |
194
|
|
|
'widgetId' => $widget->getId(), |
195
|
|
|
'html' => $htmlWidget, |
196
|
|
|
]; |
197
|
|
|
} else { |
198
|
|
|
//get the errors as a string |
199
|
|
|
$response = [ |
200
|
|
|
'success' => false, |
201
|
|
|
'message' => $noValidate === false ? $formErrorHelper->getRecursiveReadableErrors($form) : null, |
202
|
|
|
'html' => $this->widgetFormBuilder->renderNewForm($form, $widget, $slotId, $view, $quantum, $entity), |
203
|
|
|
]; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
return $response; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* edit a widget. |
211
|
|
|
* |
212
|
|
|
* @param Request $request |
213
|
|
|
* @param Widget $widget |
214
|
|
|
* @param View $currentView |
215
|
|
|
* @param string $businessEntityId The entity name is used to know which form to submit |
216
|
|
|
* |
217
|
|
|
* @return template |
218
|
|
|
*/ |
219
|
|
|
public function editWidget(Request $request, Widget $widget, View $currentView, $quantum = null, $businessEntityId = null, $widgetMode = Widget::MODE_STATIC) |
220
|
|
|
{ |
221
|
|
|
/** @var BusinessEntity[] $classes */ |
222
|
|
|
$classes = $this->cacheReader->getBusinessClassesForWidget($widget); |
|
|
|
|
223
|
|
|
|
224
|
|
|
//the id of the edited widget |
225
|
|
|
//a new widget might be created in the case of a legacy |
226
|
|
|
$initialWidgetId = $widget->getId(); |
227
|
|
|
|
228
|
|
|
//the type of method used |
229
|
|
|
$requestMethod = $request->getMethod(); |
230
|
|
|
|
231
|
|
|
//if the form is posted |
232
|
|
|
if ($requestMethod === 'POST') { |
233
|
|
|
//the widget view |
234
|
|
|
$widgetView = $widget->getWidgetMap()->getView(); |
235
|
|
|
|
236
|
|
|
//we only copy the widget if the view of the widget is not the current view |
237
|
|
|
if ($widgetView !== $currentView) { |
238
|
|
|
$widget = $this->overwriteWidget($currentView, $widget); |
239
|
|
|
} |
240
|
|
|
if ($businessEntityId !== null) { |
241
|
|
|
$form = $this->widgetFormBuilder->buildForm($widget, $currentView, $businessEntityId, $classes[$businessEntityId]->getClass(), $widgetMode, null, null, null, $quantum); |
|
|
|
|
242
|
|
|
} else { |
243
|
|
|
$form = $this->widgetFormBuilder->buildForm($widget, $currentView, null, null, $widgetMode, null, null, null, $quantum); |
|
|
|
|
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
$noValidate = $request->query->get('novalidate', false); |
247
|
|
|
$form->handleRequest($request); |
248
|
|
|
if ($noValidate === false && $form->isValid()) { |
249
|
|
|
$widget->setBusinessEntityId($businessEntityId); |
250
|
|
|
|
251
|
|
|
//force cache invalidation |
252
|
|
|
$widget->setUpdatedAt(new \DateTime()); |
253
|
|
|
$this->entityManager->persist($widget); |
254
|
|
|
|
255
|
|
|
$this->entityManager->persist($currentView); |
256
|
|
|
$this->entityManager->flush(); |
257
|
|
|
|
258
|
|
|
$response = [ |
259
|
|
|
'view' => $currentView, |
260
|
|
|
'success' => true, |
261
|
|
|
'html' => $this->widgetRenderer->render($widget, $currentView), |
262
|
|
|
'widgetId' => $initialWidgetId, |
263
|
|
|
'slot' => $widget->getWidgetMap()->getSlot(), |
264
|
|
|
'viewCssHash' => $currentView->getCssHash(), |
265
|
|
|
]; |
266
|
|
|
} else { |
267
|
|
|
$formErrorHelper = $this->formErrorHelper; |
268
|
|
|
//Return a message for developer in console and form view in order to refresh view and show form errors |
269
|
|
|
$response = [ |
270
|
|
|
'success' => false, |
271
|
|
|
'widgetId' => $initialWidgetId, |
272
|
|
|
'slot' => $widget->getWidgetMap()->getSlot(), |
273
|
|
|
'message' => $noValidate === false ? $formErrorHelper->getRecursiveReadableErrors($form) : null, |
274
|
|
|
'html' => $this->widgetFormBuilder->renderForm($form, $widget, $businessEntityId), |
|
|
|
|
275
|
|
|
]; |
276
|
|
|
} |
277
|
|
|
} else { |
278
|
|
|
$widgets = $widget->getWidgetMap()->getWidgets(); |
279
|
|
|
$forms = $this->widgetFormBuilder->renderNewQuantumForms($widget->getSlot(), $currentView, $widgets, $widget, $classes); |
280
|
|
|
|
281
|
|
|
$response = [ |
282
|
|
|
'success' => true, |
283
|
|
|
'html' => $this->templating->render( |
284
|
|
|
'VictoireCoreBundle:Widget:Form/edit.html.twig', |
285
|
|
|
[ |
286
|
|
|
'view' => $currentView, |
287
|
|
|
'slot' => $widget->getWidgetMap()->getSlot(), |
288
|
|
|
'position' => $widget->getWidgetMap()->getPosition(), |
289
|
|
|
'parentWidgetMap' => $widget->getWidgetMap()->getParent() ? $widget->getWidgetMap()->getParent()->getId() : null, |
290
|
|
|
'classes' => $classes, |
291
|
|
|
'forms' => $forms, |
292
|
|
|
'widgets' => $widgets, |
293
|
|
|
'widget' => $widget, |
294
|
|
|
] |
295
|
|
|
), |
296
|
|
|
]; |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
return $response; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Remove a widget. |
304
|
|
|
* |
305
|
|
|
* @param Widget $widget |
306
|
|
|
* |
307
|
|
|
* @return array The parameter for the view |
308
|
|
|
*/ |
309
|
|
|
public function deleteWidget(Widget $widget, View $view) |
310
|
|
|
{ |
311
|
|
|
//Used to update view in callback (we do it before delete it else it'll not exists anymore) |
312
|
|
|
$widgetId = $widget->getId(); |
313
|
|
|
//we update the widget map of the view |
314
|
|
|
$this->widgetMapBuilder->build($view); |
315
|
|
|
$widgetMap = WidgetMapHelper::getWidgetMapByWidgetAndView($widget, $view); |
|
|
|
|
316
|
|
|
//the widget is removed only if the current view is the view of the widget |
317
|
|
|
if ($widgetMap->getView() == $view && $widgetMap->getAction() != WidgetMap::ACTION_DELETE) { |
|
|
|
|
318
|
|
|
//we remove the widget |
319
|
|
|
$this->entityManager->remove($widget); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
//we update the view |
323
|
|
|
$this->entityManager->persist($view); |
324
|
|
|
//update the view deleting the widget |
325
|
|
|
$this->widgetMapManager->delete($view, $widget); |
|
|
|
|
326
|
|
|
|
327
|
|
|
$this->entityManager->flush(); |
328
|
|
|
|
329
|
|
|
return [ |
330
|
|
|
'success' => true, |
331
|
|
|
'widgetId' => $widgetId, |
332
|
|
|
'viewCssHash' => $view->getCssHash(), |
333
|
|
|
]; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* Overwrite the widget for the current view because the widget is not linked to the current view, a copy is created. |
338
|
|
|
* |
339
|
|
|
* @param View $view |
340
|
|
|
* @param Widget $widget |
341
|
|
|
* |
342
|
|
|
* @throws \Exception The slot does not exists |
343
|
|
|
* |
344
|
|
|
* @return Widget The widget |
345
|
|
|
*/ |
346
|
|
|
public function overwriteWidget(View $view, Widget $widget) |
347
|
|
|
{ |
348
|
|
|
$widgetCopy = $this->cloneEntity($widget); |
|
|
|
|
349
|
|
|
$originalWidgetMap = $widget->getWidgetMap(); |
350
|
|
|
$this->widgetMapManager->overwrite($view, $originalWidgetMap, $widgetCopy); |
|
|
|
|
351
|
|
|
|
352
|
|
|
return $widgetCopy; |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
/** |
356
|
|
|
* @param Widget $entity |
357
|
|
|
*/ |
358
|
|
|
public function cloneEntity(Widget $entity) |
359
|
|
|
{ |
360
|
|
|
$entityCopy = clone $entity; |
361
|
|
|
$entityCopy->setWidgetMap(null); |
362
|
|
|
//Look for on_to_many relations, if found, duplicate related entities. |
363
|
|
|
//It is necessary for 'list' widgets, this algo duplicates and persists list items. |
364
|
|
|
$associations = $this->entityManager->getClassMetadata(get_class($entityCopy))->getAssociationMappings(); |
365
|
|
|
$accessor = PropertyAccess::createPropertyAccessor(); |
366
|
|
|
foreach ($associations as $name => $values) { |
367
|
|
|
if ($values['type'] === ClassMetadataInfo::ONE_TO_MANY) { |
368
|
|
|
$relatedEntities = $accessor->getValue($entityCopy, $values['fieldName']); |
369
|
|
|
$relatedEntitiesCopies = []; |
370
|
|
|
foreach ($relatedEntities as $relatedEntity) { |
371
|
|
|
$relatedEntityCopy = clone $relatedEntity; |
372
|
|
|
$this->entityManager->persist($relatedEntity); |
373
|
|
|
$relatedEntitiesCopies[] = $relatedEntityCopy; |
374
|
|
|
} |
375
|
|
|
$accessor->setValue($entityCopy, $name, $relatedEntitiesCopies); |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
//Clone OneToOne relation objects |
379
|
|
|
if ($values['type'] === ClassMetadataInfo::ONE_TO_ONE) { |
380
|
|
|
$relatedEntity = $accessor->getValue($entityCopy, $values['fieldName']); |
381
|
|
|
if ($relatedEntity) { |
382
|
|
|
$relatedEntityCopy = clone $relatedEntity; |
383
|
|
|
$this->entityManager->persist($relatedEntity); |
384
|
|
|
$accessor->setValue($entityCopy, $name, $relatedEntityCopy); |
385
|
|
|
} |
386
|
|
|
} |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
$this->entityManager->persist($entityCopy); |
|
|
|
|
390
|
|
|
|
391
|
|
|
return $entityCopy; |
392
|
|
|
} |
393
|
|
|
} |
394
|
|
|
|
The
EntityManager
might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:If that code throws an exception and the
EntityManager
is closed. Any other code which depends on the same instance of theEntityManager
during this request will fail.On the other hand, if you instead inject the
ManagerRegistry
, thegetManager()
method guarantees that you will always get a usable manager instance.