Completed
Push — master ( 550846...8c8483 )
by Anton
09:53
created

ModelCommand::generate()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 55
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 55
rs 8.7752
c 0
b 0
f 0
cc 5
eloc 34
nc 9
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Bluz PHP Team
4
 * @link https://github.com/bluzphp/bluzman
5
 */
6
7
namespace Bluzman\Command\Generate;
8
9
use Bluz\Proxy\Db;
10
use Bluz\Validator\Validator as v;
11
use Bluzman\Input\InputArgument;
12
use Bluzman\Input\InputException;
13
use Bluzman\Generator;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Output\OutputInterface;
16
use Symfony\Component\Console\Question\ConfirmationQuestion;
17
18
/**
19
 * ModelCommand
20
 *
21
 * @package  Bluzman\Command
22
 */
23
class ModelCommand extends AbstractGenerateCommand
24
{
25
    /**
26
     * Command configuration
27
     */
28
    protected function configure()
29
    {
30
        $this
31
            // the name of the command (the part after "bin/bluzman")
32
            ->setName('generate:model')
33
            // the short description shown while running "php bin/bluzman list"
34
            ->setDescription('Generate a new model')
35
            // the full command description shown when running the command with
36
            // the "--help" option
37
            ->setHelp('This command allows you to generate models files')
38
        ;
39
40
        $this->addModelArgument();
41
42
        $table = new InputArgument('table', InputArgument::REQUIRED, 'Table name is required');
43
        $table->setValidator(
44
            v::string()->alphaNumeric('_')->noWhitespace()
45
        );
46
47
        $this->getDefinition()->addArgument($table);
48
    }
49
50
    /**
51
     * @param InputInterface $input
52
     * @param OutputInterface $output
53
     * @return void
54
     */
55
    protected function execute(InputInterface $input, OutputInterface $output)
56
    {
57
        try {
58
            $this->write("Running <info>generate:model</info> command");
59
60
            $model = $input->getArgument('model');
61
            $this->getDefinition()->getArgument('model')->validate($model);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Console\Input\InputArgument as the method validate() does only exist in the following sub-classes of Symfony\Component\Console\Input\InputArgument: Bluzman\Input\InputArgument. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
62
63
            $table = $input->getArgument('table');
64
            $this->getDefinition()->getArgument('table')->validate($table);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Console\Input\InputArgument as the method validate() does only exist in the following sub-classes of Symfony\Component\Console\Input\InputArgument: Bluzman\Input\InputArgument. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
65
66
            // generate directories and files
67
            $this->generate($input, $output);
68
69
            // verify it
70
            $this->verify($input, $output);
71
72
            $this->write("Model <info>{$model}</info> has been successfully created.");
73
74
        } catch (InputException $e) {
75
            $this->error("ERROR: {$e->getMessage()}");
76
        }
77
    }
78
79
    /**
80
     * @param InputInterface $input
81
     * @param OutputInterface $output
82
     * @return void
83
     * @throws InputException
84
     */
85
    protected function generate(InputInterface $input, OutputInterface $output)
86
    {
87
        $modelName = ucfirst($input->getArgument('model'));
88
        $tableName = $input->getArgument('table');
89
90
        if ($this->getApplication()->isModelExists($modelName)) {
91
            $helper = $this->getHelperSet()->get("question");
92
            $question = new ConfirmationQuestion(
93
                "\n<question>Model $modelName would be overwritten. y/N?</question>:\n> ",
94
                false
95
            );
96
97
            if (!$helper->ask($input, $output, $question)) {
98
                return;
99
            }
100
        }
101
102
        // generate table
103
        $tableFile = $this->getApplication()->getModelPath($modelName) .DS. 'Table.php';
104
105
        if (file_exists($tableFile)) {
106
            $this->comment("Table file <info>$modelName/Table.php</info> already exists");
107
        } else {
108
            $template = $this->getTemplate('TableTemplate');
109
            $template->setFilePath($tableFile);
110
            $template->setTemplateData([
111
                'model' => $modelName,
112
                'table' => $tableName,
113
                'primaryKey' => $this->getPrimaryKey($tableName)
114
            ]);
115
116
            $generator = new Generator\Generator($template);
117
            $generator->make();
118
        }
119
120
        // generate row
121
        $rowFile = $this->getApplication()->getModelPath($modelName) .DS. 'Row.php';
122
123
        if (file_exists($rowFile)) {
124
            $this->comment("Row file <info>$modelName/Row.php</info> already exists");
125
        } else {
126
            $template = $this->getTemplate('RowTemplate');
127
            $template->setFilePath($rowFile);
128
            $template->setTemplateData(
129
                [
130
                    'model' => $modelName,
131
                    'table' => $tableName,
132
                    'columns' => $this->getColumns($tableName)
133
                ]
134
            );
135
136
            $generator = new Generator\Generator($template);
137
            $generator->make();
138
        }
139
    }
140
141
    /**
142
     * @param InputInterface $input
143
     * @param OutputInterface $output
144
     * @return void
145
     * @throws \Bluzman\Generator\GeneratorException
146
     */
147
    public function verify(InputInterface $input, OutputInterface $output)
148
    {
149
        $modelPath = $this->getApplication()->getModelPath($input->getArgument('model'));
150
151
        $paths = [
152
            $modelPath . DS . 'Table.php',
153
            $modelPath . DS . 'Row.php'
154
        ];
155
156
        foreach ($paths as $path) {
157
            if (!$this->getFs()->exists($path)) {
158
                throw new Generator\GeneratorException("File `$path` is not exists");
159
            }
160
        }
161
    }
162
163
    /**
164
     * @todo move it to DB class
165
     * @return array
166
     */
167
    protected function getPrimaryKey($table)
168
    {
169
        $connect = Db::getOption('connect');
170
171
        return Db::fetchColumn(
172
            '
173
            SELECT COLUMN_NAME
174
            FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
175
            WHERE TABLE_SCHEMA = ?
176
              AND TABLE_NAME = ?
177
             AND CONSTRAINT_NAME = ?
178
            ',
179
            [$connect['name'], $table, 'PRIMARY']
180
        );
181
    }
182
183
    /**
184
     * @todo move it to DB class
185
     * @return array
186
     */
187
    protected function getColumns($table)
188
    {
189
        $connect = Db::getOption('connect');
190
191
        return Db::fetchAll(
192
            '
193
            SELECT 
194
              COLUMN_NAME as name,
195
              COLUMN_TYPE as type
196
            FROM INFORMATION_SCHEMA.COLUMNS
197
            WHERE TABLE_SCHEMA = ?
198
              AND TABLE_NAME = ?
199
            ',
200
            [$connect['name'], $table]
201
        );
202
    }
203
}
204