Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
SlideTag
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 10
272
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 fromRow
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
30
 getId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSlideId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTag
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCreatedAt
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setTag
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 setSlideId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getNormalizedTag
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 toArray
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace BuyerKiosk\DigitalSign\Models;
4
5/**
6 * SlideTag Model
7 *
8 * Represents a tag associated with a digital signage slide.
9 * Tags are used for categorization and filtering in the event management system.
10 *
11 * Schema (per SDD lines 626-655):
12 * - id: INT UNSIGNED AUTO_INCREMENT PRIMARY KEY
13 * - slideId: INT UNSIGNED NOT NULL (FK to respective slides table)
14 * - tag: VARCHAR(50) NOT NULL
15 * - created_at: TIMESTAMP DEFAULT CURRENT_TIMESTAMP
16 *
17 * @package BuyerKiosk\DigitalSign\Models
18 */
19class SlideTag
20{
21    /**
22     * Maximum allowed length for tag text
23     */
24    private const MAX_TAG_LENGTH = 50;
25
26    /**
27     * @var int|null Primary key ID
28     */
29    private ?int $id = null;
30
31    /**
32     * @var int|null ID of the associated slide
33     */
34    private ?int $slideId = null;
35
36    /**
37     * @var string|null Tag text (original case preserved)
38     */
39    private ?string $tag = null;
40
41    /**
42     * @var string|null Creation timestamp
43     */
44    private ?string $createdAt = null;
45
46    /**
47     * Create a new SlideTag instance
48     */
49    public function __construct()
50    {
51        // Properties initialized to null via class property defaults
52    }
53
54    /**
55     * Create a SlideTag from a database row
56     *
57     * @param array $row Database row with keys: id, slideId, tag, created_at
58     * @return SlideTag
59     */
60    public static function fromRow(array $row): SlideTag
61    {
62        $slideTag = new self();
63
64        if (isset($row['id'])) {
65            $slideTag->id = (int) $row['id'];
66        }
67
68        if (isset($row['slideId'])) {
69            $slideTag->slideId = (int) $row['slideId'];
70        }
71
72        if (isset($row['tag'])) {
73            // Direct assignment without validation since this comes from DB
74            $slideTag->tag = $row['tag'];
75        }
76
77        if (isset($row['created_at'])) {
78            $slideTag->createdAt = $row['created_at'];
79        }
80
81        return $slideTag;
82    }
83
84    /**
85     * Get the tag ID
86     *
87     * @return int|null
88     */
89    public function getId(): ?int
90    {
91        return $this->id;
92    }
93
94    /**
95     * Get the associated slide ID
96     *
97     * @return int|null
98     */
99    public function getSlideId(): ?int
100    {
101        return $this->slideId;
102    }
103
104    /**
105     * Get the tag text (original case preserved)
106     *
107     * @return string|null
108     */
109    public function getTag(): ?string
110    {
111        return $this->tag;
112    }
113
114    /**
115     * Get the creation timestamp
116     *
117     * @return string|null
118     */
119    public function getCreatedAt(): ?string
120    {
121        return $this->createdAt;
122    }
123
124    /**
125     * Set the tag text with validation
126     *
127     * Validation rules:
128     * - Tag is trimmed before validation and storage
129     * - Tag cannot be empty or whitespace-only
130     * - Tag cannot exceed 50 characters
131     *
132     * @param string $tag The tag text to set
133     * @throws \InvalidArgumentException If tag is empty or exceeds 50 characters
134     */
135    public function setTag(string $tag): void
136    {
137        // Trim whitespace before validation
138        $trimmedTag = trim($tag);
139
140        // Validate: cannot be empty or whitespace-only
141        if ($trimmedTag === '') {
142            throw new \InvalidArgumentException('Tag cannot be empty');
143        }
144
145        // Validate: cannot exceed maximum length
146        if (strlen($trimmedTag) > self::MAX_TAG_LENGTH) {
147            throw new \InvalidArgumentException('Tag cannot exceed 50 characters');
148        }
149
150        $this->tag = $trimmedTag;
151    }
152
153    /**
154     * Set the slide ID
155     *
156     * @param int $slideId The slide ID to associate with this tag
157     */
158    public function setSlideId(int $slideId): void
159    {
160        $this->slideId = $slideId;
161    }
162
163    /**
164     * Get the normalized (lowercase) version of the tag for comparison
165     *
166     * Tags are stored with original case preserved, but comparisons
167     * for uniqueness should be case-insensitive.
168     *
169     * @return string Lowercase version of the tag
170     */
171    public function getNormalizedTag(): string
172    {
173        return strtolower($this->tag ?? '');
174    }
175
176    /**
177     * Serialize the tag for API responses
178     *
179     * @return array{id: int|null, slideId: int|null, tag: string|null, createdAt: string|null}
180     */
181    public function toArray(): array
182    {
183        return [
184            'id' => $this->id,
185            'slideId' => $this->slideId,
186            'tag' => $this->tag,
187            'createdAt' => $this->createdAt,
188        ];
189    }
190}