Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
12.26% covered (danger)
12.26%
19 / 155
4.40% covered (danger)
4.40%
4 / 91
CRAP
0.00% covered (danger)
0.00%
0 / 1
User
12.26% covered (danger)
12.26%
19 / 155
4.40% covered (danger)
4.40%
4 / 91
7269.32
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUsername
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setUsername
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getOldUsername
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setOldUsername
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPasswordHash
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPasswordHash
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getEmail
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setEmail
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isEmailVerified
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setEmailIsVerified
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getEmailVerificationToken
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setEmailVerificationToken
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFirstName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setFirstName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLastName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setLastName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFullName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getGender
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setGender
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getStreet
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setStreet
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPostalCode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPostalCode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCity
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setCity
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRegion
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setRegion
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCountryCode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setCountryCode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getBirthdate
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setBirthdate
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPhone
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPhone
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPermissions
n/a
0 / 0
n/a
0 / 0
1
 setPermissions
n/a
0 / 0
n/a
0 / 0
1
 getPermissionMap
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
4.03
 setPermissionMap
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 hasPermission
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 addPermission
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 removePermission
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getRoot
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setRoot
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getParentUserId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setParentUserId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMemberType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setMemberType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMemberLastPaid
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setMemberLastPaid
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getWantsPostalMail
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setWantsPostalMail
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPostalTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPostalTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPostalName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPostalName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getJoinedOn
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setJoinedOn
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getJoinedReason
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setJoinedReason
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLeftOn
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setLeftOn
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLeftReason
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setLeftReason
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSolvNumber
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setSolvNumber
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSiCardNumber
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setSiCardNumber
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAhvNumber
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setAhvNumber
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDressSize
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setDressSize
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAvatarImageId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setAvatarImageId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getNotes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setNotes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLastLoginAt
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setLastLoginAt
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRoles
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addRole
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 removeRole
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 __toString
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 pretty
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
 softDelete
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
2
 testOnlyGetField
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getEntityNameForStorage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getEntityIdForStorage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getIdFieldNameForSearch
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getIdForSearch
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTitleForSearch
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCriteriaForFilter
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCriteriaForQuery
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace Olz\Entity\Users;
4
5use Doctrine\Common\Collections\ArrayCollection;
6use Doctrine\Common\Collections\Collection;
7use Doctrine\Common\Collections\Criteria;
8use Doctrine\Common\Collections\Expr\Expression;
9use Doctrine\ORM\Mapping as ORM;
10use Olz\Entity\Common\DataStorageInterface;
11use Olz\Entity\Common\DataStorageTrait;
12use Olz\Entity\Common\OlzEntity;
13use Olz\Entity\Common\SearchableInterface;
14use Olz\Entity\Roles\Role;
15use Olz\Repository\Users\UserRepository;
16
17#[ORM\Table(name: 'users')]
18#[ORM\Index(name: 'username_index', columns: ['username'])]
19#[ORM\Entity(repositoryClass: UserRepository::class)]
20class User extends OlzEntity implements DataStorageInterface, SearchableInterface {
21    use DataStorageTrait;
22
23    #[ORM\Id]
24    #[ORM\Column(type: 'integer', nullable: false)]
25    #[ORM\GeneratedValue]
26    public int $id;
27
28    #[ORM\Column(type: 'integer', nullable: true)]
29    public ?int $parent_user;
30
31    #[ORM\Column(type: 'string', nullable: false)]
32    public string $username;
33
34    #[ORM\Column(type: 'string', nullable: true)]
35    public ?string $old_username;
36
37    #[ORM\Column(type: 'text', nullable: true)]
38    public ?string $password;
39
40    #[ORM\Column(type: 'text', nullable: true)]
41    public ?string $email;
42
43    #[ORM\Column(type: 'boolean', nullable: false)]
44    public bool $email_is_verified;
45
46    #[ORM\Column(type: 'text', nullable: true)]
47    public ?string $email_verification_token;
48
49    #[ORM\Column(type: 'text', nullable: false)]
50    public string $first_name;
51
52    #[ORM\Column(type: 'text', nullable: false)]
53    public string $last_name;
54
55    #[ORM\Column(type: 'string', length: 2, nullable: true, options: ['comment' => 'M(ale), F(emale), or O(ther)'])]
56    public ?string $gender;
57
58    #[ORM\Column(type: 'text', nullable: true)]
59    public ?string $street;
60
61    #[ORM\Column(type: 'text', nullable: true)]
62    public ?string $postal_code;
63
64    #[ORM\Column(type: 'text', nullable: true)]
65    public ?string $city;
66
67    #[ORM\Column(type: 'text', nullable: true)]
68    public ?string $region;
69
70    #[ORM\Column(type: 'string', length: 3, nullable: true, options: ['comment' => 'two-letter code (ISO-3166-alpha-2)'])]
71    public ?string $country_code;
72
73    #[ORM\Column(type: 'date', nullable: true)]
74    public ?\DateTime $birthdate;
75
76    #[ORM\Column(type: 'text', nullable: true)]
77    public ?string $phone;
78
79    #[ORM\Column(type: 'text', nullable: true)]
80    public ?string $avatar_image_id;
81
82    #[ORM\Column(type: 'text', nullable: false)]
83    public string $permissions;
84
85    #[ORM\Column(type: 'text', nullable: true)]
86    public ?string $root;
87
88    #[ORM\Column(type: 'string', length: 3, nullable: true, options: ['comment' => 'Aktiv, Ehrenmitglied, Verein, Sponsor'])]
89    public ?string $member_type;
90
91    #[ORM\Column(type: 'date', nullable: true)]
92    public ?\DateTime $member_last_paid;
93
94    #[ORM\Column(type: 'boolean', nullable: false, options: ['default' => 0])]
95    public bool $wants_postal_mail;
96
97    #[ORM\Column(type: 'text', nullable: true, options: ['comment' => 'if not {m: Herr, f: Frau, o: }'])]
98    public ?string $postal_title;
99
100    #[ORM\Column(type: 'text', nullable: true, options: ['comment' => "if not 'First Last'"])]
101    public ?string $postal_name;
102
103    #[ORM\Column(type: 'date', nullable: true)]
104    public ?\DateTime $joined_on;
105
106    #[ORM\Column(type: 'text', nullable: true)]
107    public ?string $joined_reason;
108
109    #[ORM\Column(type: 'date', nullable: true)]
110    public ?\DateTime $left_on;
111
112    #[ORM\Column(type: 'text', nullable: true)]
113    public ?string $left_reason;
114
115    #[ORM\Column(type: 'text', nullable: true)]
116    public ?string $solv_number;
117
118    #[ORM\Column(type: 'text', nullable: true)]
119    public ?string $si_card_number;
120
121    #[ORM\Column(type: 'string', length: 17, nullable: true)]
122    public ?string $ahv_number;
123
124    #[ORM\Column(type: 'string', length: 4, nullable: true, options: ['comment' => '3XS, XXS, XS, S, M, L, XL, XXL, 3XL'])]
125    public ?string $dress_size;
126
127    #[ORM\Column(type: 'text', nullable: false, options: ['default' => ''])]
128    public string $notes;
129
130    /** @var Collection<int|string, Role>&iterable<Role> */
131    #[ORM\JoinTable(name: 'users_roles')]
132    #[ORM\ManyToMany(targetEntity: Role::class, inversedBy: 'users')]
133    private Collection $roles;
134
135    #[ORM\Column(type: 'datetime', nullable: true)]
136    private ?\DateTime $last_login_at;
137
138    public function __construct() {
139        $this->roles = new ArrayCollection();
140    }
141
142    public function getId(): ?int {
143        return $this->id ?? null;
144    }
145
146    public function setId(int $new_value): void {
147        $this->id = $new_value;
148    }
149
150    public function getUsername(): string {
151        return $this->username;
152    }
153
154    public function setUsername(string $new_value): void {
155        $this->username = $new_value;
156    }
157
158    public function getOldUsername(): ?string {
159        return $this->old_username;
160    }
161
162    public function setOldUsername(?string $new_value): void {
163        $this->old_username = $new_value;
164    }
165
166    public function getPasswordHash(): ?string {
167        return $this->password;
168    }
169
170    public function setPasswordHash(?string $new_value): void {
171        $this->password = $new_value;
172    }
173
174    public function getEmail(): ?string {
175        return $this->email;
176    }
177
178    public function setEmail(?string $new_value): void {
179        $this->email = $new_value;
180    }
181
182    public function isEmailVerified(): bool {
183        return $this->email_is_verified;
184    }
185
186    public function setEmailIsVerified(bool $new_value): void {
187        $this->email_is_verified = $new_value;
188    }
189
190    public function getEmailVerificationToken(): ?string {
191        return $this->email_verification_token;
192    }
193
194    public function setEmailVerificationToken(?string $new_value): void {
195        $this->email_verification_token = $new_value;
196    }
197
198    public function getFirstName(): string {
199        return $this->first_name;
200    }
201
202    public function setFirstName(string $new_value): void {
203        $this->first_name = $new_value;
204    }
205
206    public function getLastName(): string {
207        return $this->last_name;
208    }
209
210    public function setLastName(string $new_value): void {
211        $this->last_name = $new_value;
212    }
213
214    public function getFullName(): string {
215        return "{$this->getFirstName()} {$this->getLastName()}";
216    }
217
218    public function getGender(): ?string {
219        return $this->gender;
220    }
221
222    public function setGender(?string $new_value): void {
223        $this->gender = $new_value;
224    }
225
226    public function getStreet(): ?string {
227        return $this->street;
228    }
229
230    public function setStreet(?string $new_value): void {
231        $this->street = $new_value;
232    }
233
234    public function getPostalCode(): ?string {
235        return $this->postal_code;
236    }
237
238    public function setPostalCode(?string $new_value): void {
239        $this->postal_code = $new_value;
240    }
241
242    public function getCity(): ?string {
243        return $this->city;
244    }
245
246    public function setCity(?string $new_value): void {
247        $this->city = $new_value;
248    }
249
250    public function getRegion(): ?string {
251        return $this->region;
252    }
253
254    public function setRegion(?string $new_value): void {
255        $this->region = $new_value;
256    }
257
258    public function getCountryCode(): ?string {
259        return $this->country_code;
260    }
261
262    public function setCountryCode(?string $new_value): void {
263        $this->country_code = $new_value;
264    }
265
266    public function getBirthdate(): ?\DateTime {
267        return $this->birthdate;
268    }
269
270    public function setBirthdate(?\DateTime $new_value): void {
271        $this->birthdate = $new_value;
272    }
273
274    public function getPhone(): ?string {
275        return $this->phone;
276    }
277
278    public function setPhone(?string $new_value): void {
279        $this->phone = $new_value;
280    }
281
282    /** @deprecated Use `getPermissionMap` instead. */
283    public function getPermissions(): string {
284        return $this->permissions;
285    }
286
287    /** @deprecated Use `setPermissionMap` instead. */
288    public function setPermissions(string $new_value): void {
289        $this->permissions = $new_value;
290    }
291
292    /** @return array<string, true> */
293    public function getPermissionMap(): array {
294        $permission_list = preg_split('/[ ]+/', $this->permissions ?? '');
295        if (!is_array($permission_list)) {
296            return [];
297        }
298        $permission_map = [];
299        foreach ($permission_list as $permission) {
300            if (strlen($permission) > 0) {
301                $permission_map[$permission] = true;
302            }
303        }
304        return $permission_map;
305    }
306
307    /** @param array<string, bool> $new_value */
308    public function setPermissionMap(array $new_value): void {
309        $permission_list = [];
310        foreach ($new_value as $key => $value) {
311            if ($value) {
312                $permission_list[] = $key;
313            }
314        }
315        $this->permissions = ' '.implode(' ', $permission_list).' ';
316    }
317
318    public function hasPermission(string $has_permission): bool {
319        $permission_map = $this->getPermissionMap();
320        return $permission_map[$has_permission] ?? false;
321    }
322
323    public function addPermission(string $add_permission): void {
324        $permission_map = $this->getPermissionMap();
325        $permission_map[$add_permission] = true;
326        $this->setPermissionMap($permission_map);
327    }
328
329    public function removePermission(string $remove_permission): void {
330        $permission_map = $this->getPermissionMap();
331        $permission_map[$remove_permission] = false;
332        $this->setPermissionMap($permission_map);
333    }
334
335    public function getRoot(): ?string {
336        return $this->root;
337    }
338
339    public function setRoot(?string $new_value): void {
340        $this->root = $new_value;
341    }
342
343    public function getParentUserId(): ?int {
344        return $this->parent_user;
345    }
346
347    public function setParentUserId(?int $new_value): void {
348        $this->parent_user = $new_value;
349    }
350
351    public function getMemberType(): ?string {
352        return $this->member_type;
353    }
354
355    public function setMemberType(?string $new_value): void {
356        $this->member_type = $new_value;
357    }
358
359    public function getMemberLastPaid(): ?\DateTime {
360        return $this->member_last_paid;
361    }
362
363    public function setMemberLastPaid(?\DateTime $new_value): void {
364        $this->member_last_paid = $new_value;
365    }
366
367    public function getWantsPostalMail(): bool {
368        return $this->wants_postal_mail;
369    }
370
371    public function setWantsPostalMail(bool $new_value): void {
372        $this->wants_postal_mail = $new_value;
373    }
374
375    public function getPostalTitle(): ?string {
376        return $this->postal_title;
377    }
378
379    public function setPostalTitle(?string $new_value): void {
380        $this->postal_title = $new_value;
381    }
382
383    public function getPostalName(): ?string {
384        return $this->postal_name;
385    }
386
387    public function setPostalName(?string $new_value): void {
388        $this->postal_name = $new_value;
389    }
390
391    public function getJoinedOn(): ?\DateTime {
392        return $this->joined_on;
393    }
394
395    public function setJoinedOn(?\DateTime $new_value): void {
396        $this->joined_on = $new_value;
397    }
398
399    public function getJoinedReason(): ?string {
400        return $this->joined_reason;
401    }
402
403    public function setJoinedReason(?string $new_value): void {
404        $this->joined_reason = $new_value;
405    }
406
407    public function getLeftOn(): ?\DateTime {
408        return $this->left_on;
409    }
410
411    public function setLeftOn(?\DateTime $new_value): void {
412        $this->left_on = $new_value;
413    }
414
415    public function getLeftReason(): ?string {
416        return $this->left_reason;
417    }
418
419    public function setLeftReason(?string $new_value): void {
420        $this->left_reason = $new_value;
421    }
422
423    public function getSolvNumber(): ?string {
424        return $this->solv_number;
425    }
426
427    public function setSolvNumber(?string $new_value): void {
428        $this->solv_number = $new_value;
429    }
430
431    public function getSiCardNumber(): ?string {
432        return $this->si_card_number;
433    }
434
435    public function setSiCardNumber(?string $new_value): void {
436        $this->si_card_number = $new_value;
437    }
438
439    public function getAhvNumber(): ?string {
440        return $this->ahv_number;
441    }
442
443    public function setAhvNumber(?string $new_value): void {
444        $this->ahv_number = $new_value;
445    }
446
447    public function getDressSize(): ?string {
448        return $this->dress_size;
449    }
450
451    public function setDressSize(?string $new_value): void {
452        $this->dress_size = $new_value;
453    }
454
455    public function getAvatarImageId(): ?string {
456        return $this->avatar_image_id;
457    }
458
459    public function setAvatarImageId(?string $new_value): void {
460        $this->avatar_image_id = $new_value;
461    }
462
463    public function getNotes(): string {
464        return $this->notes;
465    }
466
467    public function setNotes(string $new_value): void {
468        $this->notes = $new_value;
469    }
470
471    public function getLastLoginAt(): ?\DateTime {
472        return $this->last_login_at;
473    }
474
475    public function setLastLoginAt(?\DateTime $new_value): void {
476        $this->last_login_at = $new_value;
477    }
478
479    /** @return Collection<int|string, Role>&iterable<Role> */
480    public function getRoles(): Collection {
481        return $this->roles;
482    }
483
484    public function addRole(Role $role): void {
485        if (!$this->roles->contains($role)) {
486            $this->roles->add($role);
487        }
488    }
489
490    public function removeRole(Role $role): void {
491        $this->roles->removeElement($role);
492    }
493
494    // ---
495
496    public function __toString(): string {
497        $username = $this->getUsername();
498        $id = $this->getId();
499        return "{$username} (User ID: {$id})";
500    }
501
502    public function pretty(): string {
503        $is_verified = $this->isEmailVerified() ? '✅ verified' : '❌ not verified';
504        $has_permission = $this->hasPermission('verified_email') ? '✅ permission' : '❌ no permission';
505        $roles = implode(', ', array_map(
506            fn ($role): string => $role->getUsername(),
507            [...$this->roles],
508        ));
509        $has_password = $this->getPasswordHash() ? '✅ password' : '❌ no password';
510        $is_child = $this->getParentUserId() ? "🚸 child of {$this->getParentUserId()}" : '✅ parent';
511        return <<<ZZZZZZZZZZ
512            Name: {$this->getFullName()}
513            Username: {$this->getUsername()} (old: {$this->getOldUsername()})
514            E-Mail: {$this->getEmail()} ({$is_verified} / {$has_permission})
515            Password: {$has_password} / {$is_child}
516            Permissions: {$this->getPermissions()}
517            Roles ({$this->roles->count()}): {$roles}
518            ZZZZZZZZZZ;
519    }
520
521    public function softDelete(): void {
522        $this->entityUtils()->updateOlzEntity($this, ['onOff' => false]);
523        $this->setEmail('');
524        $this->setPasswordHash('');
525        $this->setPhone('');
526        $this->setGender(null);
527        $this->setBirthdate(null);
528        $this->setStreet(null);
529        $this->setPostalCode(null);
530        $this->setCity(null);
531        $this->setRegion(null);
532        $this->setCountryCode(null);
533        $this->setPermissions('');
534        $this->setRoot(null);
535        $this->setMemberType(null);
536        $this->setMemberLastPaid(null);
537        $this->setWantsPostalMail(false);
538        $this->setPostalTitle(null);
539        $this->setPostalName(null);
540        $this->setJoinedOn(null);
541        $this->setJoinedReason(null);
542        $this->setLeftOn(null);
543        $this->setLeftReason(null);
544        $this->setSolvNumber(null);
545        $this->setSiCardNumber(null);
546        $this->setNotes('');
547        $this->roles->clear();
548    }
549
550    // ---
551
552    public function testOnlyGetField(string $field_name): mixed {
553        return $this->{$field_name};
554    }
555
556    public static function getEntityNameForStorage(): string {
557        return 'users';
558    }
559
560    public function getEntityIdForStorage(): string {
561        return "{$this->getId()}";
562    }
563
564    public static function getIdFieldNameForSearch(): string {
565        return 'id';
566    }
567
568    public function getIdForSearch(): int {
569        return $this->getId() ?? 0;
570    }
571
572    public function getTitleForSearch(): string {
573        return $this->getFullName();
574    }
575
576    public static function getCriteriaForFilter(string $key, string $value): Expression {
577        throw new \Exception("No such User filter: {$key}");
578    }
579
580    public static function getCriteriaForQuery(string $query): Expression {
581        return Criteria::expr()->orX(
582            Criteria::expr()->contains('first_name', $query),
583            Criteria::expr()->contains('last_name', $query),
584            Criteria::expr()->contains('username', $query),
585            Criteria::expr()->contains('email', $query),
586        );
587    }
588}