SeedCreate   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 182
Duplicated Lines 0 %

Test Coverage

Coverage 70.83%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 19
eloc 71
c 2
b 0
f 0
dl 0
loc 182
ccs 51
cts 72
cp 0.7083
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getSeedPath() 0 36 5
A configure() 0 15 1
A getCreateSeedDirectoryQuestion() 0 3 1
A getSelectSeedPathQuestion() 0 3 1
B execute() 0 76 11
1
<?php
2
3
/**
4
 * MIT License
5
 * For full license information, please view the LICENSE file that was distributed with this source code.
6
 */
7
8
namespace Phinx\Console\Command;
9
10
use Exception;
11
use InvalidArgumentException;
12
use Phinx\Config\NamespaceAwareInterface;
13
use Phinx\Util\Util;
14
use RuntimeException;
15
use Symfony\Component\Console\Input\InputArgument;
16
use Symfony\Component\Console\Input\InputInterface;
17
use Symfony\Component\Console\Input\InputOption;
18
use Symfony\Component\Console\Output\OutputInterface;
19
use Symfony\Component\Console\Question\ChoiceQuestion;
20
use Symfony\Component\Console\Question\ConfirmationQuestion;
21
22
class SeedCreate extends AbstractCommand
23
{
24
    /**
25
     * @var string
26
     */
27
    protected static $defaultName = 'seed:create';
28
29
    /**
30
     * {@inheritDoc}
31
     *
32
     * @return void
33
     */
34
    protected function configure()
35
    {
36
        parent::configure();
37
38
        $this->setDescription('Create a new database seeder')
39
            ->addArgument('name', InputArgument::REQUIRED, 'What is the name of the seeder?')
40
            ->addOption('path', null, InputOption::VALUE_REQUIRED, 'Specify the path in which to create this seeder')
41
            ->setHelp(sprintf(
42
                '%sCreates a new database seeder%s',
43
                PHP_EOL,
44
                PHP_EOL
45 35
            ));
46
47 35
        // An alternative template.
48
        $this->addOption('template', 't', InputOption::VALUE_REQUIRED, 'Use an alternative template');
49 35
    }
50 35
51 35
    /**
52 35
     * Get the confirmation question asking if the user wants to create the
53 35
     * seeds directory.
54 35
     *
55 35
     * @return \Symfony\Component\Console\Question\ConfirmationQuestion
56
     */
57 35
    protected function getCreateSeedDirectoryQuestion()
58 35
    {
59
        return new ConfirmationQuestion('Create seeds directory? [y]/n ', true);
60
    }
61
62
    /**
63
     * Get the question that allows the user to select which seed path to use.
64
     *
65
     * @param string[] $paths Paths
66
     * @return \Symfony\Component\Console\Question\ChoiceQuestion
67
     */
68
    protected function getSelectSeedPathQuestion(array $paths)
69
    {
70
        return new ChoiceQuestion('Which seeds path would you like to use?', $paths, 0);
71
    }
72
73
    /**
74
     * Returns the seed path to create the seeder in.
75
     *
76
     * @param \Symfony\Component\Console\Input\InputInterface $input Input
77
     * @param \Symfony\Component\Console\Output\OutputInterface $output Output
78
     * @throws \Exception
79
     * @return string
80
     */
81
    protected function getSeedPath(InputInterface $input, OutputInterface $output)
82
    {
83
        // First, try the non-interactive option:
84
        $path = $input->getOption('path');
85
86
        if (!empty($path)) {
87
            return $path;
88
        }
89
90 2
        $paths = $this->getConfig()->getSeedPaths();
91
92
        // No paths? That's a problem.
93 2
        if (empty($paths)) {
94
            throw new Exception('No seed paths set in your Phinx configuration file.');
95 2
        }
96
97
        $paths = Util::globAll($paths);
98
99 2
        if (empty($paths)) {
100
            throw new Exception(
101
                'You probably used curly braces to define seed path in your Phinx configuration file, ' .
102 2
                'but no directories have been matched using this pattern. ' .
103
                'You need to create a seed directory manually.'
104
            );
105
        }
106 2
107
        // Only one path set, so select that:
108 2
        if (count($paths) === 1) {
109
            return array_shift($paths);
110
        }
111
112
        /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */
113
        $helper = $this->getHelper('question');
114
        $question = $this->getSelectSeedPathQuestion($paths);
115
116
        return $helper->ask($input, $output, $question);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $helper->ask($input, $output, $question) also could return the type boolean|string[] which is incompatible with the documented return type string.
Loading history...
117 2
    }
118 2
119
    /**
120
     * Create the new seeder.
121
     *
122
     * @param \Symfony\Component\Console\Input\InputInterface $input Input
123
     * @param \Symfony\Component\Console\Output\OutputInterface $output Output
124
     * @throws \RuntimeException
125
     * @throws \InvalidArgumentException
126
     * @return int 0 on success
127
     */
128
    protected function execute(InputInterface $input, OutputInterface $output)
129
    {
130
        $this->bootstrap($input, $output);
131
132
        // get the seed path from the config
133
        $path = $this->getSeedPath($input, $output);
134
135
        if (!file_exists($path)) {
136
            /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */
137 2
            $helper = $this->getHelper('question');
138
            $question = $this->getCreateSeedDirectoryQuestion();
139 2
140
            if ($helper->ask($input, $output, $question)) {
141
                mkdir($path, 0755, true);
142 2
            }
143
        }
144 2
145
        $this->verifySeedDirectory($path);
146
147
        $path = realpath($path);
148
        $className = $input->getArgument('name');
149
150
        if (!Util::isValidPhinxClassName($className)) {
151
            throw new InvalidArgumentException(sprintf(
152
                'The seed class name "%s" is invalid. Please use CamelCase format',
153 2
                $className
154
            ));
155 2
        }
156 2
157
        // Compute the file path
158 2
        $filePath = $path . DIRECTORY_SEPARATOR . $className . '.php';
159 1
160 1
        if (is_file($filePath)) {
161
            throw new InvalidArgumentException(sprintf(
162 1
                'The file "%s" already exists',
163
                basename($filePath)
164
            ));
165
        }
166 1
167
        // Get the alternative template option from the command line.
168 1
        $altTemplate = $input->getOption('template');
169 1
170 1
        // Verify the alternative template file's existence.
171 1
        if ($altTemplate && !is_file($altTemplate)) {
172 1
            throw new InvalidArgumentException(sprintf(
173
                'The template file "%s" does not exist',
174
                $altTemplate
175
            ));
176 1
        }
177
178 1
        // Determine the appropriate mechanism to get the template
179 1
        // Load the alternative template if it is defined.
180
        $contents = file_get_contents($altTemplate ?: $this->getSeedTemplateFilename());
181 1
182 1
        $config = $this->getConfig();
183 1
        $namespace = $config instanceof NamespaceAwareInterface ? $config->getSeedNamespaceByPath($path) : null;
184 1
        $classes = [
185 1
            '$namespaceDefinition' => $namespace !== null ? ('namespace ' . $namespace . ';') : '',
186 1
            '$namespace' => $namespace,
187 1
            '$useClassName' => $config->getSeedBaseClassName(false),
188
            '$className' => $className,
189 1
            '$baseClassName' => $config->getSeedBaseClassName(true),
190
        ];
191
        $contents = strtr($contents, $classes);
192
193
        if (file_put_contents($filePath, $contents) === false) {
194
            throw new RuntimeException(sprintf(
195
                'The file "%s" could not be written to',
196 1
                $path
197 1
            ));
198 1
        }
199
200
        $output->writeln('<info>using seed base class</info> ' . $classes['$useClassName'], $this->verbosityLevel);
201
        $output->writeln('<info>created</info> ' . Util::relativePath($filePath), $this->verbosityLevel);
202
203
        return self::CODE_SUCCESS;
204
    }
205
}
206