1 | <?php |
||
2 | |||
3 | namespace andmemasin\surveybasemodels; |
||
4 | |||
5 | use andmemasin\myabstract\MyActiveRecord; |
||
6 | use PascalDeVink\ShortUuid\ShortUuid; |
||
7 | use Ramsey\Uuid\Uuid; |
||
8 | use yii; |
||
9 | |||
10 | /** |
||
11 | * This is the model class for a generic Respondent. |
||
12 | * |
||
13 | * @property int $respondent_id |
||
14 | * @property int $survey_id |
||
15 | * @property string $token |
||
16 | * @property Survey $survey |
||
17 | * @property string $email_address |
||
18 | * @property string $alternative_email_addresses Inserted as CSV, stored as JSON |
||
19 | * @property string $phone_number |
||
20 | * @property string $alternative_phone_numbers Inserted as CSV, stored as JSON |
||
21 | * |
||
22 | * @property boolean $isRejected |
||
23 | * @property string $shortToken If the token is uuid, then short-uuid will be returned |
||
24 | */ |
||
25 | class Respondent extends MyActiveRecord |
||
26 | { |
||
27 | const MAX_ALTERNATIVE_CONTACTS = 20; |
||
28 | /** @var bool $checkDSNForEmails whether email validation also used DSN records to check if domain exists */ |
||
29 | public static $checkDSNForEmails = true; |
||
30 | |||
31 | /** |
||
32 | * @var array $surveyIdentifyingColumns names of the columns that identify a respondent as unique inside a survey |
||
33 | */ |
||
34 | public static $surveyIdentifyingColumns = ['phone_number','email_address']; |
||
35 | |||
36 | public function init() |
||
37 | { |
||
38 | parent::init(); |
||
39 | } |
||
40 | |||
41 | |||
42 | /** |
||
43 | * {@inheritdoc} |
||
44 | */ |
||
45 | public function rules() |
||
46 | { |
||
47 | return array_merge([ |
||
48 | [['survey_id','token'], 'required'], |
||
49 | [['survey_id'], 'integer'], |
||
50 | [['email_address'], 'validateEmail'], |
||
51 | // email addresses always lowercase |
||
52 | [['email_address','email_address'],'trim'], |
||
53 | ['email_address','filter', 'filter' => 'strtolower'], |
||
54 | [['alternative_email_addresses'], 'string'], |
||
55 | [['alternative_email_addresses'], 'validateMultipleEmails'], |
||
56 | [['phone_number','email_address'],'trim'], |
||
57 | ['phone_number','filter', 'filter' => 'strtolower'], |
||
58 | [['phone_number'],'string'], |
||
59 | [['phone_number'],'validatePhoneNumber'], |
||
60 | [['alternative_phone_numbers'], 'string'], |
||
61 | [['alternative_phone_numbers'],'validateMultiplePhoneNumbers'], |
||
62 | [['token'], 'unique'], |
||
63 | ], parent::rules()); |
||
64 | } |
||
65 | |||
66 | |||
67 | |||
68 | /** |
||
69 | * @param string $attribute |
||
70 | * @param string $address |
||
71 | * @return bool |
||
72 | */ |
||
73 | public function validateEmail($attribute,$address = null){ |
||
74 | |||
75 | if($this->validateEmailFormat($attribute, $address) |
||
76 | && !$this->isSameAsMainAddress($attribute, $address) |
||
77 | && !$this->isEmailSurveyDuplicate($attribute, $address)){ |
||
78 | return true; |
||
79 | } |
||
80 | return false; |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * @param string $attribute |
||
85 | * @param string $address |
||
86 | * @return bool |
||
87 | */ |
||
88 | private function validateEmailFormat($attribute, $address = null) |
||
89 | { |
||
90 | $validator = new yii\validators\EmailValidator(); |
||
91 | $validator->checkDNS = static::$checkDSNForEmails; |
||
92 | if (!$validator->validate($address)) { |
||
93 | $this->addError($attribute, |
||
94 | Yii::t('app', |
||
95 | 'Invalid email address "{0}"',[$address] |
||
96 | ).' '.Yii::t('app','Reason: {0}',[Yii::t('app','Invalid email format')]) |
||
97 | ); |
||
98 | return false; |
||
99 | } |
||
100 | return true; |
||
101 | } |
||
102 | |||
103 | /** |
||
104 | * @param string $attribute |
||
105 | * @param string $address |
||
106 | * @return bool |
||
107 | */ |
||
108 | private function isSameAsMainAddress($attribute, $address = null) |
||
109 | { |
||
110 | if(!$address or empty($address)){ |
||
111 | return false; |
||
112 | } |
||
113 | $isSame = ($attribute=='email_address' ? false : $address == $this->email_address); |
||
114 | if ($isSame) { |
||
115 | $this->addError($attribute, |
||
116 | Yii::t('app', |
||
117 | 'Invalid email address "{0}"',[$address] |
||
118 | ).' '.Yii::t('app','Reason: {0}',[Yii::t('app',$attribute. ' duplicates main address')]) |
||
119 | ); |
||
120 | } |
||
121 | return $isSame; |
||
122 | } |
||
123 | |||
124 | |||
125 | public function validateMultipleEmails($attribute){ |
||
126 | $addresses = yii\helpers\Json::decode($this->alternative_email_addresses); |
||
127 | if($this->alternative_email_addresses && !empty($addresses)){ |
||
128 | $cleanAddresses = []; |
||
129 | if(!empty($addresses)){ |
||
130 | $i=0; |
||
131 | foreach ($addresses as $key => $address){ |
||
132 | if($address <> ""){ |
||
133 | // check the alternative numbers of that model for duplicates |
||
134 | $checkItems = $addresses; |
||
135 | unset($checkItems[$key]); |
||
136 | if(in_array($address,$checkItems)){ |
||
137 | $this->addError($attribute,Yii::t('app','Duplicate email in alternative email addresses')); |
||
138 | } |
||
139 | |||
140 | $i++; |
||
141 | if($i>=static::MAX_ALTERNATIVE_CONTACTS){ |
||
142 | $this->addError($attribute,Yii::t('app','Maximum alternative addresses limit ({0}) reached for {1}',[static::MAX_ALTERNATIVE_CONTACTS,$this->email_address])); |
||
143 | } |
||
144 | $address = strtolower(trim($address)); |
||
145 | if($this->validateEmail($attribute,$address)){ |
||
146 | $cleanAddresses[]=$address; |
||
147 | } |
||
148 | } |
||
149 | |||
150 | } |
||
151 | if(!empty($cleanAddresses)){ |
||
152 | $this->alternative_email_addresses = yii\helpers\Json::encode($cleanAddresses); |
||
153 | } else { |
||
154 | $this->alternative_email_addresses = null; |
||
155 | } |
||
156 | } |
||
157 | } |
||
158 | } |
||
159 | |||
160 | |||
161 | public function validatePhoneNumber($attribute, $phone_number = null){ |
||
162 | $this->validateSameAsMainNumber($attribute, $phone_number); |
||
163 | // TODO |
||
164 | $isValidFormat = true; |
||
165 | $this->validatePhoneSurveyDuplicate($attribute, $phone_number); |
||
166 | } |
||
167 | |||
168 | public function validateMultiplePhoneNumbers($attribute){ |
||
169 | if($this->alternative_phone_numbers){ |
||
170 | $cleanItems = []; |
||
171 | $items = yii\helpers\Json::decode($this->alternative_phone_numbers); |
||
172 | |||
173 | if(!empty($items)){ |
||
174 | $i=0; |
||
175 | foreach ($items as $key=> $item){ |
||
176 | $item = strtolower(trim($item)); |
||
177 | if($item <> ''){ |
||
178 | $i++; |
||
179 | // check the alternative numbers of that model for duplicates |
||
180 | $checkItems = $items; |
||
181 | unset($checkItems[$key]); |
||
182 | if(in_array($item,$checkItems)){ |
||
183 | $this->addError($attribute,Yii::t('app','Duplicate number in alternative phone numbers')); |
||
184 | } |
||
185 | |||
186 | |||
187 | if($i>=static::MAX_ALTERNATIVE_CONTACTS){ |
||
188 | $this->addError($attribute,Yii::t('app','Maximum alternative phone numbers limit ({0}) reached for {1}',[static::MAX_ALTERNATIVE_CONTACTS,$this->phone_number])); |
||
189 | } |
||
190 | if($this->validatePhoneNumber($attribute,$item)){ |
||
0 ignored issues
–
show
|
|||
191 | $cleanItems[]=$item; |
||
192 | } |
||
193 | |||
194 | } |
||
195 | } |
||
196 | if(!empty($cleanItems)){ |
||
197 | $this->alternative_phone_numbers = yii\helpers\Json::encode($cleanItems); |
||
198 | } else { |
||
199 | $this->alternative_phone_numbers = null; |
||
200 | } |
||
201 | } |
||
202 | } |
||
203 | } |
||
204 | |||
205 | |||
206 | /** |
||
207 | * @param string $attribute |
||
208 | * @param string $number |
||
209 | * @return null |
||
210 | */ |
||
211 | private function validateSameAsMainNumber($attribute, $number = null) |
||
212 | { |
||
213 | if (!$number or empty($number)) { |
||
214 | return null; |
||
215 | } |
||
216 | |||
217 | $isSame = ($attribute=='phone_number' ? false : $number == $this->phone_number); |
||
218 | |||
219 | if ($isSame) { |
||
220 | $this->addError($attribute, |
||
221 | Yii::t('app', |
||
222 | 'Invalid phone number "{0}"',[$number] |
||
223 | ).' '.Yii::t('app','Reason: {0}',[Yii::t('app',$attribute. ' duplicates main phone number')]) |
||
224 | ); |
||
225 | } |
||
226 | return null; |
||
227 | } |
||
228 | |||
229 | |||
230 | /** |
||
231 | * @param string $attribute |
||
232 | * @param string $phone_number Phone number to check duplicates for |
||
233 | */ |
||
234 | private function validatePhoneSurveyDuplicate($attribute, $phone_number){ |
||
235 | $query = static::find(); |
||
236 | // check only this survey |
||
237 | $query->andWhere(['survey_id'=>$this->survey_id]); |
||
238 | |||
239 | if($this->respondent_id){ |
||
240 | // not itself |
||
241 | $query->andWhere(['!=','respondent_id',$this->respondent_id]); |
||
242 | } |
||
243 | |||
244 | $condition = ['or', |
||
245 | '`phone_number`=:phone_number', |
||
246 | '`alternative_phone_numbers` LIKE :phone_number2', |
||
247 | ]; |
||
248 | $query->andWhere($condition,[':phone_number'=>$phone_number,':phone_number2'=>'%\"'.$phone_number.'\"%']); |
||
249 | |||
250 | if($query->count() > 0) { |
||
251 | $this->addError($attribute, |
||
252 | Yii::t('app', |
||
253 | 'Invalid phone number "{0}"',[$phone_number] |
||
254 | ).' '.Yii::t('app','Reason: {0}',[Yii::t('app','Duplicate phone number')]) |
||
255 | ); |
||
256 | } |
||
257 | } |
||
258 | |||
259 | |||
260 | /** |
||
261 | * @param string $email_address Email address to check duplicates for |
||
262 | * @return bool |
||
263 | */ |
||
264 | public function isEmailSurveyDuplicate($attribute, $email_address){ |
||
265 | $query = static::find(); |
||
266 | // check only this survey |
||
267 | $query->andWhere(['survey_id'=>$this->survey_id]); |
||
268 | // not itself |
||
269 | if($this->respondent_id){ |
||
270 | // not itself |
||
271 | $query->andWhere(['!=','respondent_id',$this->respondent_id]); |
||
272 | } |
||
273 | |||
274 | $email_condition = ['or', |
||
275 | '`email_address`=:email_address', |
||
276 | '`alternative_email_addresses` LIKE :email_address2', |
||
277 | ]; |
||
278 | $query->andWhere($email_condition,[':email_address'=>$email_address,':email_address2'=>'%\"'.$email_address.'\"%']); |
||
279 | if($query->count() > 0){ |
||
280 | return true; |
||
281 | } |
||
282 | |||
283 | $this->addError($attribute, |
||
284 | Yii::t('app', |
||
285 | 'Invalid email address "{0}"',[$email_address] |
||
286 | ).' '.Yii::t('app','Reason: {0}',[Yii::t('app','Duplicates some other address')]) |
||
287 | ); |
||
288 | |||
289 | return false; |
||
290 | } |
||
291 | |||
292 | /** |
||
293 | * {@inheritdoc} |
||
294 | */ |
||
295 | public function attributeLabels() |
||
296 | { |
||
297 | return [ |
||
298 | 'token' => Yii::t('app', 'Unique Token'), |
||
299 | ]; |
||
300 | } |
||
301 | |||
302 | /** |
||
303 | * @param string $token Respondents token |
||
304 | * @return static |
||
305 | */ |
||
306 | public static function findByToken($token = null){ |
||
307 | if($token){ |
||
308 | $models = self::findByTokens([$token]); |
||
309 | if (!empty($models)){ |
||
310 | return $models[0]; |
||
311 | } |
||
312 | } |
||
313 | return null; |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * @param string[] $tokens Respondents tokens |
||
318 | * @return static[] |
||
319 | */ |
||
320 | public static function findByTokens($tokens){ |
||
321 | /** @var static[] $model */ |
||
322 | $models = static::find() |
||
323 | ->andWhere(['in','token', $tokens]) |
||
324 | ->all(); |
||
325 | return $models; |
||
326 | } |
||
327 | |||
328 | /** |
||
329 | * Check whether respondent has rejected this specific survey or has a hard bounce on e_mail address |
||
330 | * @return bool |
||
331 | */ |
||
332 | public function getIsRejected(){ |
||
333 | if(Rejection::rejectedByCode($this->token)){ |
||
334 | return true; |
||
335 | } |
||
336 | if(Rejection::bouncedByEmailAddress($this->email_address)){ |
||
337 | return true; |
||
338 | } |
||
339 | |||
340 | return false; |
||
341 | } |
||
342 | |||
343 | /** |
||
344 | * Get the last respondent based on Email-address |
||
345 | * @param string $email_address |
||
346 | * @return static |
||
347 | */ |
||
348 | public static function getLatestByEmail($email_address){ |
||
349 | /** @var static $model */ |
||
350 | $model = static::find() |
||
351 | ->andWhere(['email_address'=>$email_address]) |
||
352 | ->orderBy([static::primaryKey()[0]=>SORT_DESC]) |
||
353 | ->one(); |
||
354 | return $model; |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * @return string |
||
359 | */ |
||
360 | public function getShortToken(){ |
||
361 | if(Uuid::isValid($this->token)){ |
||
362 | $uuid = Uuid::fromString($this->token); |
||
363 | $shotUuid = new ShortUuid(); |
||
364 | return $shotUuid->encode($uuid); |
||
365 | } |
||
366 | return $this->token; |
||
367 | } |
||
368 | |||
369 | |||
370 | } |
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.