Repository: laravel-shift/blueprint (php)
Issue: Option to generate nullable rules in FormRequest
<!-- PLEASE COMPLETE THIS TEMPLATE -->

### Synopsis:
The nullable fields defined in the draft could maybe also be set as nullable in the StoreRequest/UpdateRequest rules generated by Blueprint, unless there's a reason why they aren't? it's just odd to me that Blueprint generates rules for every field option except for the nullable option.

Why? because validation fails on empty/null fields. This is a fairly quick edit for the dev to fix it but I feel like it should be supported out of the box by Blueprint.

### Proposed Syntax:
No syntax change.

### Expected Behavior:
Fields defined as nullable in the draft file to be set as nullable in the Request rules as well.
Pull request description: Explicitly set nullable validation rule
Currently, Blueprint does not explicitly set the `nullable` validation rule for `nullable` columns. This means the generated form request requires `nullable` columns to be set or not in the request data at all.

For example, a `published_at` date column which is nullable would generate the validation rule `['date']`. As such, attempting to set this `published_at` column to `null` would cause validation to fail. That is not the expected behavior for a nullable column.

Originally, the thought was the columns you passed to a `validate` statement should all be "required". While that is the common case, it's assuming (too fancy).

I now think adding the `nullable` validation rule to database columns which are `nullable` generates an expected set of ready-to-use validation rules. If the developer knows a nullable column is always present in the request or should not be `null` for that form, then it's easy to change to `required`.

---
Closes #431

Patch:
diff --git a/src/Translators/Rules.php b/src/Translators/Rules.php
index 973e73a..d38888c 100644
--- a/src/Translators/Rules.php
+++ b/src/Translators/Rules.php
@@ -29,9 +29,7 @@ class Rules
     {
         $rules = [];
 
-        if (!in_array('nullable', $column->modifiers())) {
-            array_push($rules, 'required');
-        }
+        array_push($rules, in_array('nullable', $column->modifiers()) ? 'nullable' : 'required');
 
         // hack for tests...
         if (in_array($column->dataType(), ['string', 'char', 'text', 'longText', 'fullText'])) {


Test patch:
diff --git a/tests/Feature/Translators/RulesTest.php b/tests/Feature/Translators/RulesTest.php
index 28b3a4b..585d283 100644
--- a/tests/Feature/Translators/RulesTest.php
+++ b/tests/Feature/Translators/RulesTest.php
@@ -21,6 +21,16 @@ class RulesTest extends TestCase
         $this->assertEquals(['required'], Rules::fromColumn('context', $column));
     }
 
+    /**
+     * @test
+     */
+    public function forColumn_returns_nullable_rule()
+    {
+        $column = new Column('test', 'string', ['nullable']);
+
+        $this->assertEquals(['nullable', 'string'], Rules::fromColumn('context', $column));
+    }
+
     /**
      * @test
      * @dataProvider stringDataTypesProvider
@@ -201,7 +211,7 @@ class RulesTest extends TestCase
     /**
      * @test
      */
-    public function forColumn_does_not_return_between_rule_for_unsigned_decimal_without_precion_and_scale()
+    public function forColumn_does_not_return_between_rule_for_unsigned_decimal_without_precision_and_scale()
     {
         $unsignedBeforeDecimalColumn = new Column('column', 'unsigned decimal');
 
@@ -216,7 +226,7 @@ class RulesTest extends TestCase
      * @test
      * @dataProvider noBetweenRuleDataProvider
      */
-    public function forColumn_does_not_return_between_rule_for_double_without_precion_and_scale($column)
+    public function forColumn_does_not_return_between_rule_for_double_without_precision_and_scale($column)
     {
         $this->assertNotContains('between', Rules::fromColumn('context', $column));
     }
diff --git a/tests/fixtures/form-requests/certificate-store.php b/tests/fixtures/form-requests/certificate-store.php
index dec7968..5b3d719 100644
--- a/tests/fixtures/form-requests/certificate-store.php
+++ b/tests/fixtures/form-requests/certificate-store.php
@@ -29,7 +29,7 @@ class CertificateStoreRequest extends FormRequest
             'reference' => ['required', 'string'],
             'document' => ['required', 'string'],
             'expiry_date' => ['required', 'date'],
-            'remarks' => ['string'],
+            'remarks' => ['nullable', 'string'],
         ];
     }
 }
diff --git a/tests/fixtures/form-requests/certificate-update.php b/tests/fixtures/form-requests/certificate-update.php
index 34ec8d6..ea7364f 100644
--- a/tests/fixtures/form-requests/certificate-update.php
+++ b/tests/fixtures/form-requests/certificate-update.php
@@ -29,7 +29,7 @@ class CertificateUpdateRequest extends FormRequest
             'reference' => ['required', 'string'],
             'document' => ['required', 'string'],
             'expiry_date' => ['required', 'date'],
-            'remarks' => ['string'],
+            'remarks' => ['nullable', 'string'],
         ];
     }
 }