Repository: laravel-shift/blueprint (php)

Issue:
TestGeneraor - call to a member function column() on null - line 252
Laravel Version: 8.33.1
PHP Version: 7.4.3
Blueprint Version: 1.23
Platform: Linux

### Issue:
TestGeneraor - call to a member function column() on null - line 252

### Correction
line 252 : if ( ! is_null($local_model) ) {

### draft.yaml:
no model

### use case
To create sql view we need to have all the tables created (included pivot) so to divide draft.yaml in 2 or more parts. First one with the models and second one with the remaining objects. The error occurs when executing build on the second config file taht contains no model. 
  Call to a member function columns() on null when added validate to controllers
I have this draft file for `laravel Blueprint` that works fine, 

but showing an error when added the comments lines of `validate: status,amount,user_id` or `validate: payment`

```yaml
models:
  Models\Subscription:
    source: string:400
    start_date: date
    end_date: date
    user_id: id foreign:users.id
    payment_id: id foreign:payments.id
    softDeletes: true
    timestamps: true

  Models\Payment:
    status: string
    amount: decimal:8,3
    user_id: id foreign:users.id
    softDeletes: true
    timestamps: true

controllers:
  Subscription:
    resource: index,show

  Telegram:
    resource: all

  Payment:
    create:
      render: payment.create
    store:
      #   validate: status,amount,user_id
      save: payment
      fire: NewPayment with:payment
      send: PaymentCreated to:payment.user with:payment
      flash: message
      redirect: payment.create

  Api\Payment:
    store:
      # validate: payment
      save: payment
      respond: 204
```

Error:
<img width="865" alt="Screen Shot 2021-05-17 at 2 42 14 AM" src="https://user-images.githubusercontent.com/3064487/118416776-f0cb6a80-b6b9-11eb-8391-d1e561c22435.png">



StackOverflow question: 
https://stackoverflow.com/questions/67562499/laravel-blueprint-shows-error-when-added-validate-to-controller

Pull request description:
Determine correct model for context
Resolves #474
Resolves #476

Patch:
diff --git a/src/Tree.php b/src/Tree.php
index 1ca9ecc..90b354d 100644
--- a/src/Tree.php
+++ b/src/Tree.php
@@ -46,7 +46,7 @@ class Tree
         }
 
         $matches = array_filter(array_keys($this->models), function ($key) use ($context) {
-            return Str::endsWith($key, '/' . Str::studly($context));
+            return Str::endsWith(Str::afterLast(Str::afterLast($key, '\\'), '/'), [Str::studly($context), Str::studly(Str::plural($context))]);
         });
 
         if (count($matches) === 1) {


Test patch:
diff --git a/src/Generators/TestGenerator.php b/src/Generators/TestGenerator.php
index c64274e..de561cb 100644
--- a/src/Generators/TestGenerator.php
+++ b/src/Generators/TestGenerator.php
@@ -248,7 +248,7 @@ class TestGenerator implements Generator
 
                                 $setup['data'][] = $faker;
                                 $request_data[$data] = '$' . $variable_name;
-                            } else {
+                            } elseif (! is_null($local_model)) {
                                 foreach ($local_model->columns() as $local_column) {
                                     if ($local_column->name() === 'id') {
                                         continue;
diff --git a/tests/Feature/Generators/TestGeneratorTest.php b/tests/Feature/Generators/TestGeneratorTest.php
index 0d82f98..12bbe29 100644
--- a/tests/Feature/Generators/TestGeneratorTest.php
+++ b/tests/Feature/Generators/TestGeneratorTest.php
@@ -87,19 +87,25 @@ class TestGeneratorTest extends TestCase
         $this->filesystem->expects('stub')
             ->with('test.case.stub')
             ->andReturn($this->stub('test.case.stub'));
-        $dirname = dirname($path);
-        $this->filesystem->expects('exists')
-            ->with($dirname)
-            ->andReturnFalse();
-        $this->filesystem->expects('makeDirectory')
-            ->with($dirname, 0755, true);
-        $this->filesystem->expects('put')
+
+        $paths = collect($path)->combine($test)->toArray();
+        foreach ($paths as $path => $test) {
+            $dirname = dirname($path);
+
+            $this->filesystem->expects('exists')
+                ->with($dirname)
+                ->andReturnFalse();
+                
+            $this->filesystem->expects('makeDirectory')
+                ->with($dirname, 0755, true);
+
+            $this->filesystem->expects('put')
             ->with($path, $this->fixture($test));
+        }
 
         $tokens = $this->blueprint->parse($this->fixture($definition));
         $tree = $this->blueprint->analyze($tokens);
-
-        $this->assertEquals(['created' => [$path]], $this->subject->output($tree));
+        $this->assertEquals(['created' => array_keys($paths)], $this->subject->output($tree));
     }
 
     /**
@@ -362,6 +368,17 @@ class TestGeneratorTest extends TestCase
             ['drafts/respond-statements.yaml', 'tests/Feature/Http/Controllers/Api/PostControllerTest.php', 'tests/respond-statements-laravel8.php'],
             ['drafts/full-crud-example.yaml', 'tests/Feature/Http/Controllers/PostControllerTest.php', 'tests/full-crud-example-laravel8.php'],
             ['drafts/model-reference-validate.yaml', 'tests/Feature/Http/Controllers/CertificateControllerTest.php', 'tests/api-shorthand-validation-laravel8.php'],
+            ['drafts/call-to-a-member-function-columns-on-null.yaml', [
+                'tests/Feature/Http/Controllers/SubscriptionControllerTest.php',
+                'tests/Feature/Http/Controllers/TelegramControllerTest.php',
+                'tests/Feature/Http/Controllers/PaymentControllerTest.php',
+                'tests/Feature/Http/Controllers/Api/PaymentControllerTest.php'
+            ],[
+                'tests/call-to-a-member-function-columns-on-null-SubscriptionControllerTest-laravel8.php',
+                'tests/call-to-a-member-function-columns-on-null-TelegramControllerTest-laravel8.php',
+                'tests/call-to-a-member-function-columns-on-null-PaymentControllerTest-laravel8.php',
+                'tests/call-to-a-member-function-columns-on-null-Api-PaymentControllerTest-laravel8.php',
+            ]],
         ];
     }
 }
diff --git a/tests/fixtures/drafts/call-to-a-member-function-columns-on-null.yaml b/tests/fixtures/drafts/call-to-a-member-function-columns-on-null.yaml
new file mode 100644
index 0000000..0686da6
--- /dev/null
+++ b/tests/fixtures/drafts/call-to-a-member-function-columns-on-null.yaml
@@ -0,0 +1,40 @@
+models:
+  Models\Subscription:
+    source: string:400
+    start_date: date
+    end_date: date
+    user_id: id foreign:users.id
+    payment_id: id foreign:payments.id
+    softDeletes: true
+    timestamps: true
+
+  Models\Payment:
+    status: string
+    amount: decimal:8,3
+    user_id: id foreign:users.id
+    softDeletes: true
+    timestamps: true
+
+controllers:
+  Subscription:
+    resource: index,show
+
+  Telegram:
+    resource: all
+
+  Payment:
+    create:
+      render: payment.create
+    store:
+      validate: status,amount,user_id
+      save: payment
+      fire: NewPayment with:payment
+      send: PaymentCreated to:payment.user with:payment
+      flash: message
+      redirect: payment.create
+
+  Api\Payment:
+    store:
+      validate: payment
+      save: payment
+      respond: 204
\ No newline at end of file
diff --git a/tests/fixtures/tests/call-to-a-member-function-columns-on-null-Api-PaymentControllerTest-laravel8.php b/tests/fixtures/tests/call-to-a-member-function-columns-on-null-Api-PaymentControllerTest-laravel8.php
new file mode 100644
index 0000000..f717474
--- /dev/null
+++ b/tests/fixtures/tests/call-to-a-member-function-columns-on-null-Api-PaymentControllerTest-laravel8.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Tests\Feature\Http\Controllers\Api;
+
+use App\Events\NewPayment;
+use App\Mail\PaymentCreated;
+use App\Payment;
+use App\User;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Illuminate\Foundation\Testing\WithFaker;
+use Illuminate\Support\Facades\Event;
+use Illuminate\Support\Facades\Mail;
+use JMac\Testing\Traits\AdditionalAssertions;
+use Tests\TestCase;
+
+/**
+ * @see \App\Http\Controllers\Api\PaymentController
+ */
+class PaymentControllerTest extends TestCase
+{
+    use AdditionalAssertions, RefreshDatabase, WithFaker;
+
+    /**
+     * @test
+     */
+    public function store_uses_form_request_validation()
+    {
+        $this->assertActionUsesFormRequest(
+            \App\Http\Controllers\Api\PaymentController::class,
+            'store',
+            \App\Http\Requests\Api\PaymentStoreRequest::class
+        );
+    }
+
+    /**
+     * @test
+     */
+    public function store_saves_and_responds_with()
+    {
+        $status = $this->faker->word;
+        $amount = $this->faker->randomFloat(/** decimal_attributes **/);
+        $user = User::factory()->create();
+
+        $response = $this->post(route('payment.store'), [
+            'status' => $status,
+            'amount' => $amount,
+            'user_id' => $user->id,
+        ]);
+
+        $payments = Payment::query()
+            ->where('status', $status)
+            ->where('amount', $amount)
+            ->where('user_id', $user->id)
+            ->get();
+        $this->assertCount(1, $payments);
+        $payment = $payments->first();
+
+        $response->assertNoContent();
+    }
+}
diff --git a/tests/fixtures/tests/call-to-a-member-function-columns-on-null-PaymentControllerTest-laravel8.php b/tests/fixtures/tests/call-to-a-member-function-columns-on-null-PaymentControllerTest-laravel8.php
new file mode 100644
index 0000000..69ad2ba
--- /dev/null
+++ b/tests/fixtures/tests/call-to-a-member-function-columns-on-null-PaymentControllerTest-laravel8.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Tests\Feature\Http\Controllers;
+
+use App\Events\NewPayment;
+use App\Mail\PaymentCreated;
+use App\Payment;
+use App\User;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Illuminate\Foundation\Testing\WithFaker;
+use Illuminate\Support\Facades\Event;
+use Illuminate\Support\Facades\Mail;
+use JMac\Testing\Traits\AdditionalAssertions;
+use Tests\TestCase;
+
+/**
+ * @see \App\Http\Controllers\PaymentController
+ */
+class PaymentControllerTest extends TestCase
+{
+    use AdditionalAssertions, RefreshDatabase, WithFaker;
+
+    /**
+     * @test
+     */
+    public function create_displays_view()
+    {
+        $response = $this->get(route('payment.create'));
+
+        $response->assertOk();
+        $response->assertViewIs('payment.create');
+    }
+
+
+    /**
+     * @test
+     */
+    public function store_uses_form_request_validation()
+    {
+        $this->assertActionUsesFormRequest(
+            \App\Http\Controllers\PaymentController::class,
+            'store',
+            \App\Http\Requests\PaymentStoreRequest::class
+        );
+    }
+
+    /**
+     * @test
+     */
+    public function store_saves_and_redirects()
+    {
+        $status = $this->faker->word;
+        $amount = $this->faker->randomFloat(/** decimal_attributes **/);
+        $user = User::factory()->create();
+
+        Event::fake();
+        Mail::fake();
+
+        $response = $this->post(route('payment.store'), [
+            'status' => $status,
+            'amount' => $amount,
+            'user_id' => $user->id,
+        ]);
+
+        $payments = Payment::query()
+            ->where('status', $status)
+            ->where('amount', $amount)
+            ->where('user_id', $user->id)
+            ->get();
+        $this->assertCount(1, $payments);
+        $payment = $payments->first();
+
+        $response->assertRedirect(route('payment.create'));
+        $response->assertSessionHas('message', $message);
+
+        Event::assertDispatched(NewPayment::class, function ($event) use ($payment) {
+            return $event->payment->is($payment);
+        });
+        Mail::assertSent(PaymentCreated::class, function ($mail) use ($payment) {
+            return $mail->hasTo($payment->user) && $mail->payment->is($payment);
+        });
+    }
+}
diff --git a/tests/fixtures/tests/call-to-a-member-function-columns-on-null-SubscriptionControllerTest-laravel8.php b/tests/fixtures/tests/call-to-a-member-function-columns-on-null-SubscriptionControllerTest-laravel8.php
new file mode 100644
index 0000000..d81a4bc
--- /dev/null
+++ b/tests/fixtures/tests/call-to-a-member-function-columns-on-null-SubscriptionControllerTest-laravel8.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Tests\Feature\Http\Controllers;
+
+use App\Subscription;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+/**
+ * @see \App\Http\Controllers\SubscriptionController
+ */
+class SubscriptionControllerTest extends TestCase
+{
+    use RefreshDatabase;
+
+    /**
+     * @test
+     */
+    public function index_displays_view()
+    {
+        $subscriptions = Subscription::factory()->count(3)->create();
+
+        $response = $this->get(route('subscription.index'));
+
+        $response->assertOk();
+        $response->assertViewIs('subscription.index');
+        $response->assertViewHas('subscriptions');
+    }
+
+
+    /**
+     * @test
+     */
+    public function show_displays_view()
+    {
+        $subscription = Subscription::factory()->create();
+
+        $response = $this->get(route('subscription.show', $subscription));
+
+        $response->assertOk();
+        $response->assertViewIs('subscription.show');
+        $response->assertViewHas('subscription');
+    }
+}
diff --git a/tests/fixtures/tests/call-to-a-member-function-columns-on-null-TelegramControllerTest-laravel8.php b/tests/fixtures/tests/call-to-a-member-function-columns-on-null-TelegramControllerTest-laravel8.php
new file mode 100644
index 0000000..ecd7765
--- /dev/null
+++ b/tests/fixtures/tests/call-to-a-member-function-columns-on-null-TelegramControllerTest-laravel8.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Tests\Feature\Http\Controllers;
+
+use Tests\TestCase;
+
+/**
+ * @see \App\Http\Controllers\TelegramController
+ */
+class TelegramControllerTest extends TestCase
+{
+    
+}