Skip to content

Commit 9fb52dd

Browse files
committed
[#33] Adds cli generator and tests
1 parent 5bdc579 commit 9fb52dd

File tree

10 files changed

+340
-2
lines changed

10 files changed

+340
-2
lines changed

src/Module.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ class Module extends \yii\base\Module
3737
public function init()
3838
{
3939
Craft::setAlias('@viget/base', __DIR__);
40-
$this->controllerNamespace = 'viget\base\controllers';
40+
41+
if (Craft::$app->getRequest()->getIsConsoleRequest()) {
42+
$this->controllerNamespace = 'viget\base\console\controllers';
43+
} else {
44+
$this->controllerNamespace = 'viget\base\controllers';
45+
}
4146

4247
parent::init();
4348
self::$instance = $this;
@@ -206,6 +211,9 @@ private function _loadConfig()
206211
'volume' => 'partsKit',
207212
'theme' => 'light',
208213
],
214+
'scaffold' => [
215+
'templatePrefix' => null, // used for test suite
216+
],
209217
'tailwind' => [
210218
'configPath' => Craft::getAlias('@config/tailwind/tailwind.json'),
211219
],
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
<?php
2+
3+
namespace viget\base\console\controllers;
4+
5+
use Craft;
6+
use craft\console\Controller;
7+
use craft\helpers\FileHelper;
8+
use craft\services\Sections;
9+
10+
use viget\base\Module;
11+
use yii\console\ExitCode;
12+
use yii\helpers\Console;
13+
14+
class GenerateController extends Controller
15+
{
16+
17+
// TODO - pass variants to make multiple files `default, dark, two-up`
18+
// TODO - make multiple partials at once
19+
20+
private $templatesDir;
21+
private $scaffoldTemplatesDir;
22+
private $partsKitDir;
23+
private $defaultFileExtension;
24+
25+
public function init()
26+
{
27+
$templatePrefix = self::getConfig('templatePrefix', 'scaffold');
28+
$this->templatesDir = Craft::$app->path->getSiteTemplatesPath() . $templatePrefix;
29+
$this->scaffoldTemplatesDir = FileHelper::normalizePath(__dir__ . '/../../templates/_scaffold');
30+
$this->partsKitDir = self::getConfig('directory');
31+
$this->defaultFileExtension = '.html';
32+
parent::init();
33+
}
34+
35+
36+
/**
37+
* Get a config item either the default or from the config file
38+
*
39+
* @param string $key
40+
* @return string|array|null
41+
*/
42+
public static function getConfig(string $key, string $section = 'partsKit')
43+
{
44+
return Module::$config[$section][$key] ?? null;
45+
}
46+
47+
public function actionEntrytypes(string $handle)
48+
{
49+
$section = Craft::$app->sections->getSectionByHandle($handle);
50+
if(!$section) {
51+
$this->stdout("No section found for $handle" . PHP_EOL, Console::FG_RED);
52+
return ExitCode::UNSPECIFIED_ERROR;
53+
}
54+
55+
$settings = current($section->getSiteSettings()) ?? null;
56+
57+
if(!$settings) {
58+
$this->stdout("No settings found for section $handle" . PHP_EOL, Console::FG_RED);
59+
return ExitCode::UNSPECIFIED_ERROR;
60+
}
61+
62+
$templateParts = self::_splitAndSanitizeInput($settings->template);
63+
$path = $templateParts['path'];
64+
65+
$templateContent = self::_compileTemplate(
66+
$this->_loadScaffoldTemplate('template.html'),
67+
[
68+
'layout' => self::getConfig('layout'),
69+
]
70+
);
71+
72+
$structureIndex = self::_compileTemplate(
73+
$this->_loadScaffoldTemplate('structure.html'),
74+
[
75+
'path' => $templateParts['path'],
76+
]
77+
);
78+
79+
// Create the index file for our structure
80+
$this->_writeFile( $path . '/index', $structureIndex);
81+
82+
foreach($section->entryTypes as $entryType) {
83+
$this->_writeFile($path . '/' . $entryType->handle, $templateContent);
84+
}
85+
86+
return ExitCode::OK;
87+
}
88+
89+
public function actionTemplate($templatePath)
90+
{
91+
$parts = self::_splitAndSanitizeInput($templatePath);
92+
$path = $parts['path'];
93+
$filename = $parts['filename'];
94+
95+
$templateContent = self::_compileTemplate(
96+
$this->_loadScaffoldTemplate('template.html'),
97+
[
98+
'layout' => self::getConfig('layout'),
99+
]
100+
);
101+
102+
// Write template file
103+
// TODO make _elements a config option
104+
$this->_writeFile('_elements/' . $path . '/' . $filename, $templateContent);
105+
106+
return ExitCode::OK;
107+
}
108+
109+
public function actionPartial($partialPath, ?string $variantsInput = null)
110+
{
111+
$parts = self::_splitAndSanitizeInput($partialPath);
112+
$path = $parts["path"];
113+
$filename = $parts['filename'];
114+
115+
$variants = [
116+
'default',
117+
];
118+
119+
if($variantsInput) {
120+
$variants = array_merge($variants, explode(',', $variantsInput));
121+
}
122+
123+
$partialContent = $this->_loadScaffoldTemplate('partial.html');
124+
$partsKitContent = $this->_loadScaffoldTemplate('partial-parts-kit.html');
125+
126+
$partsKitContentCompiled = self::_compileTemplate($partsKitContent, [
127+
'partialPath' => $partialPath,
128+
]);
129+
130+
// Create file in _partials dir
131+
// TODO make _partials a config option
132+
$this->_writeFile('_partials/' . $path . '/' . $filename, $partialContent);
133+
134+
foreach($variants as $variant) {
135+
$this->_writeFile($this->partsKitDir . '/' . $filename . '/' . $variant, $partsKitContentCompiled);
136+
}
137+
138+
return ExitCode::OK;
139+
}
140+
141+
private function _loadScaffoldTemplate(string $path)
142+
{
143+
return file_get_contents($this->scaffoldTemplatesDir . '/' . $path);
144+
}
145+
146+
private function _writeFile(string $path, string $content)
147+
{
148+
$fullPath = $this->templatesDir . '/' . $path . $this->defaultFileExtension;
149+
150+
if (file_exists($fullPath)) {
151+
$this->stdout("File already exists for $fullPath" . PHP_EOL, Console::FG_RED);
152+
return;
153+
}
154+
155+
FileHelper::writeToFile($fullPath, $content);
156+
}
157+
158+
// TODO - unit test
159+
private static function _removeFileExtension(string $filename): string
160+
{
161+
$explode = explode('.', $filename);
162+
if(count($explode) === 1) {
163+
return $filename;
164+
}
165+
166+
return implode('.', array_slice($explode, 0, -1));
167+
}
168+
169+
// TODO - unit test
170+
private static function _splitAndSanitizeInput(string $input): array
171+
{
172+
$split = explode(DIRECTORY_SEPARATOR, $input);
173+
$path = implode(DIRECTORY_SEPARATOR, array_slice($split, 0, -1));
174+
$filename = self::_removeFileExtension(end($split));
175+
176+
return [
177+
'path' => FileHelper::normalizePath($path),
178+
'filename' => FileHelper::sanitizeFilename($filename),
179+
];
180+
}
181+
182+
// TODO - unit test
183+
private static function _compileTemplate(string $template, array $vars): string
184+
{
185+
$patterns = array_map(function ($item) {
186+
return "/%%$item%%/";
187+
}, array_values(array_flip($vars)));
188+
189+
$replacements = array_values($vars);
190+
191+
return preg_replace($patterns, $replacements, $template);
192+
}
193+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{% extends 'viget-base/_layouts/parts-kit' %}
2+
3+
{% block main %}
4+
{{ partial('_partials/%%partialPath%%', {
5+
key: value,
6+
}) }}
7+
{% endblock %}

src/templates/_scaffold/partial.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{% set prop = prop ?? null %}
2+
3+
<!-- Your partial -->
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{{ partial("%%path%%/#{entry.type.handle}", {
2+
entry: entry,
3+
}) }}

src/templates/_scaffold/template.html

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{% do craft.app.elements.eagerLoadElements(
2+
className(entry),
3+
[entry],
4+
[
5+
'assetField',
6+
'matrixField',
7+
]
8+
) %}
9+
10+
{% from '_macros' import macro %}
11+
12+
{% extends '%%layout%%' %}
13+
14+
{% set title = entry.title %}
15+
16+
{% block content %}
17+
<h1>{{ title }}</h1>
18+
{% endblock %}

tests/_craft/config/viget.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<?php
22

33
return [
4+
'scaffold' => [
5+
'templatePrefix' => '/temp',
6+
],
47
'partsKit' => [
58
'directory' => 'parts-kit',
69
'layout' => '_layouts/app',

tests/fixtures/data/entry-types.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,18 @@
77
'fieldLayoutId' => null,
88
'name' => 'Test Entry Type 1',
99
'handle' => 'testEntryType1',
10-
'titleLabel' => 'Title',
1110
'titleFormat' => null,
1211
'sortOrder' => '1',
1312
'uid' => 'entry-type-1000------------------uid'
1413
],
14+
[
15+
'id' => '1001',
16+
'sectionId' => '1000',
17+
'fieldLayoutId' => null,
18+
'name' => 'Test Entry Type 2',
19+
'handle' => 'testEntryType2',
20+
'titleFormat' => null,
21+
'sortOrder' => '2',
22+
'uid' => 'entry-type-1001------------------uid'
23+
],
1524
];

tests/fixtures/data/section-settings.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
'uriFormat' => null,
99
'template' => null,
1010
'enabledByDefault' => true,
11+
'template' => '_elements/test-section-1/index',
1112
],
1213
];
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
namespace vigetbasetests\unit\console;
3+
4+
use Craft;
5+
use craft\helpers\FileHelper;
6+
use craft\test\console\ConsoleTest;
7+
use viget\base\Module;
8+
use vigetbasetests\fixtures\EntryTypesFixture;
9+
use vigetbasetests\fixtures\SectionsFixture;
10+
use yii\base\InvalidConfigException;
11+
use yii\console\ExitCode;
12+
13+
class GenerateControllerTest extends ConsoleTest
14+
{
15+
private $siteTemplatesPath;
16+
private $templatesRoot;
17+
private $partialsRoot;
18+
private $partsKitRoot;
19+
20+
public function _fixtures(): array
21+
{
22+
return [
23+
'entryTypes' => [
24+
'class' => EntryTypesFixture::class,
25+
],
26+
'sections' => [
27+
'class' => SectionsFixture::class,
28+
],
29+
];
30+
}
31+
32+
protected function _before()
33+
{
34+
$templatePath = Craft::$app->getPath()->getSiteTemplatesPath();
35+
$templatePrefix = Module::$config['scaffold']['templatePrefix'];
36+
$partsKitDir = Module::$config['partsKit']['directory'];
37+
$this->siteTemplatesPath = $templatePath . $templatePrefix;
38+
$this->partialsRoot = $this->siteTemplatesPath . '/_partials'; // TODO - config?
39+
$this->templatesRoot = $this->siteTemplatesPath . '/_elements'; // TODO - config?
40+
$this->partsKitRoot = $this->siteTemplatesPath . '/' . $partsKitDir;
41+
}
42+
43+
protected function _after()
44+
{
45+
FileHelper::removeDirectory($this->siteTemplatesPath);
46+
}
47+
48+
/**
49+
* @throws InvalidConfigException
50+
*/
51+
public function testCreatePartial()
52+
{
53+
$this->consoleCommand('viget-base/generate/partial', [
54+
'foo',
55+
])
56+
->exitCode(ExitCode::OK)
57+
->run();
58+
59+
$this->assertFileExists($this->partialsRoot . '/foo.html');
60+
$this->assertFileExists($this->partsKitRoot . '/foo/default.html');
61+
}
62+
63+
/**
64+
* @throws InvalidConfigException
65+
*/
66+
public function testCreateTemplate()
67+
{
68+
$this->consoleCommand('viget-base/generate/template', [
69+
'foo',
70+
])
71+
->exitCode(ExitCode::OK)
72+
->run();
73+
74+
$this->assertFileExists($this->templatesRoot . '/foo.html');
75+
}
76+
77+
/**
78+
* @throws InvalidConfigException
79+
*/
80+
public function testCreateEntryTypes()
81+
{
82+
$this->consoleCommand('viget-base/generate/entrytypes', [
83+
'testSection1',
84+
])
85+
->exitCode(ExitCode::OK)
86+
->run();
87+
88+
$this->assertFileExists($this->templatesRoot . '/test-section-1/index.html');
89+
$this->assertFileExists($this->templatesRoot . '/test-section-1/testEntryType1.html');
90+
$this->assertFileExists($this->templatesRoot . '/test-section-1/testEntryType2.html');
91+
}
92+
93+
}

0 commit comments

Comments
 (0)