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\Mapping\UniqueConstraint;
7: use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
8: use Symfony\Component\Validator\Constraints as Assert;
9: use TIC\CoreBundle\Util\StringHelper as StringHelper;
10:
11: use TIC\ListBundle\Repository\ListItemRepository as EntityRepo;
12:
13: /**
14: * @ORM\Table(name="tic_list_listitem",
15: * uniqueConstraints={ @UniqueConstraint(name="ref_unique", columns={"list_ref", "ref"}) },
16: * indexes={ @ORM\Index(name="value_search", columns={"list_ref", "value"}) }
17: * )
18: * @UniqueEntity(fields={"list_ref", "ref"}, errorPath="ref")
19: * @ORM\Entity(repositoryClass=EntityRepo::class)
20: */
21: class ListItem extends BaseEntity
22: {
23:
24: /**
25: * @ORM\Column(type="string", length=201)
26: * @ORM\Id
27: * @ORM\GeneratedValue(strategy="CUSTOM")
28: * @ORM\CustomIdGenerator(class="\TIC\ListBundle\Doctrine\ReferencesIdGenerator")
29: */
30: protected $id;
31:
32: /**
33: * @ORM\ManyToOne(targetEntity="ListInfo", inversedBy="items")
34: * @ORM\JoinColumn(name="list_ref", referencedColumnName="ref")
35: * @var \TIC\ListBundle\Entity\ListInfo
36: */
37: protected $info;
38:
39: /**
40: * Référence fixe de l'élément (par défaut: slug du libellé unique dans la liste).
41: * @ORM\Column(type="string", length=100)
42: * @var string
43: */
44: protected $ref;
45:
46: /**
47: * Valeur/clé à associer au libellé (par défaut: index dans la liste).
48: * @ORM\Column(type="string", length=100)
49: * @var string
50: */
51: protected $value;
52:
53: /**
54: * Libellé de l'élément à afficher (celui de la locale par défaut si translatable).
55: * @ORM\Column(type="string", length=255)
56: * @var string
57: */
58: protected $label;
59:
60: /**
61: * @ORM\Column(type="string", length=50, nullable=true)
62: * @var string
63: */
64: protected $icon;
65:
66: /**
67: * @ORM\Column(type="smallint", options={"default"=0})
68: * @Assert\NotNull()
69: * @var integer
70: */
71: protected $ranking;
72:
73: /**
74: * @ORM\Column(type="boolean", options={"default"=true})
75: * @Assert\NotNull()
76: * @var boolean
77: */
78: protected $enabled;
79:
80:
81: // --------------------------------------------------------------------- Custom methods
82:
83: public function __construct(ListInfo $info = null, $label = null, $value = null, $ref = null) {
84: $this->info = $info;
85: $this->label = $label;
86: $this->value = $value;
87: $this->ref = $ref;
88: $this->icon = null;
89: $this->ranking = 0;
90: $this->enabled = true;
91: if ($info) $info->addItem($this);
92: }
93:
94: public function __toString() {
95: return $this->label;
96: }
97:
98:
99: public function export() {
100: return array(
101: 'id' => $this->id,
102: 'info' => $this->info->getRef(),
103: 'ref' => $this->ref,
104: 'value' => $this->value,
105: 'label' => $this->label,
106: 'icon' => $this->icon,
107: 'ranking' => $this->ranking,
108: 'enabled' => $this->enabled,
109: 'labels' => $this->getLabels(),
110: );
111: }
112:
113: public function import($data) {
114: foreach ($data as $field => $value) switch ($field) {
115: case 'id' : $this->id = $value; break;
116: case 'ref' : $this->ref = $value; break;
117: case 'value' : $this->value = $value; break;
118: case 'label' : (is_array($value)) ? $this->setLabels($value) : $this->label = $value; break;
119: case 'icon' : $this->icon = $value; break;
120: case 'ranking' : $this->ranking = $value; break;
121: case 'enabled' : $this->enabled = $value; break;
122: case 'labels' : $this->setLabels($value); break;
123: }
124: }
125: // --------------------------------------------------------------------- Shortcut methods
126:
127: /**
128: * Compatibility
129: */
130: public static function slugify($text) {
131: return StringHelper::slugify($text);
132: }
133:
134: // --------------------------------------------------------------------- Events methods
135:
136: /**
137: * Génération automatique pour les champs vides 'ref' et 'value'.
138: * Note: l'usage d'events prePersist et preUpdate sur les entités ListItem
139: * ne permettrait pas de déterminer l'ordre des éléments traités
140: * (génant pour la génération des incréments et les priorités des slugs)
141: * d'où l'usage d'un event PreFlush au niveau de la liste
142: * ATTENTION: aucun event alors sur une opération d'ajout/édition isolée
143: * d'une entité ListItem (en dehors de l'édition via une liste) ?
144: */
145: public function generateDefaultData() {
146: $nbChanges = 0;
147: if ($this->value === null || ! strlen($this->value)) {
148: $this->value = $this->makeValueAutoIndex();
149: $nbChanges++;
150: }
151: if ($this->ref === null || ! strlen($this->ref) ) {
152: $this->ref = $this->makeRefFromLabel();
153: $nbChanges++;
154: }
155: return $nbChanges;
156: }
157:
158: /**
159: * Génération automatique de la référence du noeud (slug unique du label).
160: */
161: public function makeRefFromLabel() {
162: $ref = $slug = StringHelper::slugify($this->label);
163: // vérification de l'unicité (avec suffixe numéroté si besoin)
164: $values = array(); $suffix = 1;
165: foreach ($this->info->getItems() as $item) {
166: // ATTENTION: pas de comparaison sur l'id qui ne distinguerait pas plusieurs nouveaux éléments
167: if ($item !== $this) $values[] = $item->getRef();
168: }
169: while (in_array($ref, $values)) $ref = $slug . '-' . (++$suffix);
170: return $ref;
171: }
172:
173: /**
174: * Génération automatique de la valeur du noeud (index incrémental dans la liste).
175: */
176: public function makeValueAutoIndex() {
177: // recherche de toutes les valeurs d'index déjà existantes
178: $values = array(0);
179: foreach ($this->info->getItems() as $item) {
180: // ATTENTION: pas de comparaison sur l'id qui ne distinguerait pas plusieurs nouveaux éléments
181: if ($item !== $this) $values[] = intval($item->getValue());
182: }
183: return max($values) + 1;
184: # $value = 1; while (in_array($value, $values)) $value++; return $value;
185: }
186:
187:
188: // --------------------------------------------------------------------- Translations methods
189:
190: /**
191: * Retourne le libellé par défaut d'une liste de traduction.
192: * (utile pour ListItemTranslation[] => ListItem->label)
193: */
194: protected function getDefaultLabel($labels, $default = '') {
195: return $default;
196: }
197:
198: /**
199: * Retourne la liste des libellés traduits (plutôt qu'une liste des traductions)
200: * Note: si aucune traduction une liste sera initialisée avec le libellé par défaut
201: * @return array Liste des libellés indexés par leur locale
202: */
203: public function getLabels() {
204: return array_fill_keys(array("fr"), $this->label);
205: }
206:
207: /**
208: * Enregistre une liste de libellés traduits dans la collection des translations.
209: * Note: mise à jour au passage du libellé par défaut (stocké dans l'entité hors translations)
210: * @param array $labels Liste des libellés indexés par leur locale
211: */
212: public function setLabels($labels) {
213: $this->setLabel( $this->getDefaultLabel($labels) );
214:
215: return $this;
216: }
217:
218:
219: /**
220: * Get label
221: * Note: possibilité de spécifier une locale pour interroger la collection de translations
222: * Note: il est sans doute préférable d'éviter la création d'objet depuis une demande en lecture
223: * @param string $locale La locale demandée pour récupérer les traductions
224: * @return string
225: */
226: public function getLabel($locale = null) {
227: return $this->label;
228: }
229:
230:
231: // --------------------------------------------------------------------- Auto-generated
232:
233: /**
234: * Get id
235: *
236: * @return string
237: */
238: public function getId()
239: {
240: return $this->id;
241: }
242:
243: /**
244: * Set ref
245: *
246: * @param string $ref
247: *
248: * @return ListItem
249: */
250: public function setRef($ref)
251: {
252: $this->ref = $ref;
253:
254: return $this;
255: }
256:
257: /**
258: * Get ref
259: *
260: * @return string
261: */
262: public function getRef()
263: {
264: return $this->ref;
265: }
266:
267: /**
268: * Set value
269: *
270: * @param string $value
271: *
272: * @return ListItem
273: */
274: public function setValue($value)
275: {
276: $this->value = $value;
277:
278: return $this;
279: }
280:
281: /**
282: * Get value
283: *
284: * @return string
285: */
286: public function getValue()
287: {
288: return $this->value;
289: }
290:
291: /**
292: * Set label
293: *
294: * @param string $label
295: *
296: * @return ListItem
297: */
298: public function setLabel($label)
299: {
300: $this->label = $label;
301:
302: return $this;
303: }
304:
305: /**
306: * Set icon
307: *
308: * @param string $icon
309: *
310: * @return ListItem
311: */
312: public function setIcon($icon)
313: {
314: $this->icon = $icon;
315:
316: return $this;
317: }
318:
319: /**
320: * Get icon
321: *
322: * @return string
323: */
324: public function getIcon()
325: {
326: return $this->icon;
327: }
328:
329: /**
330: * Set ranking
331: *
332: * @param integer $ranking
333: *
334: * @return ListItem
335: */
336: public function setRanking($ranking)
337: {
338: $this->ranking = $ranking;
339:
340: return $this;
341: }
342:
343: /**
344: * Get ranking
345: *
346: * @return integer
347: */
348: public function getRanking()
349: {
350: return $this->ranking;
351: }
352:
353: /**
354: * Set enabled
355: *
356: * @param boolean $enabled
357: *
358: * @return ListItem
359: */
360: public function setEnabled($enabled)
361: {
362: $this->enabled = $enabled;
363:
364: return $this;
365: }
366:
367: /**
368: * Get enabled
369: *
370: * @return boolean
371: */
372: public function getEnabled()
373: {
374: return $this->enabled;
375: }
376:
377: /**
378: * Set info
379: *
380: * @param \TIC\ListBundle\Entity\ListInfo $info
381: *
382: * @return ListItem
383: */
384: public function setInfo(\TIC\ListBundle\Entity\ListInfo $info = null)
385: {
386: $this->info = $info;
387:
388: return $this;
389: }
390:
391: /**
392: * Get info
393: *
394: * @return \TIC\ListBundle\Entity\ListInfo
395: */
396: public function getInfo()
397: {
398: return $this->info;
399: }
400:
401: }
402: