Skip to content

[detector-report] NamespaceCycleDetector #424

Description

@jessegall

Detector: NamespaceCycleDetector

Report (why the flagged code is CORRECT and the detector is wrong):
This is Laravel's morph map, and the morph map must name the model classes — that is the whole artefact. ResourceType::morphMap() is registered as Eloquent's morph map, so morphTo()/resolve()/morph() are the map and its two lookups, not a service locator. The cycle the detector sees is inherent to the pattern: a polymorphic type register names its models, and models carry the type. Relocating the arms to another namespace moves the cycle rather than removing it, because the map and the type must agree. The genuine service-locator arms on this same enum — repositoryClass(), repository(), dataClass(), which reached into App\Repositories, Domain*\Repository, App\Data and even App\Http\View — were real and have been moved out to App\Resources registries in this same change; what remains is only the morph map.

Cleanest design the reporter can conceive:
The morph map on the type enum it maps: morphTo() as an exhaustive match, morphMap() folding it for Relation::morphMap(), resolve() the inverse lookup. Everything that presents or persists a resource type is a registry above App\Enums; only the model identity stays on the enum, because Eloquent resolves a stored type string to a class through exactly this map.

⚖️ Maintainer litmus: a valid detector-report needs the flagged code to ALREADY BE the
cleanest design. If the design above differs from the flagged code at all, THAT design is
the owed fix — close this report; the fix is still owed.

Where: app/Enums/ResourceType.php:93

Code (app/Enums/ResourceType.php:93):

   90              self::WAREHOUSE => Warehouse::class,
   91          };
   92      }
→  93  
   94      public static function morphMap(): array
   95      {
   96          $map = T_Array::empty();
   97  
   98          foreach (self::cases() as $case)
   99          {
  100              $map[$case->value] = $case->morphTo();
  101          }
  102  
  103          return $map;
  104      }
  105  
  106      public static function resolve(string | object $class): ResourceType | null
  107      {
  108          if (is_object($class))
  109          {
  110              $class = get_class($class);
  111          }
  112  
  113          return array_find(self::cases(), fn (self $case) => is_a($class, $case->morphTo(), true))
  114              ?? self::tryFrom($class);
  115      }
  116  
  117      public function label(): string

Where: app/Enums/ResourceType.php:122

Code (app/Enums/ResourceType.php:122):

  119          return str($this->value)->replace('_', T_String::SPACE)->title()->toString();
  120      }
  121122      public function pluralName(): string
  123      {
  124          return str($this->value)->plural()->toString();
  125      }
  126  
  127      /**
  128       * Whether resources of this type can be filtered by a single shop via the in-picker shop-scope header.
  129       */
  130      public function isShopScoped(): bool
  131      {
  132          return in_array($this, [self::CATEGORY, self::CUSTOMER, self::ORDER, self::PRODUCT, self::VARIANT], strict: true);
  133      }
  134  
  135  }

Where: app/Enums/ResourceType.php:134

Code (app/Enums/ResourceType.php:134):

  131      {
  132          return in_array($this, [self::CATEGORY, self::CUSTOMER, self::ORDER, self::PRODUCT, self::VARIANT], strict: true);
  133      }
→ 134  
  135  }

Filed via commandments report from a consumer project.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions