Skip to content

Commit

Permalink
Merge pull request #27 from craftcms/feature/behaviors
Browse files Browse the repository at this point in the history
Behaviors
  • Loading branch information
brandonkelly committed Mar 6, 2024
2 parents 4f2931e + 867e420 commit b1d7a00
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Release Notes for Craft Generator

## Unreleased
- Added the behavior generator. ([#27](https://github.com/craftcms/generator/pull/27))
- Generator now prompts for new modules’ names. ([#28](https://github.com/craftcms/generator/pull/28))
- Added the `--with-strict-types` option. ([#29](https://github.com/craftcms/generator/pull/29))
- Fixed issues with the default `getTriggerHtml()` method output for generated element actions. ([#30](https://github.com/craftcms/generator/pull/30))
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ You can create new system components using the following commands:

```sh
php craft make asset-bundle
php craft make behavior
php craft make command
php craft make controller
php craft make element-action
Expand Down
128 changes: 128 additions & 0 deletions src/generators/Behavior.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php
/**
* @link https://craftcms.com/
* @copyright Copyright (c) Pixel & Tonic, Inc.
* @license https://craftcms.github.io/license/
*/

namespace craft\generator\generators;

use craft\events\DefineBehaviorsEvent;
use craft\helpers\ArrayHelper;
use Nette\PhpGenerator\PhpNamespace;
use craft\generator\BaseGenerator;
use ReflectionClass;
use yii\base\Behavior as BaseBehavior;
use yii\helpers\Inflector;

/**
* Creates a new behavior.
*
* When a class with an `EVENT_DEFINE_BEHAVIORS` event is provided, the generator will attempt to add an event handler that attaches the behavior.
*/
class Behavior extends BaseGenerator
{
private string $className;
private string $namespace;
private ?string $targetClass = null;
private string $displayName;

public function run(): bool
{
$this->className = $this->classNamePrompt('Behavior name:', [
'required' => true,
]);

$this->namespace = $this->namespacePrompt('Behavior namespace:', [
'default' => "$this->baseNamespace\\behaviors",
]);

$this->targetClass = $this->classPrompt('Target class (optional):', [
'ensureExists' => true,
'validator' => function(string $class, &$error) {
if (!class_exists($class)) {
$error = "$class does not exist.";
return false;
}
if (!(new ReflectionClass($class))->hasConstant('EVENT_DEFINE_BEHAVIORS')) {
$error = "$class doesn’t define an EVENT_DEFINE_BEHAVIORS event.";
return false;
}
return true;
},
]);

$this->displayName = Inflector::camel2words($this->className);

$namespace = (new PhpNamespace($this->namespace))
->addUse(BaseBehavior::class);

// `use` the class we want to target to make events and docblocks easier to grok:
if ($this->targetClass) {
$namespace->addUse($this->targetClass);
}

$class = $this->createClass($this->className, BaseBehavior::class, [
self::CLASS_METHODS => $this->methods(),
]);
$namespace->add($class);

$comment = "$this->displayName behavior";

// If a class was chosen, add the @property tag:
if ($this->targetClass) {
$classParts = explode('\\', $this->targetClass);
$className = end($classParts);
$comment .= "\n\n" . "@property $className \$owner";
}

$class->setComment($comment);

$this->writePhpClass($namespace);

$message = "**Behavior created!**";

if (
$this->targetClass &&
$this->isForModule() &&
!$this->addRegistrationEventHandlerCode(
$this->targetClass,
'EVENT_DEFINE_BEHAVIORS',
"$this->namespace\\$this->className",
$fallbackExample,
false,
DefineBehaviorsEvent::class,
'behaviors',
)
) {
$moduleFile = $this->moduleFile();
$message .= "\n" . <<<MD
Add the following code to `$moduleFile` to attach your new behavior to all instances of `$this->targetClass`:
```
$fallbackExample
```
MD;
}

if (!$this->targetClass) {
$message .= "\n" . <<<MD
You can register your behavior from a component class’s `behaviors()`/`defineBehaviors()` method, or via an `EVENT_DEFINE_BEHAVIORS` event.
MD;
}

$this->command->success($message);
return true;
}

private function methods(): array
{
return [
'events' => <<<PHP
return [
// ...
];
PHP,
];
}
}

0 comments on commit b1d7a00

Please sign in to comment.