Repository: laravel-shift/blueprint (php)

Issue:
Table name set via meta is not honoured by foreign keys
- Laravel Version: 11.3.1
- PHP Version: 8.3.6
- Blueprint Version: 2.10.0
- Platform: Linux

### Issue:

First up - thanks for the project, wish i'd found it earlier as it's going to save me a lot of grunt work!

On to the issue - as per https://github.com/laravel-shift/blueprint/pull/577 & https://github.com/laravel-shift/blueprint/issues/639 we appear to have the ability to use set a custom table name via meta when defining a model.

This works and `$table` is added to the model with the expected value.

However, when attempting to create a second model (Service in the included example) that references a model which has a custom table name defined, that custom table name is not used when creating the foreign key.

With the draft.yaml in this report - when running migrations a `ERROR:  relation "customers" does not exist (Connection: pgsql, SQL: alter table "service" add constraint "service_customer_id_foreign" foreign key ("customer_id") references "customers" ("id"))` exception will be produced.

The culprit appears to be that Generators/MigrationGenerator.php @ line 318 - updating this line to include the table in the constrained() method "fixes" the issue, and both the models with custom table names and those without appear to migrate as expected.

However it then would operate identically to the if statement below it so i'm uncertain as to the underlying reason for these two if statements to be different:

```diff
diff --git a/src/Generators/MigrationGenerator.php b/src/Generators/MigrationGenerator.php
index b8c63a5..e105c5f 100644
--- a/src/Generators/MigrationGenerator.php
+++ b/src/Generators/MigrationGenerator.php
@@ -315,7 +315,7 @@ class MigrationGenerator extends AbstractClassGenerator implements Generator
                 $on_update_suffix = '->cascadeOnUpdate()';
             }
             if ($column_name === Str::singular($table) . '_' . $column) {
-                return self::INDENT . "{$prefix}->constrained(){$on_delete_suffix}{$on_update_suffix}";
+                return self::INDENT . "{$prefix}->constrained('{$table}'){$on_delete_suffix}{$on_update_suffix}";
             }
             if ($column === 'id') {
                 return self::INDENT . "{$prefix}->constrained('{$table}'){$on_delete_suffix}{$on_update_suffix}";
```

The 'meta' functionality is also not documented on the website as far as I can see so i'll open a separate issue at https://github.com/laravel-shift/blueprint-docs.git assuming it is expected to be supported.

### draft.yaml:
```yaml
models:
  Customer:
    name: unique string index
    meta:
      table: customer
  Service:
    customer_id: id foreign:customer.id
    name: unique string index
  Plan:
    name: unique string index
  Rate:
    plan_id: id foreign:plans.id
    name: unique string index

controllers:
  Customer:
    resource
  Service:
    resource
  Plan:
    resource
  Rate:
    resource
```

Pull request:
Honor table name from dot notation
This PR addresses a disconnect between the code and documentation in regards to customizing foreign keys.

The documentation stated:

> If you are not following Laravel's naming conventions, you may set the attribute on the `foreign` modifier. Blueprint supports either the foreign table name or the table and column name using dot notation.

The code always assumed the _table name_ was a model reference and either pluralized or snake cased the value.

After this PR, Blueprint either supports a _model reference_ (base class name or fully qualified class name) or the table and column name using dot notation.

Examples:
```yaml
country_id: id foreign:country          # table: countries column: id
ccid: string foreign:country.id         # table: country column: id
country_code: string foreign:Country    # table: countries column: code
c_code: string foreign:countries.code   # table: countries column: code
```

**Note:** this syntax is only needed if you are using unconventional table or column names.

---
Closes #684 

Patch:
diff --git a/src/Generators/MigrationGenerator.php b/src/Generators/MigrationGenerator.php
index 9f46c2b..4aa56a7 100644
--- a/src/Generators/MigrationGenerator.php
+++ b/src/Generators/MigrationGenerator.php
@@ -275,12 +275,11 @@ class MigrationGenerator extends AbstractClassGenerator implements Generator
             $column = Str::afterLast($column_name, '_');
         } elseif (Str::contains($on, '.')) {
             [$table, $column] = explode('.', $on);
-            $table = Str::snake($table);
         } elseif (Str::contains($on, '\\')) {
-            $table = Str::lower(Str::plural(Str::afterLast($on, '\\')));
+            $table = Str::snake(Str::plural(Str::afterLast($on, '\\')));
             $column = Str::afterLast($column_name, '_');
         } else {
-            $table = Str::plural($on);
+            $table = Str::snake(Str::plural($on));
             $column = Str::afterLast($column_name, '_');
         }
 
@@ -315,12 +314,15 @@ class MigrationGenerator extends AbstractClassGenerator implements Generator
             if ($on_delete_clause === 'cascade') {
                 $on_delete_suffix = '->cascadeOnDelete()';
             }
+
             if ($on_update_clause === 'cascade') {
                 $on_update_suffix = '->cascadeOnUpdate()';
             }
+
             if ($column_name === Str::singular($table) . '_' . $column) {
                 return self::INDENT . "{$prefix}->constrained(){$on_delete_suffix}{$on_update_suffix}";
             }
+
             if ($column === 'id') {
                 return self::INDENT . "{$prefix}->constrained('{$table}'){$on_delete_suffix}{$on_update_suffix}";
             }


Test patch:
diff --git a/tests/fixtures/drafts/unconventional-foreign-key.yaml b/tests/fixtures/drafts/unconventional-foreign-key.yaml
index 7cfd1f2..74c334b 100644
--- a/tests/fixtures/drafts/unconventional-foreign-key.yaml
+++ b/tests/fixtures/drafts/unconventional-foreign-key.yaml
@@ -1,7 +1,7 @@
 models:
   State:
     name: string
-    countries_id: id foreign:countries
-    country_code: string foreign:countries
-    ccid: string foreign:countries
+    countries_id: id foreign:country
+    country_code: string foreign:Country
+    ccid: string foreign:country.id
     c_code: string foreign:countries.code
diff --git a/tests/fixtures/factories/unconventional-foreign-key.php b/tests/fixtures/factories/unconventional-foreign-key.php
index e957cb9..26f2e99 100644
--- a/tests/fixtures/factories/unconventional-foreign-key.php
+++ b/tests/fixtures/factories/unconventional-foreign-key.php
@@ -25,7 +25,7 @@ class StateFactory extends Factory
             'name' => fake()->name(),
             'countries_id' => Country::factory(),
             'country_code' => Country::factory()->create()->code,
-            'ccid' => Country::factory()->create()->ccid,
+            'ccid' => Country::factory(),
             'c_code' => Country::factory()->create()->code,
         ];
     }
diff --git a/tests/fixtures/migrations/unconventional-foreign-key.php b/tests/fixtures/migrations/unconventional-foreign-key.php
index c6a4439..eeac6eb 100644
--- a/tests/fixtures/migrations/unconventional-foreign-key.php
+++ b/tests/fixtures/migrations/unconventional-foreign-key.php
@@ -20,7 +20,7 @@ return new class extends Migration
             $table->string('country_code');
             $table->foreign('country_code')->references('code')->on('countries');
             $table->string('ccid');
-            $table->foreign('ccid')->references('ccid')->on('countries');
+            $table->foreign('ccid')->references('id')->on('country');
             $table->string('c_code');
             $table->foreign('c_code')->references('code')->on('countries');
             $table->timestamps();