Completed
Push — master ( b6ba3d...a4127c )
by Simonas
8s
created

SettingsManager::has()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 11
rs 9.4285
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
/*
4
 * This file is part of the ONGR package.
5
 *
6
 * (c) NFQ Technologies UAB <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ONGR\SettingsBundle\Service;
13
14
use Doctrine\Common\Cache\CacheProvider;
15
use ONGR\CookiesBundle\Cookie\Model\GenericCookie;
16
use ONGR\ElasticsearchBundle\Result\Aggregation\AggregationValue;
17
use ONGR\ElasticsearchDSL\Aggregation\TermsAggregation;
18
use ONGR\ElasticsearchDSL\Aggregation\TopHitsAggregation;
19
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
20
use ONGR\ElasticsearchBundle\Service\Repository;
21
use ONGR\ElasticsearchBundle\Service\Manager;
22
use ONGR\SettingsBundle\Document\Setting;
23
24
/**
25
 * Class SettingsManager responsible for managing settings actions.
26
 */
27
class SettingsManager
28
{
29
    /**
30
     * Symfony event dispatcher.
31
     *
32
     * @var EventDispatcherInterface
33
     */
34
    private $eventDispatcher;
35
36
    /**
37
     * Elasticsearch manager which handles setting repository.
38
     *
39
     * @var Manager
40
     */
41
    private $manager;
42
43
    /**
44
     * Settings repository.
45
     *
46
     * @var Repository
47
     */
48
    private $repo;
49
50
    /**
51
     * Cache pool container.
52
     *
53
     * @var CacheProvider
54
     */
55
    private $cache;
56
57
    /**
58
     * Cookie storage for active cookies.
59
     *
60
     * @var GenericCookie
61
     */
62
    private $activeProfilesCookie;
63
64
    /**
65
     * Active profiles setting name to store in the cache engine.
66
     *
67
     * @var string
68
     */
69
    private $activeProfilesSettingName;
70
71
    /**
72
     * @param Repository               $repo
73
     * @param EventDispatcherInterface $eventDispatcher
74
     */
75
    public function __construct(
76
        $repo,
77
        EventDispatcherInterface $eventDispatcher
78
    ) {
79
        $this->repo = $repo;
80
        $this->manager = $repo->getManager();
81
        $this->eventDispatcher = $eventDispatcher;
82
    }
83
84
    /**
85
     * @return CacheProvider
86
     */
87
    public function getCache()
88
    {
89
        return $this->cache;
90
    }
91
92
    /**
93
     * @param CacheProvider $cache
94
     */
95
    public function setCache($cache)
96
    {
97
        $this->cache = $cache;
98
    }
99
100
    /**
101
     * @return GenericCookie
102
     */
103
    public function getActiveProfilesCookie()
104
    {
105
        return $this->activeProfilesCookie;
106
    }
107
108
    /**
109
     * @param GenericCookie $activeProfilesCookie
110
     */
111
    public function setActiveProfilesCookie($activeProfilesCookie)
112
    {
113
        $this->activeProfilesCookie = $activeProfilesCookie;
114
    }
115
116
    /**
117
     * @return string
118
     */
119
    public function getActiveProfilesSettingName()
120
    {
121
        return $this->activeProfilesSettingName;
122
    }
123
124
    /**
125
     * @param string $activeProfilesSettingName
126
     */
127
    public function setActiveProfilesSettingName($activeProfilesSettingName)
128
    {
129
        $this->activeProfilesSettingName = $activeProfilesSettingName;
130
    }
131
132
    /**
133
     * Creates setting.
134
     *
135
     * @param array        $data
136
     *
137
     * @return Setting
138
     */
139
    public function create(array $data = [])
140
    {
141
        $data = array_filter($data);
142
        if (!isset($data['name']) || !isset($data['type'])) {
143
            throw new \LogicException('Missing one of the mandatory field!');
144
        }
145
146
        if (!isset($data['value'])) {
147
            $data['value'] = 0;
148
        }
149
150
        $name = $data['name'];
151
        $existingSetting = $this->get($name);
152
153
        if ($existingSetting) {
154
            throw new \LogicException(sprintf('Setting %s already exists.', $name));
155
        }
156
157
        $settingClass = $this->repo->getClassName();
158
        /** @var Setting $setting */
159
        $setting = new $settingClass();
160
161
        #TODO Introduce array populate function in Setting document instead of this foreach.
162
        foreach ($data as $key => $value) {
163
            $setting->{'set'.ucfirst($key)}($value);
164
        }
165
166
        $this->manager->persist($setting);
167
        $this->manager->commit();
168
169
        return $setting;
170
    }
171
172
    /**
173
     * Overwrites setting parameters with given name.
174
     *
175
     * @param string      $name
176
     * @param array       $data
177
     *
178
     * @return Setting
179
     */
180
    public function update($name, $data = [])
181
    {
182
        $setting = $this->get($name);
183
184
        if (!$setting) {
185
            throw new \LogicException(sprintf('Setting %s not exist.', $name));
186
        }
187
188
        #TODO Add populate function to document class
189
        foreach ($data as $key => $value) {
190
            $setting->{'set'.ucfirst($key)}($value);
191
        }
192
193
        $this->manager->persist($setting);
194
        $this->manager->commit();
195
196
        return $setting;
197
    }
198
199
    /**
200
     * Deletes a setting.
201
     *
202
     * @param string    $name
203
     *
204
     * @return array
205
     */
206
    public function delete($name)
207
    {
208
        $setting = $this->repo->findOneBy(['name' => $name]);
209
        return $this->repo->remove($setting->getId());
210
    }
211
212
    /**
213
     * Returns setting object.
214
     *
215
     * @param string $name
216
     *
217
     * @return Setting
218
     */
219
    public function get($name)
220
    {
221
        /** @var Setting $setting */
222
        $setting = $this->repo->findOneBy(['name' => $name]);
223
224
        return $setting;
225
    }
226
227
    /**
228
     * Returns setting object.
229
     *
230
     * @param string $name
231
     *
232
     * @return bool
233
     */
234
    public function has($name)
235
    {
236
        /** @var Setting $setting */
237
        $setting = $this->repo->findOneBy(['name' => $name]);
238
239
        if ($setting) {
240
            return true;
241
        }
242
243
        return false;
244
    }
245
246
    /**
247
     * Get setting value by current active profiles setting.
248
     *
249
     * @param string $name
250
     * @param bool $default
251
     *
252
     * @return mixed
253
     */
254
    public function getValue($name, $default = null)
255
    {
256
        $setting = $this->get($name);
257
258
        if ($setting) {
259
            return $setting->getValue();
260
        }
261
262
        return $default;
263
    }
264
265
    /**
266
     * Get setting value by checking also from cache engine.
267
     *
268
     * @param string $name
269
     * @param bool   $checkWithActiveProfiles Checks if setting is in active profile.
270
     *
271
     * @return mixed
272
     */
273
    public function getCachedValue($name, $checkWithActiveProfiles = true)
274
    {
275
        if ($this->cache->contains($name)) {
276
            $setting = $this->cache->fetch($name);
277
        } elseif ($this->has($name)) {
278
            $settingDocument = $this->get($name);
279
            $setting = [
280
                'value' => $settingDocument->getValue(),
281
                'profiles' => $settingDocument->getProfile(),
282
            ];
283
            $this->cache->save($name, $setting);
284
        } else {
285
            return null;
286
        }
287
288
        if ($checkWithActiveProfiles) {
289
            $profilesFromEs = $this->getActiveProfiles();
290
            $profilesFromCookie = (array)$this->activeProfilesCookie->getValue();
291
292
            $profiles = array_merge($profilesFromEs, $profilesFromCookie);
293
            $settingProfiles = $setting['profiles'];
294
            if (count(array_intersect($profiles, $settingProfiles))) {
295
                return $setting['value'];
296
            }
297
        }
298
299
        return $setting['value'];
300
    }
301
302
    /**
303
     * Get all full profile information.
304
     *
305
     * @return array
306
     */
307
    public function getAllProfiles()
308
    {
309
        $profiles = [];
310
311
        $search = $this->repo->createSearch();
312
        $topHitsAgg = new TopHitsAggregation('documents', 20);
313
        $termAgg = new TermsAggregation('profiles', 'profile');
314
        $termAgg->addAggregation($topHitsAgg);
315
        $search->addAggregation($termAgg);
316
317
        $result = $this->repo->execute($search);
318
319
        /** @var Setting $activeProfiles */
320
        $activeProfiles = $this->getValue($this->activeProfilesSettingName, []);
0 ignored issues
show
Documentation introduced by
array() is of type array, but the function expects a boolean|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
321
322
        /** @var AggregationValue $agg */
323
        foreach ($result->getAggregation('profiles') as $agg) {
0 ignored issues
show
Bug introduced by
The method getAggregation does only exist in ONGR\ElasticsearchBundle\Result\DocumentIterator, but not in ONGR\ElasticsearchBundle\Result\RawIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
324
            $settings = [];
325
            $docs = $agg->getAggregation('documents');
326
            foreach ($docs['hits']['hits'] as $doc) {
327
                $settings[] = $doc['_source']['name'];
328
            }
329
            $name = $agg->getValue('key');
330
            $profiles[] = [
331
                'active' => $activeProfiles ? in_array($agg->getValue('key'), $activeProfiles) : false,
332
                'name' => $name,
333
                'settings' => implode(', ', $settings),
334
            ];
335
        }
336
337
        return $profiles;
338
    }
339
340
    /**
341
     * Get profile names, optionally can return only active ones.
342
     *
343
     * @param bool $onlyActive
344
     *
345
     * @return array
346
     */
347
    public function getAllProfilesNameList($onlyActive = false)
348
    {
349
        $profiles = [];
350
        $allProfiles = $this->getAllProfiles();
351
352
        foreach ($allProfiles as $profile) {
353
            if ($onlyActive and !$profile['active']) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
354
                continue;
355
            }
356
357
            $profiles[] = $profile['name'];
358
        }
359
360
        return $profiles;
361
    }
362
363
    /**
364
     * Returns cached active profiles names list.
365
     *
366
     * @return array
367
     */
368
    public function getActiveProfiles()
369
    {
370
        if ($this->cache->contains($this->activeProfilesSettingName)) {
371
            return $this->cache->fetch($this->activeProfilesSettingName);
372
        }
373
374
        $profiles = $this->getAllProfilesNameList(true);
375
376
        $this->cache->save($this->activeProfilesSettingName, $profiles);
377
378
        return $profiles;
379
    }
380
}
381