• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grueneschweiz / mailchimpservice / 17108989360

20 Aug 2025 08:00PM UTC coverage: 68.598% (-1.6%) from 70.247%
17108989360

push

github

Michael-Schaer
[FEAT] Sync from mailchimp to crm in specific cases

197 of 333 new or added lines in 10 files covered. (59.16%)

4 existing lines in 2 files now uncovered.

959 of 1398 relevant lines covered (68.6%)

11.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.75
/app/Synchronizer/Config.php
1
<?php
2

3

4
namespace App\Synchronizer;
5

6

7
use App\Exceptions\ConfigException;
8
use App\Synchronizer\Mapper\FieldMapFacade;
9
use Symfony\Component\Yaml\Exception\ParseException;
10
use Symfony\Component\Yaml\Yaml;
11

12
class Config
13
{
14
    private const CRM_ID_KEY = 'id';
15

16
    private $fields;
17
    private $auth;
18
    private $dataOwner;
19
    private $mailchimp;
20
    private $mailchimpToCrm;
21
    private $errors;
22
    private $crmEmailKey;
23
    private $webling;
24

25
    /**
26
     * Config constructor.
27
     *
28
     * @param string $configFileName file name of the config file
29
     *
30
     * @throws ConfigException
31
     */
32
    public function __construct(string $configFileName)
33
    {
34
        $this->loadConfig($configFileName);
60✔
35
    }
36

37
    /**
38
     * Read the config file and populate object
39
     *
40
     * @param string $configFileName file name of the config file
41
     *
42
     * @throws ConfigException
43
     */
44
    private function loadConfig(string $configFileName)
45
    {
46
        $configFileName = ltrim($configFileName, './');
60✔
47
        $configFolderPath = rtrim(config('app.config_base_path'), '/');
60✔
48
        $configFilePath = base_path($configFolderPath . '/' . $configFileName);
60✔
49

50
        if (!file_exists($configFilePath)) {
60✔
51
            throw new ConfigException('The config file was not found.');
1✔
52
        }
53

54
        try {
55
            $config = Yaml::parseFile($configFilePath);
60✔
56
        } catch (ParseException $e) {
×
57
            throw new ConfigException("YAML parse error: {$e->getMessage()}");
×
58
        }
59

60
        // prevalidate config
61
        if ($config['auth']) {
60✔
62
            $this->auth = $config['auth'];
60✔
63
        } else {
64
            throw new ConfigException("Missing 'auth' section.");
×
65
        }
66

67
        if ($config['dataOwner']) {
60✔
68
            $this->dataOwner = $config['dataOwner'];
60✔
69
        } else {
70
            throw new ConfigException("Missing 'dataOwner' section.");
×
71
        }
72

73
        if ($config['mailchimp']) {
60✔
74
            $this->mailchimp = $config['mailchimp'];
60✔
75
        } else {
76
            throw new ConfigException("Missing 'mailchimp' section.");
×
77
        }
78

79
        if ($config['mailchimpToCrm']) {
60✔
80
            $this->mailchimpToCrm = $config['mailchimpToCrm'];
60✔
81
            $this->mailchimpToCrm['isUpsertToCrmEnabled'] = true;
60✔
82
        } else {
NEW
83
            $this->mailchimpToCrm['isUpsertToCrmEnabled'] = false;
×
84
        }
85

86
        if ($config['fields']) {
60✔
87
            $this->fields = $config['fields'];
60✔
88
        } else {
89
            throw new ConfigException("Missing 'fields' section.");
×
90
        }
91

92
        if (isset($config['webling'])) {
60✔
93
            $this->webling = $config['webling'];
60✔
94
        }
95
    }
96

97
    public static function getCrmIdKey()
98
    {
99
        return self::CRM_ID_KEY;
23✔
100
    }
101

102
    /**
103
     * Return array with crm credentials
104
     *
105
     * @return array {clientId: string, clientSecret: string, url: string}
106
     *
107
     * @throws ConfigException
108
     */
109
    public function getCrmCredentials(): array
110
    {
111
        if (
112
            empty($this->auth['crm'])
4✔
113
            || empty($this->auth['crm']['clientId'])
4✔
114
            || empty($this->auth['crm']['clientSecret'])
2✔
115
            || empty($this->auth['crm']['url'])
4✔
116
        ) {
117
            throw new ConfigException("Missing CRM credentials.");
2✔
118
        }
119

120
        return $this->auth['crm'];
2✔
121
    }
122

123
    /**
124
     * Return array with mailchimp credentials
125
     *
126
     * @return array {apikey: string, url: string}
127
     *
128
     * @throws ConfigException
129
     */
130
    public function getMailchimpCredentials(): array
131
    {
132
        if (
133
            empty($this->auth['mailchimp'])
4✔
134
            || empty($this->auth['mailchimp']['apikey'])
4✔
135
        ) {
136
            throw new ConfigException("Missing Mailchimp credentials.");
2✔
137
        }
138

139
        return $this->auth['mailchimp'];
2✔
140
    }
141

142
    /**
143
     * Return array with name and email of data owner
144
     *
145
     * @return array {email: string, name: string,}
146
     *
147
     * @throws ConfigException
148
     */
149
    public function getDataOwner(): array
150
    {
151
        if (
152
            empty($this->dataOwner)
5✔
153
            || empty($this->dataOwner['email'])
5✔
154
            || empty($this->dataOwner['name'])
5✔
155
        ) {
156
            throw new ConfigException("Missing data owner details.");
2✔
157
        }
158

159
        return $this->dataOwner;
3✔
160
    }
161

162
    /**
163
     * Return bool that indicates if all records should be synced even if they dont have
164
     * relevant subscriptions
165
     *
166
     * @return bool
167
     */
168
    public function getSyncAll(): bool
169
    {
170
        if (array_key_exists('syncAll', $this->mailchimp)) {
30✔
171
            return filter_var($this->mailchimp['syncAll'], FILTER_VALIDATE_BOOLEAN);
30✔
172
        }
173

174
        return false;
×
175
    }
176

177
    /**
178
     * Return bool that indicates if subscriptions through mailchimp should be ignored
179
     *
180
     * @return bool
181
     */
182
    public function getIgnoreSubscribeThroughMailchimp(): bool
183
    {
184
        if (array_key_exists('ignoreSubscribeThroughMailchimp', $this->mailchimp)) {
2✔
185
            return filter_var($this->mailchimp['ignoreSubscribeThroughMailchimp'], FILTER_VALIDATE_BOOLEAN);
2✔
186
        }
187

188
        return false;
×
189
    }
190

191
    /**
192
     * Return the default list id in Mailchimp
193
     *
194
     * @return string the list id
195
     *
196
     * @throws ConfigException
197
     */
198
    public function getMailchimpListId(): string
199
    {
200
        if (empty($this->mailchimp['listId'])) {
47✔
201
            throw new ConfigException("Missing mailchimp list id.");
2✔
202
        }
203

204
        return $this->mailchimp['listId'];
45✔
205
    }
206

207
    /**
208
     * The mailchimp merge field key that corresponds to the crm's id
209
     *
210
     * @return string
211
     * @throws ConfigException
212
     */
213
    public function getMailchimpKeyOfCrmId(): string
214
    {
215
        foreach ($this->getFieldMaps() as $map) {
39✔
216
            if (self::CRM_ID_KEY === $map->getCrmKey()) {
39✔
217
                $keys = array_keys($map->getMailchimpDataArray());
37✔
218

219
                return reset($keys);
37✔
220
            }
221
        }
222

223
        throw new ConfigException('Missing "' . self::CRM_ID_KEY . '" field.');
2✔
224
    }
225

226
    /**
227
     * Return array with the field maps
228
     *
229
     * @return FieldMapFacade[]
230
     *
231
     * @throws ConfigException
232
     */
233
    public function getFieldMaps(): array
234
    {
235
        if (!is_array($this->fields)) {
54✔
236
            throw new ConfigException("Fields configuration must be an array.");
×
237
        }
238

239
        $fields = [];
54✔
240
        foreach ($this->fields as $config) {
54✔
241
            $fields[] = new FieldMapFacade($config);
54✔
242
        }
243

244
        return $fields;
54✔
245
    }
246

247
    /**
248
     * Return the field key in the crm that corresponds to the email field
249
     *
250
     * @return string
251
     * @throws ConfigException
252
     */
253
    public function getCrmEmailKey(): string
254
    {
255
        if ($this->crmEmailKey) {
19✔
256
            return $this->crmEmailKey;
18✔
257
        }
258

259
        foreach ($this->getFieldMaps() as $map) {
19✔
260
            if ($map->isEmail()) {
19✔
261
                $this->crmEmailKey = $map->getCrmKey();
19✔
262

263
                return $this->crmEmailKey;
19✔
264
            }
265
        }
266

267
        throw new ConfigException('Missing email field.');
×
268
    }
269

270
    /**
271
     * Return array of the Webling group ids of the prioritized groups
272
     *
273
     * @return int[]
274
     */
275
    public function getPrioritizedGroups(): array
276
    {
277
        return $this->webling['prioritizedGroups'] ?? [];
5✔
278
    }
279

280
    /**
281
     * Return the validation errors of this config
282
     *
283
     * @return array
284
     */
285
    public function getErrors(): array
286
    {
287
        if (is_null($this->errors)) {
1✔
288
            $this->isValid();
1✔
289
        }
290

291
        return $this->errors;
1✔
292
    }
293

294
    /**
295
     * Return true, if the given config is valid
296
     *
297
     * @return bool
298
     */
299
    public function isValid(): bool
300
    {
301
        $methods = [
3✔
302
            'getCrmCredentials',
3✔
303
            'getDataOwner',
3✔
304
            'getFieldMaps',
3✔
305
            'getMailchimpCredentials',
3✔
306
            'getMailchimpKeyOfCrmId',
3✔
307
            'getMailchimpListId'
3✔
308
        ];
3✔
309

310
        $this->errors = [];
3✔
311
        foreach ($methods as $method) {
3✔
312
            // we throw errors and catch them, because one can also change the config
313
            // while the endpoint is already established, so the validation is not
314
            // necessarily executed.
315
            try {
316
                $this->$method();
3✔
317
            } catch (ConfigException $e) {
2✔
318
                $this->errors[] = $e->getMessage();
2✔
319
            }
320
        }
321

322
        return empty($this->errors);
3✔
323
    }
324

325
    /**
326
     * Get language tags from the config
327
     *
328
     * @return array List of language tag names
329
     */
330
    public function getLanguageTagsFromConfig(): array
331
    {
NEW
332
        $languageTags = [];
×
NEW
333
        $fieldMaps = $this->getFieldMaps();
×
334

NEW
335
        foreach ($fieldMaps as $fieldMap) {
×
336
            if (
NEW
337
                $fieldMap->getMailchimpParentKey() === 'tags' &&
×
NEW
338
                $fieldMap->canSyncToMailchimp() &&
×
NEW
339
                $fieldMap->getCrmKey() === 'language'
×
340
            ) {
NEW
341
                $reflection = new \ReflectionObject($fieldMap);
×
NEW
342
                $property = $reflection->getProperty('field');
×
NEW
343
                $property->setAccessible(true);
×
NEW
344
                $field = $property->getValue($fieldMap);
×
345

NEW
346
                $tagReflection = new \ReflectionObject($field);
×
NEW
347
                $tagNameProperty = $tagReflection->getProperty('mailchimpTagName');
×
NEW
348
                $tagNameProperty->setAccessible(true);
×
NEW
349
                $tagName = $tagNameProperty->getValue($field);
×
350

NEW
351
                if (!empty($tagName)) {
×
NEW
352
                    $languageTags[] = $tagName;
×
353
                }
354
            }
355
        }
356

NEW
357
        return $languageTags;
×
358
    }
359

360
    /**
361
     * Return bool that indicates if upserting to CRM is enabled
362
     *
363
     * @return bool
364
     */
365
    public function isUpsertToCrmEnabled(): bool
366
    {
NEW
367
        return !empty($this->mailchimpToCrm['isUpsertToCrmEnabled']);
×
368
    }
369

370
    /**
371
     * Return the tag that should be added to new subscribers
372
     *
373
     * @return string
374
     */
375
    public function getNewTag(): string
376
    {
377
        if (empty($this->mailchimpToCrm['newtag'])) {
3✔
NEW
378
            throw new ConfigException('Missing "newtag" field.');
×
379
        }
380

381
        return $this->mailchimpToCrm['newtag'];
3✔
382
    }
383
    /**
384
     * Return array of keys that should trigger an upsert to CRM when set to 'yes'
385
     * If the configuration is an array, returns that array
386
     * Otherwise returns an empty array (no upsert)
387
     *
388
     * @return array
389
     */
390
    public function getInterestsToSync(): array
391
    {
NEW
392
        if (array_key_exists('interestsToSync', $this->mailchimpToCrm)) {
×
NEW
393
            $config = $this->mailchimpToCrm['interestsToSync'];
×
394

NEW
395
            if (is_array($config)) {
×
NEW
396
                return $config;
×
397
            }
398
        }
399

NEW
400
        return [];
×
401
    }
402

403
    /**
404
     * Return the number of months to consider for the changed within filter
405
     *
406
     * @return int
407
     */
408
    public function getChangedWithinMonths(): int
409
    {
410
        return $this->mailchimpToCrm['changedWithinMonths'] ?? 6;
3✔
411
    }
412

413
    /**
414
     * Return the number of months to consider for the opt-in older than filter
415
     *
416
     * @return int
417
     */
418
    public function getOptInOlderThanMonths(): int
419
    {
420
        return $this->mailchimpToCrm['optInOlderThanMonths'] ?? 2;
3✔
421
    }
422

423
    /**
424
     * Return the group where new people should be added in the crm
425
     *
426
     * @return int
427
     */
428
    public function getGroupForNewMembers(): int
429
    {
430
        if (empty($this->mailchimpToCrm['groupForNewMembers'])) {
13✔
NEW
431
            throw new ConfigException('Missing "groupForNewMembers" field.');
×
432
        }
433

434
        return $this->mailchimpToCrm['groupForNewMembers'];
13✔
435
    }
436
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc