webchemistry / stimulus
Requires
- php: >=8.0
- doctrine/lexer: ^1.2
- nette/finder: ^2.5 || ^3.0
- nette/php-generator: ^3.5.0 || ^4.0.0
- typertion/php: ^1.0
- utilitte/php: ^1.5
- webchemistry/simple-json: ^1.0
Requires (Dev)
- latte/latte: ^3.0.0
- nette/application: ^3.1
- nette/di: ^3.0
- nette/forms: ^3.1
- phpstan/phpstan: ^1.8
README
composer require webchemistry/stimulus
extensions: - WebChemistry\Stimulus\DI\StimulusExtension
设置提取器和生成器
首先,设置提取器和生成器。支持命名空间标识符。命名空间
带注释的代码(以下为复制粘贴的代码)
require __DIR__ . '/vendor/autoload.php'; // directory with controllers, only *_controller.js and *-controller.js are extracted $extractor = new JavascriptSourceExtractor(__DIR__ . '/js/stimulus/controllers'); // optional, we want UI namespace instead of Ui $keywords = ['ui' => 'UI']; // generate classes as Stimulus\*\_*Controller, these classes we don't edit $originalClassNameConverter = new PrependClassNameConverter( 'Stimulus\\', new AppendClassNameConverter( new BaseClassNameConverter($keywords, fn (string $className) => '_' . $className), 'Controller', ), ); // We want edit these classes $emptyClassNameConverter = new PrependClassNameConverter( 'App\\Stimulus\\Controller\\', new AppendClassNameConverter( new BaseClassNameConverter($keywords), 'Controller', ), ); // for autocomplete_controller.js these controllers are generated // class Stimulus\_AutocompleteController // class App\Stimulus\Controller\AutocompleteController extends Stimulus\_AutocompleteController // for namespace/autocomplete_controller.js these controllers are generated // class Stimulus\Namespace\_AutocompleteController // class App\Stimulus\Controller\Namespace\AutocompleteController extends Stimulus\_AutocompleteController // Generator generates static methods $generator = new StaticClassStimulusControllerGenerator($extractor, $originalClassNameConverter); // Generator generates class with empty body and extends original class (Stimulus\*\_*Controller) $emptyGenerator = new EmptyClassStimulusControllerGenerator( $extractor, $emptyClassNameConverter, $originalClassNameConverter, ); // Files are written in app/generated/stimulus/*, Stimulus\ namespace have to be removed from path $writer = new FilesystemWriter(__DIR__ . '/app/generated/stimulus', 'Stimulus\\'); // Files are written in app/src/Stimulus/Controller/*, App\Stimulus\Controller\ namespace have to be removed from path, if file exists don't rewrite it $emptyWriter = new FilesystemWriter(__DIR__ . '/app/src/Stimulus/Controller', 'App\\Stimulus\\Controller\\', false); foreach ($generator->generate() as $generated) { $writer->write($generated); } foreach ($emptyGenerator->generate() as $generated) { $emptyWriter->write($generated); }
复制粘贴的代码
require __DIR__ . '/vendor/autoload.php'; $extractor = new JavascriptSourceExtractor(__DIR__ . '/js/stimulus/controllers'); $keywords = ['ui' => 'UI']; $originalClassNameConverter = new PrependClassNameConverter( 'Stimulus\\', new AppendClassNameConverter( new BaseClassNameConverter($keywords, fn (string $className) => '_' . $className), 'Controller', ), ); $emptyClassNameConverter = new PrependClassNameConverter( 'App\\Stimulus\\Controller\\', new AppendClassNameConverter( new BaseClassNameConverter($keywords), 'Controller', ), ); $generator = new StaticClassStimulusControllerGenerator($extractor, $originalClassNameConverter); $emptyGenerator = new EmptyClassStimulusControllerGenerator( $extractor, $emptyClassNameConverter, $originalClassNameConverter, ); $writer = new FilesystemWriter(__DIR__ . '/app/generated/stimulus', 'Stimulus\\'); $emptyWriter = new FilesystemWriter(__DIR__ . '/app/src/Stimulus/Controller', 'App\\Stimulus\\Controller\\', false); foreach ($generator->generate() as $generated) { $writer->write($generated); } foreach ($emptyGenerator->generate() as $generated) { $emptyWriter->write($generated); }
文件是如何生成的
类 JavascriptSourceExtractor
使用 JavaScript 注释进行生成。每个控制器必须用 @controller
标注,操作用 @action
标注,参数用 @param
标注,值、类和目标用 @property
标注。
my_controller.js
/** * @controller * * @property {String} stringValue * * @property {HTMLElement[]} itemTargets * @property {HTMLElement} resultsTarget * * @property {String} activeClass */ export default class extends Controller { static targets = ['results', 'item']; static values = { string: String, }; static classes = ['active']; /** * @action */ switch() { } }
这个 PHP 类是生成的
declare(strict_types = 1); /** * NOTE: This class is auto generated by file: my_controller.js * Do not edit the class manually */ namespace Stimulus; use WebChemistry\Stimulus\Type\StimulusAction; use WebChemistry\Stimulus\Type\StimulusController; use WebChemistry\Stimulus\Type\StimulusTarget; abstract class _MyController { final public const identifier = 'my'; public static function construct(string $stringValue, string $activeClass): StimulusController { return new StimulusController(self::identifier, [ 'stringValue' => $stringValue, 'activeClass' => $activeClass, ], []); } public static function itemTarget(): StimulusTarget { return new StimulusTarget(self::identifier, 'itemTarget'); } public static function switchAction(): StimulusAction { return new StimulusAction(self::identifier, 'switch', []); } }
默认情况下,每个属性都是必需的,如果我们想使其可选,我们有几种方法
- 在属性名末尾添加
?
:@property {String} stringValue?
- 在属性的第三个部分(选项)中添加
{ optional }
:@property {String} stringValue {optional}
- 添加 hasser
@property {Boolean} hasStringValue
JavaScript 类型与 PHP 类型
webchemisty/stimulus 为编写应用程序引入了更严格的环境。如今,每个人都使用静态分析(至少他们应该这样做),因此正确的类型至关重要。
库按照以下方式转换 JavaScript 类型
Number
=> int|float
通过选项(第三个部分)实现缩窄:{ number: int }
Array
和 Object
=> mixed[]
Boolean
=> bool
String
=> string
other
=> mixed
数组
String[]
=> string[]
Number[]
=> array<int|float>
缩窄: { number: float }
Bool[]
=> bool[]
other
=> mixed[]
自定义类型
有时我们需要覆盖注释类型 @param ...
和类型 method(... $type)
,为此有选项 type
和 commentType
,例如 { type: mixed, commentType: "array<string, resource>" }
操作参数
Stimulus 3 引入了 操作参数。对于生成,只需使用交集类型或对象类型。
export default class extends Controller { /** * @action * @param { { params: { value: String } } & PointerEvent} event */ switch(event) { const { value } = event.params; } /** * @action * @param { { params: { value: String } } } event */ switchTwo(event) { const { value } = event.params; } }
在 PHP 中的使用
use WebChemistry\Stimulus\Renderer\HtmlRenderer; $htmlAttributes = HtmlRenderer::render( App\Stimulus\Controller\MyController::construct('string', 'activeClass'), App\Stimulus\Controller\MyController::switchAction()->event('click'), ); // data-controller="my" data-my-string-value="string" data-my-active-class="active" data-action="click->my#switch" // or as array [attribute => value] HtmlRenderer::toArray(...);
在 Latte 中的使用
<div n:stimulus=" App\Stimulus\Controller\MyController::construct('string', 'activeClass'), App\Stimulus\Controller\MyController::switchAction()->event('click'), "></div>
控制器作为服务
有时我们想要注入其他服务
declare(strict_types = 1); namespace App\Stimulus\Controller; use Stimulus\_MyController as ParentController; use WebChemistry\Stimulus\Type\StimulusController; final class MyController extends ParentController { public function __construct( private LinkGenerator $linkGenerator, ) {} public function doConstruct(): StimulusController { return self::construct($this->linkGenerator->link('...')); } }