Skip to content

[detector-report] NamespaceCycleDetector #426

Description

@jessegall

Detector: NamespaceCycleDetector

Report (why the flagged code is CORRECT and the detector is wrong):
Same class of finding as #425, on the sibling model. These are Eloquent relations and a search-index hook, not a layer breach. A variant BELONGS TO a product and a tax, HAS MANY stockpiles and barcode aliases; belongsTo/hasMany is how that is declared, and the other end declares the inverse because the association is genuinely two-way. There is no arrow to invert: an association is symmetric by nature, and Eloquent requires both ends to name each other's class. The service-locator arms that WERE a real breach on this branch — ResourceType's repositoryClass/dataClass reaching into App\Repositories and App\Http\View, and HandheldCodeField reaching into Domain for a Variant — were both fixed at the source rather than reported.

Cleanest design the reporter can conceive:
Variant declares belongsTo(Product::class), belongsTo(Tax::class), hasMany(Stockpile::class) and hasMany(BarcodeAlias::class), with each counterpart declaring the inverse. SearchIndexLookup::apply() keeps the index query in one place instead of on each searchable model. Nothing about how a variant is presented, persisted or resolved lives on the model — those are registries and resolvers above it.

⚖️ 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: domain/Variant/Projections/Variant.php:171

Code (domain/Variant/Projections/Variant.php:171):

  168  
  169      public function product(): BelongsTo
  170      {
→ 171          return $this->belongsTo(Product::class)->withTrashed();
  172      }
  173  
  174      public function tax(): BelongsTo
  175      {
  176          return $this->belongsTo(Tax::class);
  177      }
  178  
  179      public function stockpiles(): HasMany
  180      {
  181          return $this->hasMany(Stockpile::class, 'sku', 'sku');
  182      }
  183  
  184      public function barcodeAliases(): HasMany
  185      {
  186          return $this->hasMany(BarcodeAlias::class);
  187      }
  188  
  189  }

Where: domain/Variant/Projections/Variant.php:176

Code (domain/Variant/Projections/Variant.php:176):

  173  
  174      public function tax(): BelongsTo
  175      {
→ 176          return $this->belongsTo(Tax::class);
  177      }
  178  
  179      public function stockpiles(): HasMany
  180      {
  181          return $this->hasMany(Stockpile::class, 'sku', 'sku');
  182      }
  183  
  184      public function barcodeAliases(): HasMany
  185      {
  186          return $this->hasMany(BarcodeAlias::class);
  187      }
  188  
  189  }

Where: domain/Variant/Projections/Variant.php:181

Code (domain/Variant/Projections/Variant.php:181):

  178  
  179      public function stockpiles(): HasMany
  180      {
→ 181          return $this->hasMany(Stockpile::class, 'sku', 'sku');
  182      }
  183  
  184      public function barcodeAliases(): HasMany
  185      {
  186          return $this->hasMany(BarcodeAlias::class);
  187      }
  188  
  189  }

Where: domain/Variant/Projections/Variant.php:186

Code (domain/Variant/Projections/Variant.php:186):

  183  
  184      public function barcodeAliases(): HasMany
  185      {
→ 186          return $this->hasMany(BarcodeAlias::class);
  187      }
  188  
  189  }

Where: domain/Variant/Projections/Variant.php:150

Code (domain/Variant/Projections/Variant.php:150):

  147  
  148      public function searchFor(Builder $builder, string $value): void
  149      {
→ 150          SearchIndexLookup::apply($builder, 'variant', $value);
  151      }
  152  
  153      // ----------[ Scopes ]----------
  154  
  155      /**
  156       * Look this variant up the way a scanner does: across every shop, because a scanned
  157       * barcode is universal, and with the product loaded so the code can be named on screen.
  158       *
  159       * @param  Builder<self>  $builder
  160       */
  161      #[Scope]
  162      public function scannable(Builder $builder): void
  163      {
  164          $builder->withoutGlobalScope(ShopScope::class)->with('product');
  165      }
  166  
  167      // ----------[ Relations ]----------
  168  
  169      public function product(): BelongsTo
  170      {
  171          return $this->belongsTo(Product::class)->withTrashed();
  172      }
  173  
  174      public function tax(): BelongsTo

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