Completed
Pull Request — master (#6)
by
unknown
02:19
created

MakeUser   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 210
Duplicated Lines 2.86 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 5
dl 6
loc 210
rs 9.8
c 0
b 0
f 0
ccs 42
cts 42
cp 1

6 Methods

Rating   Name   Duplication   Size   Complexity  
F handle() 0 79 17
A validateEmail() 0 10 3
A additionalFields() 0 12 2
A normaliseValue() 6 16 4
A fileHandlerFactory() 0 14 3
A setPasswords() 0 9 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Dyrynda\Artisan\Console\Commands;
4
5
use Exception;
6
use SplFileInfo;
7
use Illuminate\Console\Command;
8
use Illuminate\Support\Facades\Password;
9
use Dyrynda\Artisan\Exceptions\MakeUserException;
10
use Dyrynda\Artisan\Exceptions\ImportFileException;
11
use Dyrynda\Artisan\BulkImport\BulkImportFileHandler;
12
13
class MakeUser extends Command
14
{
15
    /**
16
     * The name and signature of the console command.
17
     *
18
     * @var string
19
     */
20
    protected $signature = 'make:user
21
                                {--email=       : Set the email for the new user}
22
                                {--name=        : Set the name for the new user}
23
                                {--password=    : The password to set for the new user}
24
                                {--send-reset   : Send a password reset email for the new user}
25
                                {--fields=      : Additional database fields to set on the user}
26
                                {--force        : Create the user model circumventing guarded fields}
27
                                {--import-file= : Relative path and filename for a file to import users from. File name MUST contain the extension representing the type of file (Ex: ./path/to/file.csv)}
28
    ';
29
30
    /**
31
     * The console command description.
32
     *
33
     * @var string
34
     */
35
    protected $description = 'Create new application users';
36
37 9
    /**
38
     * Execute the console command.
39 9
     *
40 9
     * Handle creation of the new application user.
41 9
     *
42 9
     * @return void
43 9
     */
44
    public function handle()
45
    {
46 9
        $dataToProcess = [];
47
48 9
        try {
49
            $bulkImportFile = is_string($this->option('import-file')) ? $this->fileHandlerFactory($this->option('import-file')) : null;
50 7
51 7
            $modelCommand = $this->option('force') ? 'forceCreate' : 'create';
52 7
53
            if (! is_null($bulkImportFile)) {
54
                $dataToProcess = $bulkImportFile->getData();
55 7
56 3
                $sendReset = false;
57
58 3
                if (! in_array('password', array_keys($dataToProcess[0])) || $this->option('send-reset')) {
59
                    $sendReset = true;
60
                }
61 7
                $dataToProcess = $this->setPasswords($dataToProcess);
62
63 7
            } else {
64 2
                $email = $this->option('email');
65 2
                $name = $this->option('name') ?: '';
66
                $password = bcrypt($this->option('password') ?: str_random(32));
67 2
                $sendReset = ! $this->option('password') || $this->option('send-reset');
68
69 2
                $dataToProcess[0] = [
70
                    'email' => $email,
71 9
                    'name' => $name,
72
                    'password' => $password,
73
                ];
74
            }
75
76
            foreach ($dataToProcess as $dataRow) {
77
78
                $email = $dataRow['email'] ?? null;
79
80
                app('db')->beginTransaction();
81 9
82
                $this->validateEmail($email);
83 9
84 1
                app(config('auth.providers.users.model'))->{$modelCommand}(array_merge(
85
                    $dataRow,
86
                    $bulkImportFile ? [] : $this->additionalFields()
87 8
                ));
88 1
89
                if ($sendReset) {
90 7
                    Password::sendResetLink(compact('email'));
91
                }
92
93
                app('db')->commit();
94
            }
95
96
            if (count($dataToProcess)) {
97 7
                $createdMessage = $bulkImportFile
98
                    ? 'Created '.count($dataToProcess).' user(s).'
99 7
                    : "Created new user for email {$email}.";
0 ignored issues
show
Bug introduced by
The variable $email does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
100 4
101
                $passwordResetMessage = $bulkImportFile
102
                    ? 'Sent password reset emails.'
103 3
                    : "Sent password reset email to {$email}.";
104 3
105
                $this->info($createdMessage);
106 3
107 3
                if ($sendReset) {
108
                    $this->info($passwordResetMessage);
109
                }
110
111
            } else {
112
                $this->error('The user(s) were not created');
113
            }
114
115
        } catch (Exception $e) {
116 3
            $this->error($e->getMessage());
117
118 3
            $this->error('The user(s) were not created');
119 1
120
            app('db')->rollBack();
121
        }
122 2
    }
123 1
124
    /**
125
     * Determine if the given email address already exists.
126 1
     *
127 1
     * @param  string  $email
128
     * @return void
129
     *
130 1
     * @throws \Dyrynda\Artisan\Exceptions\MakeUserException
131
     */
132
    private function validateEmail($email)
133
    {
134
        if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
135
            throw MakeUserException::invalidEmail($email);
136
        }
137
138
        if (app(config('auth.providers.users.model'))->where('email', $email)->exists()) {
139
            throw MakeUserException::emailExists($email);
140
        }
141
    }
142
143
    /**
144
     * Return any additional database fields passed by the --fields option.
145
     *
146
     * @return array
147
     */
148
    private function additionalFields()
149
    {
150
        if (! $this->option('fields')) {
151
            return [];
152
        }
153
154
        return collect(explode(',', $this->option('fields')))->mapWithKeys(function ($field) {
155
            list($column, $value) = explode(':', $field);
156
157
            return [trim($column) => $this->normaliseValue($value)];
158
        })->toArray();
159
    }
160
161
    /**
162
     * Normalise the given (database) field input value.
163
     *
164
     * @param  mixed  $value
165
     * @return mixed
166
     */
167
    private function normaliseValue($value)
168
    {
169
        if ($value == 'null') {
170
            return;
171
        }
172
173 View Code Duplication
        if (in_array($value, [1, 'true', true], true)) {
174
            return true;
175
        }
176
177 View Code Duplication
        if (in_array($value, [0, 'false', false], true)) {
178
            return false;
179
        }
180
181
        return trim($value);
182
    }
183
184
    /**
185
     * Create file handler objects.
186
     *
187
     * @param string  $path
188
     * @return BulkImportFileHandler
189
     *
190
     * @throws \Dyrynda\Artisan\Exceptions\ImportFileException
191
     */
192
    private function fileHandlerFactory($path) : BulkImportFileHandler
193
    {
194
        if (! strpos($path, '.')) {
195
            throw ImportFileException::noExtension();
196
        }
197
198
        $file = new SplFileInfo($path);
199
200
        if (! class_exists($class = '\\Dyrynda\\Artisan\\BulkImport\\Handlers\\'.studly_case($file->getExtension()))) {
201
            throw ImportFileException::unsupported($file->getExtension());
202
        }
203
204
        return new $class($file);
205
    }
206
207
    /**
208
     * Add default password to data.
209
     *
210
     * @param array  $data
211
     * @return array
212
     */
213
    private function setPasswords($data)
214
    {
215
        return collect($data)->map(function ($row) {
216
            return array_merge(
217
                $row,
218
                ! isset($row['password']) ? ['password' => str_random(32)] : ['password' => bcrypt($row['password'])]
219
            );
220
        })->all();
221
    }
222
}
223