Completed
Pull Request — master (#170)
by Simonas
03:06
created

SettingsManager::getAllProfilesNameList()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 15
rs 9.2
cc 4
eloc 8
nc 3
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 ONGR\ElasticsearchBundle\Result\Aggregation\AggregationValue;
15
use ONGR\ElasticsearchDSL\Aggregation\TermsAggregation;
16
use ONGR\ElasticsearchDSL\Aggregation\TopHitsAggregation;
17
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
18
use ONGR\ElasticsearchBundle\Service\Repository;
19
use ONGR\ElasticsearchBundle\Service\Manager;
20
use ONGR\SettingsBundle\Document\Setting;
21
22
/**
23
 * Class SettingsManager responsible for managing settings actions.
24
 */
25
class SettingsManager
26
{
27
    /**
28
     * Symfony event dispatcher.
29
     *
30
     * @var EventDispatcherInterface
31
     */
32
    private $eventDispatcher;
33
34
    /**
35
     * Elasticsearch manager which handles setting repository.
36
     *
37
     * @var Manager
38
     */
39
    private $manager;
40
41
    /**
42
     * Settings repository.
43
     *
44
     * @var Repository
45
     */
46
    private $repo;
47
48
    /**
49
     * Active profiles setting name.
50
     *
51
     * @var string
52
     */
53
    private $activeProfilesSetting;
54
55
    /**
56
     * @param Repository               $repo
57
     * @param EventDispatcherInterface $eventDispatcher
58
     */
59
    public function __construct(
60
        $repo,
61
        EventDispatcherInterface $eventDispatcher
62
    ) {
63
        $this->repo = $repo;
64
        $this->manager = $repo->getManager();
65
        $this->eventDispatcher = $eventDispatcher;
66
    }
67
68
    /**
69
     * @return string
70
     */
71
    public function getActiveProfilesSetting()
72
    {
73
        return $this->activeProfilesSetting;
74
    }
75
76
    /**
77
     * @param string $activeProfilesSetting
78
     */
79
    public function setActiveProfilesSetting($activeProfilesSetting)
80
    {
81
        $this->activeProfilesSetting = $activeProfilesSetting;
82
    }
83
84
    /**
85
     * Overwrites setting with given name.
86
     *
87
     * @param string       $name
88
     * @param array        $data
89
     * @param bool         $force
90
     *
91
     * @return Setting
92
     */
93
    public function create($name, array $data = [], $force = false)
94
    {
95
        $existingSetting = $this->get($name);
96
        if ($existingSetting && !$force) {
97
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by ONGR\SettingsBundle\Serv...SettingsManager::create of type ONGR\SettingsBundle\Document\Setting.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
98
        }
99
100
        if ($existingSetting && $force) {
101
            /** @var Setting $setting */
102
            $setting = $existingSetting;
103
        } else {
104
            $settingClass = $this->repo->getClassName();
105
            /** @var Setting $setting */
106
            $setting = new $settingClass();
107
        }
108
109
        $setting->setName($name);
110
        
111
        foreach ($data as $key => $value) {
112
            $setting->{'set'.ucfirst($key)}($value);
113
        }
114
115
        $this->manager->persist($setting);
116
        $this->manager->commit();
117
118
        return $setting;
119
    }
120
121
    /**
122
     * Overwrites setting with given name.
123
     *
124
     * @param string      $name
125
     * @param array       $data
126
     *
127
     * @return Setting
128
     */
129
    public function update($name, $data)
130
    {
131
        return $this->create($name, $data, true);
132
    }
133
134
    /**
135
     * Deletes a setting.
136
     *
137
     * @param string    $name
138
     */
139
    public function delete($name)
140
    {
141
        $setting = $this->repo->findOneBy(['name' => $name]);
142
        $this->repo->remove($setting->getId());
143
    }
144
145
    /**
146
     * Returns setting value.
147
     *
148
     * @param string $name
149
     * @param mixed  $default
150
     *
151
     * @return Setting
152
     */
153
    public function get($name, $default = null)
154
    {
155
        $setting = $this->repo->findOneBy(['name' => $name]);
156
157
        if ($setting) {
158
            return $setting;
159
        } else {
160
            return $default;
161
        }
162
    }
163
164
    public function getAllProfiles()
165
    {
166
        $profiles = [];
167
168
        $search = $this->repo->createSearch();
169
        $topHitsAgg = new TopHitsAggregation('documents', 10000);
170
        $termAgg = new TermsAggregation('profiles', 'profile');
171
        $termAgg->addAggregation($topHitsAgg);
172
        $search->addAggregation($termAgg);
173
174
        $result = $this->repo->execute($search);
175
176
        /** @var Setting $activeProfiles */
177
        $activeProfiles = $this->get($this->activeProfilesSetting, []);
178
179
        /** @var AggregationValue $agg */
180
        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...
181
            $settings = [];
182
            $docs = $agg->getAggregation('documents');
183
            foreach ($docs['hits']['hits'] as $doc) {
184
                $settings[] = $doc['_source']['name'];
185
            }
186
            $name = $agg->getValue('key');
187
            $profiles[] = [
188
                'active' => $activeProfiles ? in_array($agg->getValue('key'), $activeProfiles->getValue()) : false,
189
                'name' => $name,
190
                'settings' => implode(', ', $settings),
191
            ];
192
        }
193
194
        return $profiles;
195
    }
196
197
    public function getAllProfilesNameList($onlyActive = false)
198
    {
199
        $profiles = [];
200
        $allProfiles = $this->getAllProfiles();
201
202
        foreach ($allProfiles as $profile) {
203
            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...
204
                continue;
205
            }
206
207
            $profiles[] = $profile['name'];
208
        }
209
210
        return $profiles;
211
    }
212
}
213