QrBill::extractQrCodeDataFromElements()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 13
c 2
b 0
f 0
dl 0
loc 23
rs 9.5222
cc 5
nc 4
nop 1
1
<?php declare(strict_types=1);
2
3
namespace Sprain\SwissQrBill;
4
5
use Sprain\SwissQrBill\Constraint\ValidCreditorInformationPaymentReferenceCombination;
6
use Sprain\SwissQrBill\DataGroup\AddressInterface;
7
use Sprain\SwissQrBill\DataGroup\Element\AdditionalInformation;
8
use Sprain\SwissQrBill\DataGroup\Element\AlternativeScheme;
9
use Sprain\SwissQrBill\DataGroup\Element\CreditorInformation;
10
use Sprain\SwissQrBill\DataGroup\Element\Header;
11
use Sprain\SwissQrBill\DataGroup\Element\PaymentAmountInformation;
12
use Sprain\SwissQrBill\DataGroup\Element\PaymentReference;
13
use Sprain\SwissQrBill\DataGroup\EmptyElement\EmptyAdditionalInformation;
14
use Sprain\SwissQrBill\DataGroup\EmptyElement\EmptyAddress;
15
use Sprain\SwissQrBill\DataGroup\QrCodeableInterface;
16
use Sprain\SwissQrBill\Exception\InvalidQrBillDataException;
17
use Sprain\SwissQrBill\QrCode\QrCode;
18
use Sprain\SwissQrBill\String\StringModifier;
19
use Sprain\SwissQrBill\Validator\SelfValidatableInterface;
20
use Sprain\SwissQrBill\Validator\SelfValidatableTrait;
21
use Symfony\Component\Validator\Constraints as Assert;
22
use Symfony\Component\Validator\Mapping\ClassMetadata;
23
24
final class QrBill implements SelfValidatableInterface
25
{
26
    use SelfValidatableTrait;
27
28
    private Header $header;
29
    private ?CreditorInformation $creditorInformation = null;
30
    private ?AddressInterface $creditor = null;
31
    private ?PaymentAmountInformation $paymentAmountInformation = null;
32
    private ?AddressInterface $ultimateDebtor = null;
33
    private ?PaymentReference $paymentReference = null;
34
    private ?AdditionalInformation $additionalInformation = null;
35
    /** @var AlternativeScheme[] */
36
    private array $alternativeSchemes = [];
37
38
    private function __construct(Header $header)
39
    {
40
        $this->header = $header;
41
    }
42
43
    public static function create(): self
44
    {
45
        $header = Header::create(
46
            Header::QRTYPE_SPC,
47
            Header::VERSION_0200,
48
            Header::CODING_LATIN
49
        );
50
51
        return new self($header);
52
    }
53
54
    public function getHeader(): Header
55
    {
56
        return $this->header;
57
    }
58
59
    public function setHeader(Header $header): self
60
    {
61
        $this->header = $header;
62
63
        return $this;
64
    }
65
66
    public function getCreditorInformation(): ?CreditorInformation
67
    {
68
        return $this->creditorInformation;
69
    }
70
71
    public function setCreditorInformation(CreditorInformation $creditorInformation): self
72
    {
73
        $this->creditorInformation = $creditorInformation;
74
75
        return $this;
76
    }
77
78
    public function getCreditor(): ?AddressInterface
79
    {
80
        return $this->creditor;
81
    }
82
83
    public function setCreditor(AddressInterface $creditor): self
84
    {
85
        $this->creditor = $creditor;
86
        
87
        return $this;
88
    }
89
90
    public function getPaymentAmountInformation(): ?PaymentAmountInformation
91
    {
92
        return $this->paymentAmountInformation;
93
    }
94
95
    public function setPaymentAmountInformation(PaymentAmountInformation $paymentAmountInformation): self
96
    {
97
        $this->paymentAmountInformation = $paymentAmountInformation;
98
        
99
        return $this;
100
    }
101
102
    public function getUltimateDebtor(): ?AddressInterface
103
    {
104
        return $this->ultimateDebtor;
105
    }
106
107
    public function setUltimateDebtor(AddressInterface $ultimateDebtor): self
108
    {
109
        $this->ultimateDebtor = $ultimateDebtor;
110
        
111
        return $this;
112
    }
113
114
    public function getPaymentReference(): ?PaymentReference
115
    {
116
        return $this->paymentReference;
117
    }
118
119
    public function setPaymentReference(PaymentReference $paymentReference): self
120
    {
121
        $this->paymentReference = $paymentReference;
122
        
123
        return $this;
124
    }
125
126
    public function getAdditionalInformation(): ?AdditionalInformation
127
    {
128
        return $this->additionalInformation;
129
    }
130
131
    public function setAdditionalInformation(AdditionalInformation $additionalInformation): self
132
    {
133
        $this->additionalInformation = $additionalInformation;
134
135
        return $this;
136
    }
137
138
    public function getAlternativeSchemes(): array
139
    {
140
        return $this->alternativeSchemes;
141
    }
142
143
    public function setAlternativeSchemes(array $alternativeSchemes): self
144
    {
145
        $this->alternativeSchemes = $alternativeSchemes;
146
147
        return $this;
148
    }
149
150
    public function addAlternativeScheme(AlternativeScheme $alternativeScheme): self
151
    {
152
        $this->alternativeSchemes[] = $alternativeScheme;
153
154
        return $this;
155
    }
156
157
    /**
158
     * @throws InvalidQrBillDataException
159
     */
160
    public function getQrCode(?string $fileFormat = null): QrCode
161
    {
162
        if (!$this->isValid()) {
163
            throw new InvalidQrBillDataException(
164
                'The provided data is not valid to generate a qr code. Use getViolations() to find details.'
165
            );
166
        }
167
168
        return QrCode::create(
169
            $this->getQrCodeContent(),
170
            $fileFormat
171
        );
172
    }
173
174
    private function getQrCodeContent(): string
175
    {
176
        $elements = [
177
            $this->getHeader(),
178
            $this->getCreditorInformation(),
179
            $this->getCreditor(),
180
            new EmptyAddress(), # Placeholder for ultimateCreditor, which is currently not yet allowed to be used by the implementation guidelines
181
            $this->getPaymentAmountInformation(),
182
            $this->getUltimateDebtor() ?: new EmptyAddress(),
183
            $this->getPaymentReference(),
184
            $this->getAdditionalInformation() ?: new EmptyAdditionalInformation(),
185
            $this->getAlternativeSchemes()
186
        ];
187
188
        $qrCodeStringElements = $this->extractQrCodeDataFromElements($elements);
189
190
        return implode("\n", $qrCodeStringElements);
191
    }
192
193
    private function extractQrCodeDataFromElements(array $elements): array
194
    {
195
        $qrCodeElements = [];
196
197
        foreach ($elements as $element) {
198
            if ($element instanceof QrCodeableInterface) {
199
                $qrCodeElements[] = $element->getQrCodeData();
200
            } elseif (is_array($element)) {
201
                $qrCodeElements[] = $this->extractQrCodeDataFromElements($element);
202
            }
203
        }
204
205
        $qrCodeElements = array_merge(... $qrCodeElements);
206
207
        array_walk($qrCodeElements, static function (&$string) {
208
            if (is_string($string)) {
209
                $string = StringModifier::replaceLineBreaksWithString($string);
210
                $string = StringModifier::replaceMultipleSpacesWithOne($string);
211
                $string = trim($string);
212
            }
213
        });
214
215
        return $qrCodeElements;
216
    }
217
218
    public static function loadValidatorMetadata(ClassMetadata $metadata): void
219
    {
220
        $metadata->addConstraint(
221
            new ValidCreditorInformationPaymentReferenceCombination()
222
        );
223
224
        $metadata->addPropertyConstraints('header', [
225
            new Assert\NotNull(),
226
            new Assert\Valid()
227
        ]);
228
229
        $metadata->addPropertyConstraints('creditorInformation', [
230
            new Assert\NotNull(),
231
            new Assert\Valid()
232
        ]);
233
234
        $metadata->addPropertyConstraints('creditor', [
235
            new Assert\NotNull(),
236
            new Assert\Valid()
237
        ]);
238
239
        $metadata->addPropertyConstraints('paymentAmountInformation', [
240
            new Assert\NotNull(),
241
            new Assert\Valid()
242
        ]);
243
244
        $metadata->addPropertyConstraints('ultimateDebtor', [
245
            new Assert\Valid()
246
        ]);
247
248
        $metadata->addPropertyConstraints('paymentReference', [
249
            new Assert\NotNull(),
250
            new Assert\Valid()
251
        ]);
252
253
        $metadata->addPropertyConstraints('alternativeSchemes', [
254
            new Assert\Count([
255
                'max' => 2
256
            ]),
257
            new Assert\Valid([
258
                'traverse' => true
259
            ])
260
        ]);
261
    }
262
}
263