Passed
Push — master ( 88fe71...f59922 )
by Hannes
02:55 queued 11s
created

JsonDonorRepository::createDoc()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 23
nc 1
nop 1
dl 0
loc 26
rs 9.552
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of byrokrat\giroapp.
4
 *
5
 * byrokrat\giroapp is free software: you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License as published
7
 * by the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * byrokrat\giroapp is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with byrokrat\giroapp. If not, see <http://www.gnu.org/licenses/>.
17
 *
18
 * Copyright 2016-19 Hannes Forsgård
19
 */
20
21
declare(strict_types = 1);
22
23
namespace byrokrat\giroapp\Db\Json;
24
25
use byrokrat\giroapp\Db\DonorRepositoryInterface;
26
use byrokrat\giroapp\Exception\DonorDoesNotExistException;
27
use byrokrat\giroapp\Exception\DonorAlreadyExistsException;
28
use byrokrat\giroapp\Model\Donor;
29
use byrokrat\giroapp\Model\DonorCollection;
30
use byrokrat\giroapp\Model\DonorFactory;
31
use byrokrat\giroapp\Model\PostalAddress;
32
use byrokrat\giroapp\Utils\SystemClock;
33
use byrokrat\giroapp\State\StateInterface;
34
use byrokrat\amount\Currency\SEK;
35
use hanneskod\yaysondb\CollectionInterface;
36
use hanneskod\yaysondb\Operators as y;
37
38
final class JsonDonorRepository implements DonorRepositoryInterface
39
{
40
    const TYPE = 'giroapp/donor:alpha5';
41
42
    /** @var CollectionInterface */
43
    private $collection;
44
45
    /** @var DonorFactory */
46
    private $donorFactory;
47
48
    /** @var SystemClock */
49
    private $systemClock;
50
51
    public function __construct(
52
        CollectionInterface $collection,
53
        DonorFactory $donorFactory,
54
        SystemClock $systemClock
55
    ) {
56
        $this->collection = $collection;
57
        $this->donorFactory = $donorFactory;
58
        $this->systemClock = $systemClock;
59
    }
60
61
    public function findAll(): DonorCollection
62
    {
63
        return new DonorCollection(function () {
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type iterable expected by parameter $donors of byrokrat\giroapp\Model\D...llection::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

63
        return new DonorCollection(/** @scrutinizer ignore-type */ function () {
Loading history...
64
            foreach ($this->collection as $doc) {
65
                yield $this->createDonor($doc);
66
            }
67
        });
68
    }
69
70
    public function findByMandateKey(string $mandateKey): ?Donor
71
    {
72
        if ($this->collection->has($mandateKey)) {
73
            return $this->createDonor($this->collection->read($mandateKey));
74
        }
75
76
        return null;
77
    }
78
79
    public function requireByMandateKey(string $mandateKey): Donor
80
    {
81
        if ($donor = $this->findByMandateKey($mandateKey)) {
82
            return $donor;
83
        }
84
85
        throw new DonorDoesNotExistException("unknown donor key: $mandateKey");
86
    }
87
88
    public function findByPayerNumber(string $payerNumber): ?Donor
89
    {
90
        $doc = $this->collection->findOne(
91
            y::doc(['payer_number' => y::equals($payerNumber)])
92
        );
93
94
        if (empty($doc)) {
95
            return null;
96
        }
97
98
        return $this->createDonor($doc);
99
    }
100
101
    public function requireByPayerNumber(string $payerNumber): Donor
102
    {
103
        if ($donor = $this->findByPayerNumber($payerNumber)) {
104
            return $donor;
105
        }
106
107
        throw new DonorDoesNotExistException("unknown payer number: $payerNumber");
108
    }
109
110
    public function addNewDonor(Donor $newDonor): void
111
    {
112
        $expr = y::atLeastOne(
113
            y::doc(['mandate_key' => y::equals($newDonor->getMandateKey())]),
114
            y::doc(['payer_number' => y::equals($newDonor->getPayerNumber())]),
115
            y::doc(['donor_id' => y::equals($newDonor->getDonorId())])
116
        );
117
118
        if ($doc = $this->collection->findOne($expr)) {
119
            throw new DonorAlreadyExistsException(
120
                sprintf(
121
                    "Unable to save donor %s due to a conflict with donor %s",
122
                    $newDonor->getMandateKey(),
123
                    $doc['mandate_key'] ?? ''
124
                )
125
            );
126
        }
127
128
        $this->collection->insert($this->createDoc($newDonor), $newDonor->getMandateKey());
129
    }
130
131
    public function deleteDonor(Donor $donor): void
132
    {
133
        $this->requireByMandateKey($donor->getMandateKey());
134
135
        $this->collection->delete(
136
            y::doc(['mandate_key' => y::equals($donor->getMandateKey())])
137
        );
138
    }
139
140
    public function updateDonorName(Donor $donor, string $newName): void
141
    {
142
        $this->updateDonor($donor, ['name' => $newName]);
143
    }
144
145
    public function updateDonorState(Donor $donor, StateInterface $newState, string $stateDesc = ''): void
146
    {
147
        $this->updateDonor($donor, [
148
            'state' => $newState->getStateId(),
149
            'state_desc' => $stateDesc ?: $newState->getDescription()
150
        ]);
151
    }
152
153
    public function updateDonorPayerNumber(Donor $donor, string $newPayerNumber): void
154
    {
155
        $expr = y::doc([
156
            'payer_number' => y::equals($newPayerNumber),
157
            'mandate_key' => y::not(y::equals($donor->getMandateKey())),
158
        ]);
159
160
        if ($doc = $this->collection->findOne($expr)) {
161
            throw new DonorAlreadyExistsException(
162
                sprintf(
163
                    "Unable to set payer number %s due to a conflict with donor %s",
164
                    $donor->getMandateKey(),
165
                    $doc['mandate_key'] ?? ''
166
                )
167
            );
168
        }
169
170
        $this->updateDonor($donor, ['payer_number' => $newPayerNumber]);
171
    }
172
173
    public function updateDonorAmount(Donor $donor, SEK $newAmount): void
174
    {
175
        $this->updateDonor($donor, ['donation_amount' => $newAmount->getAmount()]);
176
    }
177
178
    public function updateDonorAddress(Donor $donor, PostalAddress $newAddress): void
179
    {
180
        $this->updateDonor($donor, [
181
            'address' => [
182
                'line1' => $newAddress->getLine1(),
183
                'line2' => $newAddress->getLine2(),
184
                'line3' => $newAddress->getLine3(),
185
                'postal_code' => $newAddress->getPostalCode(),
186
                'postal_city' => $newAddress->getPostalCity(),
187
            ]
188
        ]);
189
    }
190
191
    public function updateDonorEmail(Donor $donor, string $newEmail): void
192
    {
193
        $this->updateDonor($donor, ['email' => $newEmail]);
194
    }
195
196
    public function updateDonorPhone(Donor $donor, string $newPhone): void
197
    {
198
        $this->updateDonor($donor, ['phone' => $newPhone]);
199
    }
200
201
    public function updateDonorComment(Donor $donor, string $newComment): void
202
    {
203
        $this->updateDonor($donor, ['comment' => $newComment]);
204
    }
205
206
    public function setDonorAttribute(Donor $donor, string $key, string $value): void
207
    {
208
        $attributes = $this->createDoc($donor)['attributes'] ?? [];
209
        $attributes[$key] = $value;
210
        $this->updateDonor($donor, ['attributes' => $attributes]);
211
    }
212
213
    public function deleteDonorAttribute(Donor $donor, string $key): void
214
    {
215
        $attributes = $this->createDoc($donor)['attributes'] ?? [];
216
        unset($attributes[$key]);
217
        $this->updateDonor($donor, ['attributes' => $attributes]);
218
    }
219
220
    private function updateDonor(Donor $donor, array $updatedValues): void
221
    {
222
        $this->requireByMandateKey($donor->getMandateKey());
223
224
        $doc = array_merge($this->createDoc($donor), $updatedValues);
225
226
        $doc['updated'] = $this->systemClock->getNow()->format(\DateTime::W3C);
227
228
        $this->collection->insert($doc, $donor->getMandateKey());
229
    }
230
231
    private function createDoc(Donor $donor): array
232
    {
233
        return [
234
            'type' => self::TYPE,
235
            'mandate_key' => $donor->getMandateKey(),
236
            'state' => $donor->getState()->getStateId(),
237
            'state_desc' => $donor->getStateDesc(),
238
            'mandate_source' => $donor->getMandateSource(),
239
            'payer_number' => $donor->getPayerNumber(),
240
            'account' => $donor->getAccount()->getNumber(),
241
            'donor_id' => $donor->getDonorId()->format('S-sk'),
242
            'name' => $donor->getName(),
243
            'address' => [
244
                'line1' => $donor->getPostalAddress()->getLine1(),
245
                'line2' => $donor->getPostalAddress()->getLine2(),
246
                'line3' => $donor->getPostalAddress()->getLine3(),
247
                'postal_code' => $donor->getPostalAddress()->getPostalCode(),
248
                'postal_city' => $donor->getPostalAddress()->getPostalCity(),
249
            ],
250
            'email' => $donor->getEmail(),
251
            'phone' => $donor->getPhone(),
252
            'donation_amount' => $donor->getDonationAmount()->getAmount(),
253
            'comment' => $donor->getComment(),
254
            'created' => $donor->getCreated()->format(\DateTime::W3C),
255
            'updated' => $donor->getUpdated()->format(\DateTime::W3C),
256
            'attributes' => $donor->getAttributes(),
257
        ];
258
    }
259
260
    private function createDonor(array $doc): Donor
261
    {
262
        return $this->donorFactory->createDonor(
263
            $doc['mandate_key'],
264
            $doc['state'],
265
            $doc['state_desc'],
266
            $doc['mandate_source'],
267
            $doc['payer_number'],
268
            $doc['account'],
269
            $doc['donor_id'],
270
            $doc['name'],
271
            array_values($doc['address']),
272
            $doc['email'],
273
            $doc['phone'],
274
            $doc['donation_amount'],
275
            $doc['comment'],
276
            $doc['created'],
277
            $doc['updated'],
278
            $doc['attributes']
279
        );
280
    }
281
}
282