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.
Detector:
NamespaceCycleDetectorReport (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.
Where:
domain/Variant/Projections/Variant.php:171Code (
domain/Variant/Projections/Variant.php:171):Where:
domain/Variant/Projections/Variant.php:176Code (
domain/Variant/Projections/Variant.php:176):Where:
domain/Variant/Projections/Variant.php:181Code (
domain/Variant/Projections/Variant.php:181):Where:
domain/Variant/Projections/Variant.php:186Code (
domain/Variant/Projections/Variant.php:186):Where:
domain/Variant/Projections/Variant.php:150Code (
domain/Variant/Projections/Variant.php:150):Filed via
commandments reportfrom a consumer project.