Completed
Pull Request — master (#650)
by Jeroen De
19:07 queued 01:42
created

AddDonationValidator::addViolations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace WMDE\Fundraising\Frontend\DonationContext\UseCases\AddDonation;
6
7
use WMDE\Fundraising\Frontend\DonationContext\UseCases\AddDonation\AddDonationValidationResult as Result;
8
use WMDE\Fundraising\Frontend\PaymentContext\Domain\Model\PaymentType;
9
use WMDE\Fundraising\Frontend\Validation\PaymentDataValidator;
10
use WMDE\Fundraising\Frontend\Validation\BankDataValidator;
11
use WMDE\Fundraising\Frontend\Validation\ConstraintViolation;
12
use WMDE\Fundraising\Frontend\Validation\EmailValidator;
13
14
/**
15
 * @license GNU GPL v2+
16
 * @author Gabriel Birke < [email protected] >
17
 */
18
class AddDonationValidator {
19
	private $paymentDataValidator;
20
	private $bankDataValidator;
21
	private $emailValidator;
22
23
	/**
24
	 * @var AddDonationRequest
25
	 */
26
	private $request;
27
28
	/**
29
	 * @var ConstraintViolation[]
30
	 */
31
	private $violations;
32
33
	private $maximumFieldLengths = [
34
		Result::SOURCE_DONOR_EMAIL => 250,
35
		Result::SOURCE_DONOR_COMPANY => 100,
36
		Result::SOURCE_DONOR_FIRST_NAME => 50,
37
		Result::SOURCE_DONOR_LAST_NAME => 50,
38
		Result::SOURCE_DONOR_SALUTATION => 16,
39
		Result::SOURCE_DONOR_TITLE => 16,
40
		Result::SOURCE_DONOR_STREET_ADDRESS => 100,
41
		Result::SOURCE_DONOR_POSTAL_CODE => 8,
42
		Result::SOURCE_DONOR_CITY => 100,
43
		Result::SOURCE_DONOR_COUNTRY => 8,
44
		Result::SOURCE_BANK_NAME => 50,
45
		Result::SOURCE_BIC => 32,
46
		Result::SOURCE_TRACKING_SOURCE => 250
47
	];
48
49
	public function __construct( PaymentDataValidator $paymentDataValidator, BankDataValidator $bankDataValidator,
50
								 EmailValidator $emailValidator ) {
51
52
		$this->paymentDataValidator = $paymentDataValidator;
53
		$this->bankDataValidator = $bankDataValidator;
54
		$this->emailValidator = $emailValidator;
55
	}
56
57
	public function validate( AddDonationRequest $addDonationRequest ): Result {
58
		$this->request = $addDonationRequest;
59
		$this->violations = [];
60
61
		$this->validateAmount();
62
		$this->validatePayment();
63
		$this->validateBankData();
64
		$this->validateDonorName();
65
		$this->validateDonorEmail();
66
		$this->validateDonorAddress();
67
		$this->validateTrackingData();
68
69
		return new Result( ...$this->violations );
70
	}
71
72
	private function validateAmount() {
73
		// TODO validate without euro class, put conversion in PaymentDataValidator
74
		$result = $this->paymentDataValidator->validate(
75
			$this->request->getAmount()->getEuroFloat(),
76
			$this->request->getPaymentType()
77
		);
78
79
		$violations = array_map( function( ConstraintViolation $violation ) {
80
			$violation->setSource( Result::SOURCE_PAYMENT_AMOUNT );
81
			return $violation;
82
		}, $result->getViolations() );
83
		$this->addViolations( $violations );
84
	}
85
86
	private function addViolations( array $violations ) {
87
		$this->violations = array_merge( $this->violations, $violations );
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($this->violations, $violations) of type array is incompatible with the declared type array<integer,object<WMD...n\ConstraintViolation>> of property $violations.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
88
	}
89
90
	private function validateBankData() {
91
		if ( $this->request->getPaymentType() !== PaymentType::DIRECT_DEBIT ) {
92
			return;
93
		}
94
95
		$bankData = $this->request->getBankData();
96
		$validationResult = $this->bankDataValidator->validate( $bankData );
0 ignored issues
show
Bug introduced by
It seems like $bankData defined by $this->request->getBankData() on line 95 can be null; however, WMDE\Fundraising\Fronten...taValidator::validate() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
97
98
		$this->addViolations( $validationResult->getViolations() );
99
		$this->validateFieldLength( $bankData->getBankName(), Result::SOURCE_BANK_NAME );
100
		$this->validateFieldLength( $bankData->getBic(), Result::SOURCE_BIC );
101
	}
102
103
	private function validateDonorEmail() {
104
		if ( $this->request->donorIsAnonymous() ) {
105
			return;
106
		}
107
		if ( $this->emailValidator->validate( $this->request->getDonorEmailAddress() )->hasViolations() ) {
108
			$this->addViolations( [ new ConstraintViolation(
109
				$this->request->getDonorEmailAddress(),
110
				Result::VIOLATION_MISSING,
111
				Result::SOURCE_DONOR_EMAIL
112
			) ] );
113
		} else {
114
			$this->validateFieldLength( $this->request->getDonorEmailAddress(), Result::SOURCE_DONOR_EMAIL );
115
		}
116
	}
117
118
	private function validateDonorName() {
119
		if ( $this->request->donorIsAnonymous() ) {
120
			return;
121
		}
122
		if ( $this->request->donorIsCompany() ) {
123
			$this->validateCompanyName();
124
		}
125
		else {
126
			$this->validatePersonName();
127
		}
128
	}
129
130
	private function validateCompanyName() {
131
		if ( $this->request->getDonorCompany() === '' ) {
132
			$this->violations[] = new ConstraintViolation(
133
				$this->request->getDonorCompany(),
134
				Result::VIOLATION_MISSING,
135
				Result::SOURCE_DONOR_COMPANY
136
			);
137
		} else {
138
			$this->validateFieldLength( $this->request->getDonorCompany(), Result::SOURCE_DONOR_COMPANY );
139
		}
140
	}
141
142
	private function validatePersonName() {
143
		$violations = [];
144
145
		if ( $this->request->getDonorFirstName() === '' ) {
146
			$violations[] = new ConstraintViolation(
147
				$this->request->getDonorFirstName(),
148
				Result::VIOLATION_MISSING,
149
				Result::SOURCE_DONOR_FIRST_NAME
150
			);
151
		} else {
152
			$this->validateFieldLength( $this->request->getDonorFirstName(), Result::SOURCE_DONOR_FIRST_NAME );
153
		}
154
155
		if ( $this->request->getDonorLastName() === '' ) {
156
			$violations[] = new ConstraintViolation(
157
				$this->request->getDonorLastName(),
158
				Result::VIOLATION_MISSING,
159
				Result::SOURCE_DONOR_LAST_NAME
160
			);
161
		} else {
162
			$this->validateFieldLength( $this->request->getDonorLastName(), Result::SOURCE_DONOR_LAST_NAME );
163
		}
164
165
		if ( $this->request->getDonorSalutation() === '' ) {
166
			$violations[] = new ConstraintViolation(
167
				$this->request->getDonorSalutation(),
168
				Result::VIOLATION_MISSING,
169
				Result::SOURCE_DONOR_SALUTATION
170
			);
171
		} else {
172
			$this->validateFieldLength( $this->request->getDonorSalutation(), Result::SOURCE_DONOR_SALUTATION );
173
		}
174
175
		$this->validateFieldLength( $this->request->getDonorTitle(), Result::SOURCE_DONOR_TITLE );
176
		// TODO: check if donor title is in the list of allowed titles?
177
178
		$this->addViolations( $violations );
179
	}
180
181
	private function validateDonorAddress() {
182
		if ( $this->request->donorIsAnonymous() ) {
183
			return;
184
		}
185
186
		$violations = [];
187
188
		if ( $this->request->getDonorStreetAddress() === '' ) {
189
			$violations[] = new ConstraintViolation(
190
				$this->request->getDonorStreetAddress(),
191
				Result::VIOLATION_MISSING,
192
				Result::SOURCE_DONOR_STREET_ADDRESS
193
			);
194
		} else {
195
			$this->validateFieldLength( $this->request->getDonorStreetAddress(), Result::SOURCE_DONOR_STREET_ADDRESS );
196
		}
197
198
		if ( $this->request->getDonorPostalCode() === '' ) {
199
			$violations[] = new ConstraintViolation(
200
				$this->request->getDonorPostalCode(),
201
				Result::VIOLATION_MISSING,
202
				Result::SOURCE_DONOR_POSTAL_CODE
203
			);
204
		} else {
205
			$this->validateFieldLength( $this->request->getDonorPostalCode(), Result::SOURCE_DONOR_POSTAL_CODE );
206
		}
207
208
		if ( $this->request->getDonorCity() === '' ) {
209
			$violations[] = new ConstraintViolation(
210
				$this->request->getDonorCity(),
211
				Result::VIOLATION_MISSING,
212
				Result::SOURCE_DONOR_CITY
213
			);
214
		} else {
215
			$this->validateFieldLength( $this->request->getDonorCity(), Result::SOURCE_DONOR_CITY );
216
		}
217
218
		if ( $this->request->getDonorCountryCode() === '' ) {
219
			$violations[] = new ConstraintViolation(
220
				$this->request->getDonorCountryCode(),
221
				Result::VIOLATION_MISSING,
222
				Result::SOURCE_DONOR_COUNTRY
223
			);
224
		} else {
225
			$this->validateFieldLength( $this->request->getDonorCountryCode(), Result::SOURCE_DONOR_COUNTRY );
226
		}
227
228
		if ( !preg_match( '/^\\d{4,5}$/', $this->request->getDonorPostalCode() ) ) {
229
			$violations[] = new ConstraintViolation(
230
				$this->request->getDonorPostalCode(),
231
				Result::VIOLATION_NOT_POSTCODE,
232
				Result::SOURCE_DONOR_POSTAL_CODE
233
			);
234
		}
235
236
		$this->addViolations( $violations );
237
	}
238
239
	private function validatePayment() {
240
		if ( ! in_array( $this->request->getPaymentType(), PaymentType::getPaymentTypes() ) ) {
241
			$this->violations[] = new ConstraintViolation(
242
				$this->request->getPaymentType(),
243
				Result::VIOLATION_WRONG_PAYMENT_TYPE,
244
				Result::SOURCE_PAYMENT_TYPE
245
			);
246
		}
247
	}
248
249
	private function validateFieldLength( string $value, string $fieldName ) {
250
		if ( strlen( $value ) > $this->maximumFieldLengths[$fieldName] )  {
251
			$this->violations[] = new ConstraintViolation( $value, Result::VIOLATION_WRONG_LENGTH, $fieldName );
252
		}
253
	}
254
255
	private function validateTrackingData() {
256
		$this->validateFieldLength( $this->request->getSource(), Result::SOURCE_TRACKING_SOURCE );
257
		// validation of impression counts is not needed because input is converted to int
258
		// validation of skin, color and layout is not needed because they are static legacy values and empty.
259
	}
260
}