1: <?php
2: namespace TIC\ListBundle\Entity;
3:
4: use TIC\DormBundle\Base\TICEntity as BaseEntity;
5: use Doctrine\ORM\Mapping as ORM;
6: use Doctrine\ORM\Event\PreFlushEventArgs;
7: use Symfony\Component\Validator\Constraints as Assert;
8:
9: use TIC\DormBundle\Traits\EntityTimestampable;
10: use TIC\DormBundle\Traits\EntityEnabled;
11: use TIC\DormBundle\Traits\EntityBlameable;
12:
13: use TIC\ListBundle\Repository\ListInfoRepository as EntityRepo;
14:
15: /**
16: * Entité représentant une liste administrable (conteneur d'éléments avec capacités).
17: * @ORM\Table(name="tic_list_listinfo")
18: * @ORM\Entity(repositoryClass=EntityRepo::class)
19: * @ORM\HasLifecycleCallbacks()
20: */
21:
22: class ListInfo extends BaseEntity
23: {
24:
25: use EntityTimestampable;
26: use EntityEnabled;
27: use EntityBlameable;
28:
29:
30: /**
31: * @ORM\Column(type="string", length=100, unique=true)
32: * @ORM\Id
33: * @Assert\NotBlank()
34: * @var string
35: */
36: protected $ref;
37:
38: /**
39: * @ORM\Column(type="string", length=250)
40: * @Assert\NotBlank()
41: * @var string
42: */
43: protected $title;
44:
45: /**
46: * @ORM\Column(type="text", nullable=true)
47: * @var string
48: */
49: protected $description;
50:
51: /**
52: * Gestion des élements possible par l'interface web (sinon lecture seule pour sync par import/export) ?
53: * @ORM\Column(type="boolean", options={"default"=true})
54: * @var boolean
55: */
56: protected $editable;
57:
58: /**
59: * Utilisation des champs 'value' des éléments (clé des options, par défaut index auto) ?
60: * @ORM\Column(type="boolean", options={"default"=true})
61: * @var boolean
62: */
63: protected $valuable;
64:
65: /**
66: * Utilisation des champs 'ref' des éléments (fixe pour import/export, par défaut slug du label) ?
67: * @ORM\Column(type="boolean")
68: * @var boolean
69: */
70: protected $referable;
71:
72: /**
73: * Utilisation des champs 'enabled' des éléments (activation/désactivation) ?
74: * @ORM\Column(type="boolean", options={"default"=true})
75: * @var boolean
76: */
77: protected $deactivatable;
78:
79: /**
80: * Utilisation des champs 'ranking' des éléments (tri pondéré modifiable) ?
81: * @ORM\Column(type="boolean")
82: * @var boolean
83: */
84: protected $sortable;
85:
86: /**
87: * Utilisation des champs 'icon' des éléments (référence glyphicon/font-awesome) ?
88: * @ORM\Column(type="boolean")
89: * @var boolean
90: */
91: protected $iconable;
92:
93:
94: /**
95: * @ORM\OneToMany(targetEntity="ListItem", mappedBy="info", cascade={"persist","remove"}, orphanRemoval=true)
96: , indexBy="ref"
97: * @ORM\OrderBy({"ranking" = "ASC", "label" = "ASC"})
98: * @var ArrayCollection
99: */
100: protected $items;
101:
102:
103: // --------------------------------------------------------------------- Custom methods
104:
105: public function __construct($nbItems = 0, $ref = null) {
106: $this->enabled = true;
107: $this->ref = $ref;
108: $this->editable = true;
109: $this->valuable = true;
110: $this->referable = false;
111: $this->deactivatable = true;
112: $this->sortable = false;
113: $this->iconable = false;
114:
115: $this->items = new \Doctrine\Common\Collections\ArrayCollection();
116:
117: // ajout de nouveaux éléments vierges pour commencer à peupler la liste
118: for ($n = 0; $n < $nbItems; $n++) new ListItem($this);
119: }
120:
121: public function __toString() {
122: return $this->title;
123: }
124:
125:
126: public function export($withItems = false) {
127: $data = array(
128: 'ref' => $this->ref,
129: 'title' => $this->title,
130: 'description' => $this->description,
131: 'capacities' => $this->getCapacities(),
132: );
133: if ($withItems) $data['items'] = $this->exportItems();
134: return $data;
135: }
136:
137: public function exportItems() {
138: $fields = array('label');
139: foreach ($this->getCapacities() as $capacity) switch ($capacity) {
140: case 'valuable' : $fields[] = 'value'; break;
141: case 'referable' : $fields[] = 'ref'; break;
142: case 'deactivatable' : $fields[] = 'enabled'; break;
143: case 'sortable' : $fields[] = 'ranking'; break;
144: case 'iconable' : $fields[] = 'icon'; break;
145: }
146: $data = array();
147: foreach ($this->items as $item) {
148: $data[ $item->getId() ] = array_filter($item->export(), function($field) use($fields){ return in_array($field, $fields); }, ARRAY_FILTER_USE_KEY);
149: };
150: return $data;
151: }
152:
153:
154: public function import($data) {
155: foreach ($data as $field => $value) switch ($field) {
156: case 'title' : $this->setTitle( $value ); break;
157: case 'description' : $this->setDescription( $value ); break;
158: case 'capacities' : $this->setCapacities( $value ); break;
159: case 'items' : $this->importItems( $value ); break;
160: }
161: }
162:
163: public function importItems($data) {
164: foreach ($data as $id => $values) {
165: $item = $this->getItemById($id);
166: if (! $item) {
167: if (isset($values['ref'] )) $item = $this->getItemByRef( $values['ref'], true);
168: elseif (isset($values['value'])) $item = $this->getItemByValue( $values['value'], true);
169: elseif (isset($values['label'])) $item = $this->getItemByLabel( $values['label'], true);
170: }
171: if (! $item) $item = new ListItem($this);
172: $item->import($values);
173: }
174: $this->generateDefaultItemsData();
175: }
176:
177:
178: // --------------------------------------------------------------------- Events methods
179:
180: /**
181: * A déclencher manuellement avant d'enregistrer les éléments de la liste.
182: * @TODO pour automatiser le déclenchement, plutôt que d'utiliser les events doctrine
183: * pourquoi pas utiliser le validator du formulaire (Assert sur hasRef et hasValue)
184: */
185: public function generateDefaultItemsData() {
186: $changeItems = array();
187: foreach ($this->items as $item) {
188: // @TODO: possibilité ici d'optimiser les boucles sur les éléments
189: // en récupérant toutes les refs et toutes les values existantes
190: // puis en effectuant les générations d'ici directement
191: if ($item->generateDefaultData()) $changeItems[] = $item;
192: }
193: return $changeItems;
194: }
195:
196:
197: // --------------------------------------------------------------------- Virtual capacities
198:
199: protected $capacities = array('editable', 'valuable', 'referable', 'deactivatable', 'sortable', 'iconable');
200:
201: /**
202: * Retourne les choix d'options sélectionnables dans la propriété virtuelle des capacités
203: */
204: public function getCapacityChoices() {
205: $choices = array();
206: foreach ($this->capacities as $field) {
207: $choices['ticlist.list.label.' . $field] = $field;
208: }
209: return $choices;
210: }
211:
212: /**
213: * Getter de la propriété virtuelle pour regrouper l'édition des capacités dans le formulaire
214: */
215: public function getCapacities() {
216: $capacities = array();
217: foreach ($this->capacities as $field) {
218: if ($this->{$field}) $capacities[] = $field;
219: }
220: return $capacities;
221: }
222:
223: /**
224: * Setter de la propriété virtuelle pour regrouper l'édition des capacités dans le formulaire
225: */
226: public function setCapacities($capacities) {
227: foreach ($this->capacities as $field) {
228: $this->{$field} = in_array($field, $capacities);
229: }
230: return $this;
231: }
232:
233:
234: // --------------------------------------------------------------------- Shortcut methods
235:
236: /**
237: * Get id
238: * @return string
239: */
240: public function getId() {
241: return $this->ref;
242: }
243:
244: /**
245: * Retourne le nombre d'éléments dans la liste (avec filtre d'activation optionnel).
246: * @param bool|null $enabled Filtre sur l'état activé si true, sur l'état désactivé si false
247: * @return integer Nombre d'éléments total ou activés ou désactivés
248: */
249: public function countItems($enabled = null){
250: if ($enabled === null ) return $this->items->count();
251: if ($enabled === true ) return $this->items->filter( function($item){ return $item->getEnabled(); })->count();
252: if ($enabled === false) return $this->items->filter( function($item){ return !$item->getEnabled(); })->count();
253: }
254:
255: /**
256: * Retourne la liste des éléments activés (filtrage sur la collection).
257: */
258: public function getActiveItems(){
259: return $this->items->filter( function($item){ return $item->getEnabled(); });
260: }
261:
262: /**
263: * Get the item by his generated id.
264: * @return ListItem|null
265: */
266: public function getItemById($id) {
267: foreach ($this->items as $item) if ("$id" === $item->getId()) return $item;
268: }
269:
270: /**
271: * Get the item by his internal value.
272: * @return ListItem|null
273: */
274: public function getItemByRef($ref, $createIt = false) {
275: foreach ($this->items as $item) if ("$ref" === $item->getRef()) return $item;
276: if ($createIt) return new ListItem($this, null, null, $ref);
277: }
278:
279: /**
280: * Get the item by his external value.
281: * @return ListItem|null
282: */
283: public function getItemByValue($value, $createIt = false) {
284: foreach ($this->items as $item) if ("$value" === $item->getValue()) return $item;
285: if ($createIt) return new ListItem($this, $value, $value);
286: }
287:
288: /**
289: * Get the item by his default label.
290: * @return ListItem|null
291: */
292: public function getItemByLabel($label, $createIt = false) {
293: foreach ($this->items as $item) if ("$label" === $item->getLabel()) return $item;
294: if ($createIt) return new ListItem($this, $label);
295: }
296:
297:
298: // --------------------------------------------------------------------- Tweaked methods
299:
300: /**
301: * Set ref
302: * Note: format forcé avec transformation automatique des caractères non autorisés
303: * @param string $ref
304: * @return ListInfo
305: */
306: public function setRef($ref)
307: {
308: if ($this->title === null) $this->setTitle($ref);
309: $ref = strtolower(strtr($ref, ' ', '_'));
310: $ref = preg_replace('/[^a-z0-9\-_]/', '', $ref);
311: $this->ref = $ref;
312: return $this;
313: }
314:
315: /**
316: * Set title
317: * Note: si valeur vide, composition d'un titre par défaut à partir de la référence.
318: * @param string $title
319: * @return ListInfo
320: */
321: public function setTitle($title)
322: {
323: if ($title === null || ! strlen($title)) $title = $this->ref;
324: $title = ucfirst(strtr($title, '_', ' '));
325: $this->title = $title;
326: return $this;
327: }
328:
329:
330: /**
331: * Add item
332: * @param \TIC\ListBundle\Entity\ListItem $item
333: * @return ListInfo
334: */
335: public function addItem(\TIC\ListBundle\Entity\ListItem $item) {
336: # if ($this->items->contains($item)) return $this;
337: $item->setInfo($this);
338: $this->items[] = $item;
339: return $this;
340: }
341:
342: /**
343: * Set items
344: * @param \TIC\ListBundle\Entity\ListItem[] $items Liste des éléments (array ou ArrayCollection)
345: * @return ListInfo
346: */
347: public function setItems($items) {
348: $this->updateCollection('items', $items, 'setInfo');
349: return $this;
350: }
351:
352:
353: // --------------------------------------------------------------------- Auto-generated
354:
355: /**
356: * Get ref
357: *
358: * @return string
359: */
360: public function getRef()
361: {
362: return $this->ref;
363: }
364:
365: /**
366: * Get title
367: *
368: * @return string
369: */
370: public function getTitle()
371: {
372: return $this->title;
373: }
374:
375: /**
376: * Set description
377: *
378: * @param string $description
379: *
380: * @return ListInfo
381: */
382: public function setDescription($description)
383: {
384: $this->description = $description;
385:
386: return $this;
387: }
388:
389: /**
390: * Get description
391: *
392: * @return string
393: */
394: public function getDescription()
395: {
396: return $this->description;
397: }
398:
399: /**
400: * Set editable
401: *
402: * @param boolean $editable
403: *
404: * @return ListInfo
405: */
406: public function setEditable($editable)
407: {
408: $this->editable = $editable;
409:
410: return $this;
411: }
412:
413: /**
414: * Get editable
415: *
416: * @return boolean
417: */
418: public function getEditable()
419: {
420: return $this->editable;
421: }
422:
423: /**
424: * Set valuable
425: *
426: * @param boolean $valuable
427: *
428: * @return ListInfo
429: */
430: public function setValuable($valuable)
431: {
432: $this->valuable = $valuable;
433:
434: return $this;
435: }
436:
437: /**
438: * Get valuable
439: *
440: * @return boolean
441: */
442: public function getValuable()
443: {
444: return $this->valuable;
445: }
446:
447: /**
448: * Set referable
449: *
450: * @param boolean $referable
451: *
452: * @return ListInfo
453: */
454: public function setReferable($referable)
455: {
456: $this->referable = $referable;
457:
458: return $this;
459: }
460:
461: /**
462: * Get referable
463: *
464: * @return boolean
465: */
466: public function getReferable()
467: {
468: return $this->referable;
469: }
470:
471: /**
472: * Set deactivatable
473: *
474: * @param boolean $deactivatable
475: *
476: * @return ListInfo
477: */
478: public function setDeactivatable($deactivatable)
479: {
480: $this->deactivatable = $deactivatable;
481:
482: return $this;
483: }
484:
485: /**
486: * Get deactivatable
487: *
488: * @return boolean
489: */
490: public function getDeactivatable()
491: {
492: return $this->deactivatable;
493: }
494:
495: /**
496: * Set sortable
497: *
498: * @param boolean $sortable
499: *
500: * @return ListInfo
501: */
502: public function setSortable($sortable)
503: {
504: $this->sortable = $sortable;
505:
506: return $this;
507: }
508:
509: /**
510: * Get sortable
511: *
512: * @return boolean
513: */
514: public function getSortable()
515: {
516: return $this->sortable;
517: }
518:
519: /**
520: * Set iconable
521: *
522: * @param boolean $iconable
523: *
524: * @return ListInfo
525: */
526: public function setIconable($iconable)
527: {
528: $this->iconable = $iconable;
529:
530: return $this;
531: }
532:
533: /**
534: * Get iconable
535: *
536: * @return boolean
537: */
538: public function getIconable()
539: {
540: return $this->iconable;
541: }
542:
543: /**
544: * Remove item
545: *
546: * @param \TIC\ListBundle\Entity\ListItem $item
547: */
548: public function removeItem(\TIC\ListBundle\Entity\ListItem $item)
549: {
550: $this->items->removeElement($item);
551: }
552:
553: /**
554: * Get items
555: *
556: * @return \Doctrine\Common\Collections\Collection
557: */
558: public function getItems()
559: {
560: return $this->items;
561: }
562: }
563: