Passed
Push — master ( 6619f1...09285f )
by Manuel
02:06
created

QrBill   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 257
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 29
eloc 107
dl 0
loc 257
rs 10
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A getCreditor() 0 3 1
A extractQrCodeDataFromElements() 0 19 4
A loadValidatorMetadata() 0 37 1
A setHeader() 0 5 1
A setPaymentAmountInformation() 0 5 1
A getHeader() 0 3 1
A setCreditor() 0 5 1
A getAdditionalInformation() 0 3 1
A getQrCode() 0 18 2
A addAlternativeScheme() 0 5 1
A setAlternativeSchemes() 0 5 1
A getQrCodeData() 0 17 3
A getPaymentReference() 0 3 1
A setCreditorInformation() 0 5 1
A setAdditionalInformation() 0 5 1
A setPaymentReference() 0 5 1
A getAlternativeSchemes() 0 3 1
A getUltimateDebtor() 0 3 1
A create() 0 11 1
A getPaymentAmountInformation() 0 3 1
A getCreditorInformation() 0 3 1
A setUltimateDebtor() 0 5 1
A setErrorCorrectionLevel() 0 5 1
1
<?php
2
3
namespace Sprain\SwissQrBill;
4
5
use Endroid\QrCode\ErrorCorrectionLevel;
6
use Endroid\QrCode\QrCode;
7
use Sprain\SwissQrBill\DataGroups\Abstracts\Address;
8
use Sprain\SwissQrBill\DataGroups\AdditionalInformation;
9
use Sprain\SwissQrBill\DataGroups\AlternativeScheme;
10
use Sprain\SwissQrBill\DataGroups\CreditorInformation;
11
use Sprain\SwissQrBill\DataGroups\Header;
12
use Sprain\SwissQrBill\DataGroups\Interfaces\QrCodeData;
13
use Sprain\SwissQrBill\DataGroups\PaymentAmountInformation;
14
use Sprain\SwissQrBill\DataGroups\PaymentReference;
15
use Sprain\SwissQrBill\DataGroups\StructuredAddress;
16
use Sprain\SwissQrBill\Exception\InvalidQrBillDataException;
17
use Sprain\SwissQrBill\String\StringModifier;
18
use Sprain\SwissQrBill\Validator\Interfaces\Validatable;
19
use Sprain\SwissQrBill\Validator\ValidatorTrait;
20
use Symfony\Component\Validator\Constraints as Assert;
21
use Symfony\Component\Validator\Mapping\ClassMetadata;
22
23
class QrBill implements Validatable
24
{
25
    use ValidatorTrait;
26
27
    const SWISS_CROSS_LOGO_FILE = __DIR__ . '/../assets/swiss-cross.png';
28
29
    const ERROR_CORRECTION_LEVEL_HIGH = ErrorCorrectionLevel::HIGH;
30
    const ERROR_CORRECTION_LEVEL_MEDIUM = ErrorCorrectionLevel::MEDIUM;
31
    const ERROR_CORRECTION_LEVEL_LOW = ErrorCorrectionLevel::LOW;
32
33
    /** @var Header */
34
    private $header;
35
36
    /** @var CreditorInformation */
37
    private $creditorInformation;
38
39
    /** @var Address */
40
    private $creditor;
41
42
    /** @var PaymentAmountInformation */
43
    private $paymentAmountInformation;
44
45
    /** @var Address */
46
    private $ultimateDebtor;
47
48
    /** @var PaymentReference */
49
    private $paymentReference;
50
51
    /** @var AdditionalInformation */
52
    private $additionalInformation;
53
54
    /** @var AlternativeScheme[] */
55
    private $alternativeSchemes = [];
56
57
    /** @var string  */
58
    private $errorCorrectionLevel = self::ERROR_CORRECTION_LEVEL_HIGH;
59
60
    public static function create() : self
61
    {
62
        $header = new Header();
63
        $header->setCoding(Header::CODING_LATIN);
64
        $header->setQrType(Header::QRTYPE_SPC);
65
        $header->setVersion(Header::VERSION_0200);
66
67
        $qrBill = new self();
68
        $qrBill->setHeader($header);
69
70
        return $qrBill;
71
    }
72
73
    public function getHeader(): Header
74
    {
75
        return $this->header;
76
    }
77
78
    public function setHeader(Header $header) : self
79
    {
80
        $this->header = $header;
81
        
82
        return $this;
83
    }
84
85
    public function getCreditorInformation(): CreditorInformation
86
    {
87
        return $this->creditorInformation;
88
    }
89
90
    public function setCreditorInformation(CreditorInformation $creditorInformation) : self
91
    {
92
        $this->creditorInformation = $creditorInformation;
93
94
        return $this;
95
    }
96
97
    public function getCreditor(): Address
98
    {
99
        return $this->creditor;
100
    }
101
102
    public function setCreditor(Address $creditor) : self
103
    {
104
        $this->creditor = $creditor;
105
        
106
        return $this;
107
    }
108
109
    public function getPaymentAmountInformation(): PaymentAmountInformation
110
    {
111
        return $this->paymentAmountInformation;
112
    }
113
114
    public function setPaymentAmountInformation(PaymentAmountInformation $paymentAmountInformation) : self
115
    {
116
        $this->paymentAmountInformation = $paymentAmountInformation;
117
        
118
        return $this;
119
    }
120
121
    public function getUltimateDebtor(): ?Address
122
    {
123
        return $this->ultimateDebtor;
124
    }
125
126
    public function setUltimateDebtor(Address $ultimateDebtor) : self
127
    {
128
        $this->ultimateDebtor = $ultimateDebtor;
129
        
130
        return $this;
131
    }
132
133
    public function getPaymentReference(): PaymentReference
134
    {
135
        return $this->paymentReference;
136
    }
137
138
    public function setPaymentReference(PaymentReference $paymentReference) : self
139
    {
140
        $this->paymentReference = $paymentReference;
141
        
142
        return $this;
143
    }
144
145
    public function getAdditionalInformation(): ?AdditionalInformation
146
    {
147
        return $this->additionalInformation;
148
    }
149
150
    public function setAdditionalInformation(AdditionalInformation $additionalInformation) : self
151
    {
152
        $this->additionalInformation = $additionalInformation;
153
154
        return $this;
155
    }
156
157
    public function getAlternativeSchemes(): array
158
    {
159
        return $this->alternativeSchemes;
160
    }
161
162
    public function setAlternativeSchemes(array $alternativeSchemes) : self
163
    {
164
        $this->alternativeSchemes = $alternativeSchemes;
165
166
        return $this;
167
    }
168
169
    public function addAlternativeScheme(AlternativeScheme $alternativeScheme) : self
170
    {
171
        $this->alternativeSchemes[] = $alternativeScheme;
172
173
        return $this;
174
    }
175
176
    public function setErrorCorrectionLevel(string $errorCorrectionLevel) : self
177
    {
178
        $this->errorCorrectionLevel = $errorCorrectionLevel;
179
180
        return $this;
181
    }
182
183
    public function getQrCode() : QrCode
184
    {
185
        if (!$this->isValid()) {
186
            throw new InvalidQrBillDataException(
187
                'The provided data is not valid to generate a qr code. Use getViolations() to find details.'
188
            );
189
        }
190
191
        $qrCode = new QrCode();
192
        $qrCode->setText($this->getQrCodeData());
193
        $qrCode->setSize(543); // recommended 46x46 mm in px @ 300dpi
194
        $qrCode->setLogoPath(self::SWISS_CROSS_LOGO_FILE);
195
        $qrCode->setLogoWidth(83); // recommended 7x7 mm in px @ 300dpi
196
        $qrCode->setRoundBlockSize(false);
197
        $qrCode->setMargin(0);
198
        $qrCode->setErrorCorrectionLevel(new ErrorCorrectionLevel($this->errorCorrectionLevel));
199
200
        return $qrCode;
201
    }
202
203
    private function getQrCodeData() : string
204
    {
205
        $elements = [
206
            $this->getHeader(),
207
            $this->getCreditorInformation(),
208
            $this->getCreditor(),
209
            new StructuredAddress(),
210
            $this->getPaymentAmountInformation(),
211
            $this->getUltimateDebtor() ?: new StructuredAddress(),
212
            $this->getPaymentReference(),
213
            $this->getAdditionalInformation() ?: new AdditionalInformation(),
214
            $this->getAlternativeSchemes()
215
        ];
216
217
        $qrCodeStringElements = $this->extractQrCodeDataFromElements($elements);
218
219
        return implode("\r\n", $qrCodeStringElements);
220
    }
221
222
    private function extractQrCodeDataFromElements(array $elements) : array
223
    {
224
        $qrCodeElements = [];
225
226
        foreach ($elements as $element) {
227
            if ($element instanceof QrCodeData) {
228
                $qrCodeElements = array_merge($qrCodeElements, $element->getQrCodeData());
229
            } elseif (is_array($element)) {
230
                $qrCodeElements = array_merge($qrCodeElements, $this->extractQrCodeDataFromElements($element));
231
            }
232
        }
233
234
        array_walk($qrCodeElements, function(&$string){
235
            $string = StringModifier::replaceLineBreaksWithString($string);
236
            $string = StringModifier::replaceMultipleSpacesWithOne($string);
237
            $string = trim($string);
238
        });
239
240
        return $qrCodeElements;
241
    }
242
243
    public static function loadValidatorMetadata(ClassMetadata $metadata)
244
    {
245
        $metadata->addPropertyConstraints('header', [
246
            new Assert\NotNull(),
247
            new Assert\Valid()
248
        ]);
249
250
        $metadata->addPropertyConstraints('creditorInformation', [
251
            new Assert\NotNull(),
252
            new Assert\Valid()
253
        ]);
254
255
        $metadata->addPropertyConstraints('creditor', [
256
            new Assert\NotNull(),
257
            new Assert\Valid()
258
        ]);
259
260
        $metadata->addPropertyConstraints('paymentAmountInformation', [
261
            new Assert\NotNull(),
262
            new Assert\Valid()
263
        ]);
264
265
        $metadata->addPropertyConstraints('ultimateDebtor', [
266
            new Assert\Valid()
267
        ]);
268
269
        $metadata->addPropertyConstraints('paymentReference', [
270
            new Assert\NotNull(),
271
            new Assert\Valid()
272
        ]);
273
274
        $metadata->addPropertyConstraints('alternativeSchemes', [
275
            new Assert\Count([
276
                'max' => 2
277
            ]),
278
            new Assert\Valid([
279
                'traverse' => true
280
            ])
281
        ]);
282
    }
283
}
284