Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generate the form attributes based on validations #70

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions generators/crud/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
use yii\db\BaseActiveRecord;
use yii\db\Schema;
use yii\gii\CodeFile;
use yii\helpers\ArrayHelper;
use yii\helpers\Inflector;
use yii\helpers\VarDumper;
use yii\validators\Validator;
use yii\web\Controller;

/**
Expand Down Expand Up @@ -548,4 +550,133 @@ public function getColumnNames()
return $model->attributes();
}
}

public function validatorPriority(Validator $validator)
{
static $priorities = [
'yii\validators\NumberValidator' => 0,
'yii\validators\EmailValidator' => 1,
'yii\validators\BooleanValidator' => 2,
'yii\validators\RangeValidator' => 3,
'faryshta\\validators\\EnumValidator' => 4,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it do in the core? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will take it out then. I was also thinking on a way to do it extendable so plugins could create their generators on gii without making an entire new gii.

any idea?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it non-static then and move it to module level.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't understand this tip. can you explain further?

specially on how to let other modules or libraries define their own associated widgets for the validators they define such as my enum validator

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've suggested taking static $priorities out from the function to the class level i.e. public $validators or something like that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way it could be configured externally.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also it doesn't really make sense to specify priorities as integers. Order of values in array should be enough.

'yii\validators\DateValidator' => 5,
'yii\validators\CapchaValidator' => 6,
'yii\validators\FileValidator' => 7,
'yii\validators\ImageValidator' => 8,
'yii\validators\ExistValidator' => 9,
];

return ArrayHelper::getValue($priorities, $validator->className(), -1);
}

public function generateValidatorField($attribute, $validator)
{
// space tab
static $t = [
'',
' ',
' ',
' ',
' ',
];

// code to generate each attribute based on the validators saved sofar.
switch ($validator->className()) {

case 'yii\validators\NumberValidator':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of hardcoding we can introduce something like NumberValidatorFieldGenerator. This way introducing our own generators will be possible. These could be configured via what's called $priorities now.

return "\$form->field(\$model, '$attribute')->input('number')";
break;

case 'yii\validators\EmailValidator':
return "\$form->field(\$model, '$attribute')->input('email')";
break;

case 'yii\validators\BooleanValidator':
return "\$form->field(\$model, '$attribute')->checkbox()";
break;

case 'yii\validators\RangeValidator':
$field = "\$form->field(\$model, '$attribute')->dropDownList(\n"
. "$t[2][\n";

foreach ($validator->range as $key) {
$field .= "{$t[3]}'$key' => '$key',\n";
}

return $field . "{$t[2]}],\n"
. "{$t[2]}['prompt' => "
. "\$model->getAttributeLabel('$attribute')]\n"
. "{$t[1]})";
break;

case 'faryshta\validators\EnumValidator':
$params = '';
if (!empty($validator->enumClass)) {
$params .= "'enumClass' => '{$validator->enumClass}',";
}
if (!empty($validator->enumName)) {
$params .= "'enumName' => '{$validator->enumName}',";
}
if (!empty($params)) {
$params = ",\n{$t[2]}[$params]";
}

return "\$form->field(\$model, '$attribute')->widget(\n"
. "{$t[2]}'faryshta\\widgets\\EnumDropdown'$params\n"
. "{$t[1]})";
break;

case 'yii\validators\DateValidator':
return "\$form->field(\$model, '$attribute')->widget(\n"
. "{$t[2]}'yii\jui\Datepicker',\n"
. "{$t[2]}['dateFormat' => '{$validator->format}'],\n"
. "{$t[1]})";
break;

case 'yii\validators\CapchaValidator':
return "\$form->field(\$model, '$attribute')->widget('yii\captcha\Captcha')";
break;

case 'yii\validators\FileValidator':
case 'yii\validators\ImageValidator':
return "\$form->field(\$model, '$attribute')->fileInput()";
break;

case 'yii\validators\ExistValidator':
$targetAttribute = empty($validator->targetAttribute)
? $attribute
: $validator->targetAttribute;

// if the attribute is an array for example ['id' => 'user_id']
// then the first key will be used as targetAttribute
if (is_array($targetAttribute)) {
$targetAttribute = array_keys($targetAttribute);
$targetAttribute = $targetAttribute[0];
}

$targetClass = empty($validator->targetClass)
? $model->className()
: $validator->targetClass;

$field = "\$form->field(\$model, '$attribute')->dropDownList(\n"
. "{$t[2]}$targetClass::find()\n"
. "{$t[3]}->select(['$targetAttribute'])\n";

if (is_array($validator->filter)) {
$field .= "{$t[3]}->andWhere([\n";
foreach ($validator->filter as $key => $val) {
$field .= "{$t[4]}'$key' => '$val',\n";
}
$field .= "{$t[3]}])\n";
}

echo "{$field}{$t[3]}->indexBy('$targetAttribute')\n"
. "{$t[3]}->column(),\n"
. "{$t[2]}['prompt' => "
. "\$model->getAttributeLabel('$attribute')]\n"
. "{$t[1]})";
break;
default: return $validator->className();
}
}
}
33 changes: 29 additions & 4 deletions generators/crud/default/views/_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,36 @@

<?= "<?php " ?>$form = ActiveForm::begin(); ?>

<?php foreach ($generator->getColumnNames() as $attribute) {
if (in_array($attribute, $safeAttributes)) {
echo " <?= " . $generator->generateActiveField($attribute) . " ?>\n\n";
<?php

$model = new $generator->modelClass();
$attributePriorities = array_flip($model->safeAttributes());

foreach ($model->validators as $validator) {
foreach ($validator->attributes as $attr) {
if (is_int($attributePriorities[$attr])
|| $generator->validatorPriority($attributePriorities[$attr])
< $generator->validatorPriority($validator)
) {
$attributePriorities[$attr] = $validator;
}
}
} ?>
}

foreach ($attributePriorities as $attribute => $validator) {
echo " <?= ";
if (is_int($validator)
|| $generator->validatorPriority($validator) === -1
|| preg_match('/^(password|pass|passwd|passcode)$/i', $attribute)
) {
// previously defined behavior
echo $generator->generateActiveField($attribute);
} else {
echo $generator->generateValidatorField($attribute, $validator);
}
echo " ?>\n\n";
}
?>
<div class="form-group">
<?= "<?= " ?>Html::submitButton($model->isNewRecord ? <?= $generator->generateString('Create') ?> : <?= $generator->generateString('Update') ?>, ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
Expand Down