Skip to main content

codebase-pattern-finder

Finds similar implementations, usage examples, and established patterns in this Shopsys project that you can model new work after. Like codebase-locator, but it also returns concrete code snippets with file:line references, not just locations.

Source facts

Repository
shopsys/project-base
Last source activity
July 14, 2026 at 08:18
Detected SKILL.md language
English
Stars
38
Forks
12

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
codebase-pattern-finder
description
Finds similar implementations, usage examples, and established patterns in this Shopsys project that you can model new work after. Like codebase-locator, but it also returns concrete code snippets with file:line references, not just locations.
tools
Grep, Glob, Read, LS
> **Monorepo note:** if the repository has top-level `packages/` and `project-base/` directories (the shopsys/shopsys monorepo layout, not a standalone project), also read `.agents/skills/monorepo-vs-project/SKILL.md` and apply its delta (package-first + path/role remap) on top of this skill. You are a specialist at finding code patterns and examples in this Shopsys **project**. Your job is to locate similar implementations that can serve as templates or inspiration for new work — and to show the actual code. > For how the code is shaped and how to extend it correctly, see `.agents/skills/shopsys-architecture/SKILL.md`. To only locate files (no code extraction), use `.agents/skills/codebase-locator/SKILL.md`. This skill finds patterns and shows them. ## What this repository is (for pattern search) This is a project built on Shopsys Platform. Two kinds of code: - **Your project code** — under `app/` (backend) and `storefront/` (frontend). This is where the project's own patterns live and where you look **first** for a model to copy. - **Framework reference** — the `shopsys/*` packages installed **read-only** in `vendor/shopsys/*/src/`. These hold battle-tested base patterns. Cite them to show the base a project class extends, or as the canonical reference when the project doesn't customize a feature. When you show a pattern, **prefer a project example** (`app/src/…` or `storefront/…`) and cite the framework base in `vendor/shopsys/…` where it clarifies the extension chain. ## Core responsibilities 1. **Find similar implementations** — comparable features, usage examples, established patterns, test examples. 2. **Extract reusable patterns** — show structure, highlight key aspects, note conventions. 3. **Provide concrete examples** — real snippets, multiple variations, note which is preferred, always with `file:line` references. ## Search strategy ### Step 1: Identify pattern types Think about what the user is seeking and which categories to search: - **Business logic** — Facade implementations and business methods - **Data access** — Repository and QueryBuilder usage - **Domain entities** — multi-domain support and relationships - **Extension** — project classes extending framework base classes - **Forms** — Symfony FormType implementations and extensions - **Controllers** — admin controller structures - **GraphQL** — resolvers, mutations, and ResolverMaps - **Storefront** — React components, hooks, TypeScript patterns - **Testing** — PHPUnit test structures with modern attributes ### Step 2: Search Use `Grep`, `Glob`, and `LS`. Search `app/` and `storefront/` first; widen to `vendor/shopsys/*/src/` for the base pattern or when a feature isn't customized in the project. ### Step 3: Read and extract Read files with promising patterns, extract the relevant sections, note context and usage, and identify variations. ## Output format Structure your findings like this: ```` ## Pattern Examples: [Pattern Type] ### Pattern 1: Facade business logic **Found in**: `app/src/Model/Product/ProductFacade.php:36` (extends `vendor/shopsys/framework/src/Model/Product/ProductFacade.php`) **Used for**: Business-logic entry point coordinating repository, factory, and persistence ```php <?php declare(strict_types=1); namespace App\Model\Product; use Doctrine\ORM\EntityManagerInterface; use Shopsys\FrameworkBundle\Model\Product\ProductFacade as BaseProductFacade; class ProductFacade extends BaseProductFacade { public function __construct( protected readonly EntityManagerInterface $em, protected readonly ProductRepository $productRepository, protected readonly ProductDataFactory $productDataFactory, protected readonly ProductFactory $productFactory, ) { // ... parent constructor wiring } /** * @param int $productId * @return \App\Model\Product\Product */ public function getById(int $productId): Product { return $this->productRepository->getById($productId); } /** * @param \App\Model\Product\ProductData $productData * @return \App\Model\Product\Product */ public function create(ProductData $productData): Product { $product = $this->productFactory->create($productData); $this->em->persist($product); $this->em->flush(); return $product; } } ``` **Key aspects**: - Project facade extends the framework base in `vendor/shopsys/framework/src/…` - Constructor injection with `readonly` promoted properties - EntityManager handles persistence; repository/factory do the work - Return types narrowed to the project's own `Product` ```` Additional patterns worth including in a report: ### Pattern: Modern controller with PHP attributes **Found in**: `vendor/shopsys/framework/src/Controller/Admin/CategoryController.php:60` (framework reference; override in `app/src/Controller/Admin/` only if customizing) **Used for**: Admin CRUD with route/permission attributes ```php <?php #[Route(path: '/category/edit/{id}', requirements: ['id' => '\d+'])] #[CanEdit(methods: [HttpMethod::POST])] #[CanView(methods: [HttpMethod::GET])] public function editAction(Request $request, int $id): Response { $category = $this->categoryFacade->getById($id); $categoryData = $this->categoryDataFactory->createFromCategory($category); $form = $this->createForm(CategoryFormType::class, $categoryData, [ 'category' => $category, 'scenario' => CategoryFormType::SCENARIO_EDIT, ]); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $this->categoryFacade->edit($id, $categoryData); // ... flash message + redirect } } ``` **Key aspects**: - Route/permission expressed as PHP attributes (`#[Route]`, `#[CanEdit]`, `#[CanView]`) - DataFactory populates the form data object from the entity - Standard Symfony form handling with a named scenario ### Pattern: GraphQL ResolverMap with inheritance **Found in**: `app/src/FrontendApi/Resolver/Products/ProductResolverMap.php:21` (extends the framework base ResolverMap in `vendor/shopsys/frontend-api/src/…`) **Used for**: GraphQL field resolution with project customizations ```php <?php declare(strict_types=1); namespace App\FrontendApi\Resolver\Products; /** * @property \App\FrontendApi\Resolver\Products\DataMapper\ProductArrayFieldMapper $productArrayFieldMapper */ class ProductResolverMap extends BaseProductResolverMap { /** * @return array<string, callable> */ #[Override] protected function mapProduct(): array { return [ self::RESOLVE_FIELD => function ($value, ArgumentInterface $args, ArrayObject $context, ResolveInfo $info) { /** @var \App\FrontendApi\Resolver\Products\DataMapper\ProductArrayFieldMapper|\App\FrontendApi\Resolver\Products\DataMapper\ProductEntityFieldMapper $mapper */ $mapper = $value instanceof Product ? $this->productEntityFieldMapper : $this->productArrayFieldMapper; try { return $this->getObjectMethodForField($mapper, $info->fieldName)($value); } catch (MethodNotFoundException $exception) { return FieldResolver::valueFromObjectOrArray($value, $info->fieldName); } }, ]; } } ``` **Key aspects**: - Project ResolverMap extends the framework base and overrides one map with `#[Override]` - Chooses an entity- vs array-based field mapper depending on the resolved value - Falls back to generic field resolution on `MethodNotFoundException` ### Pattern: React functional component **Found in**: `storefront/components/Pages/Cart/CartSummary.tsx` **Used for**: Storefront React components with TypeScript ```typescript import { FC } from 'react'; import { useTranslation } from 'next-i18next'; import { Button } from 'components/Forms/Button/Button'; import { Price } from 'components/Basic/Price/Price'; type CartSummaryProps = { cartItems: CartItem[]; totalPrice: Money; onCheckout: () => void; }; export const CartSummary: FC<CartSummaryProps> = ({ cartItems, totalPrice, onCheckout }) => { const { t } = useTranslation(); const itemsCount = cartItems.reduce((sum, item) => sum + item.quantity, 0); return ( <div className="cart-summary"> <div className="cart-summary__items"> {t('Items in cart')}: {itemsCount} </div> <div className="cart-summary__total"> <Price value={totalPrice.priceWithVat} currency={totalPrice.currency} /> </div> <Button onClick={onCheckout} variant="primary"> {t('Proceed to checkout')} </Button> </div> ); }; ``` **Key aspects**: - Typed props via a local `type` and `FC<Props>` - `useTranslation` for all user-facing strings - Composition of shared storefront components (`Price`, `Button`) ### Pattern: Modern PHPUnit functional test **Found in**: `app/tests/App/Functional/Model/Product/ProductFacadeTest.php:17` **Used for**: Functional testing with dependency injection and PHPUnit attributes ```php <?php declare(strict_types=1); namespace Tests\App\Functional\Model\Product; use App\Model\Product\ProductDataFactory; use App\Model\Product\ProductFacade; use PHPUnit\Framework\Attributes\DataProvider; use Tests\App\Test\TransactionFunctionalTestCase; final class ProductFacadeTest extends TransactionFunctionalTestCase { /** * @inject */ private ProductDataFactory $productDataFactory; /** * @inject */ private ProductFacade $productFacade; #[DataProvider('getTestSellingDeniedDataProvider')] public function testSellingDenied( bool $hidden, bool $sellingDenied, bool $calculatedSellingDenied, ): void { // Test implementation using injected services and modern attributes } /** * @return array<int, array<int, bool>> */ public static function getTestSellingDeniedDataProvider(): array { return [ [false, false, false], [true, false, true], [false, true, true], ]; } } ``` **Key aspects**: - `final` test class extending `TransactionFunctionalTestCase` - Services injected with the `@inject` annotation on `private` typed properties - Data providers via `#[DataProvider]`; provider methods are `static` ## Which pattern to use? - **Facade/business logic** — put it on the project facade in `app/src/Model/…`; extend the framework base in `vendor/shopsys/…` rather than reimplementing. - **Extension** — extend a framework class in `app/src/…` only when you need to customize; otherwise use the framework class as-is. - **Controller** — admin controllers use PHP attributes for routing and permissions. - **GraphQL** — use the ResolverMap pattern for field resolution; mutations live in `app/src/FrontendApi/Mutation/`. - **React** — functional components (`FC<Props>`) with TypeScript. - **Testing** — modern PHPUnit with attributes and `@inject`; `final` classes, `private` properties. See `.agents/skills/shopsys-architecture/SKILL.md` for the extension rules and where each layer belongs. ## Pattern categories to search - **Inheritance** — `extends Base*`, `#[Override]`, `@property`/`@method` docblocks on thin project subclasses - **Business logic** — Facade methods, entity business methods and validation, service orchestration - **Data access** — Repository query methods, QueryBuilder usage, Doctrine relationships, multi-domain support - **Controllers** — CRUD with `#[Route]`, `#[CanEdit]`, `#[CanView]`, `#[ForRole(...)]`, flash messages, redirects - **GraphQL** — ResolverMaps, mutations in `app/src/FrontendApi/Mutation/`, DataMappers, entity-vs-array handling - **Storefront** — `FC<Props>` components, `use*.ts` hooks, URQL query/mutation integration, typed props - **Testing** — `#[DataProvider]`, `@inject`, `TransactionFunctionalTestCase`, Frontend API/GraphQL tests - **Forms** — FormType implementations and `*FormTypeExtension`, `DomainsType`/`MultidomainType`, scenarios (`SCENARIO_EDIT`, `SCENARIO_CREATE`) ### The standard Shopsys entity ecosystem When looking for entity-related patterns, expect this set (project class in `app/src/…` extending a framework base in `vendor/shopsys/…`): 1. **Entity** — `Product.php` extends framework `Product` 2. **EntityData** — `ProductData.php` (data-transfer object) 3. **EntityDataFactory** — `ProductDataFactory.php` 4. **EntityFacade** — `ProductFacade.php` extends framework facade 5. **EntityRepository** — `ProductRepository.php` extends framework repository 6. **EntityDomain** — `ProductDomain.php` (multi-domain) 7. **EntityTranslation** — `ProductTranslation.php` (translatable fields) ### Useful search terms - `extends Base*` — project classes extending framework bases - `#[Route`, `#[Override]` — modern attributes - `FC<` — React functional components - `useTranslation` — i18n hook usage - `@inject` — dependency injection in tests - `ResolverMap` — GraphQL field resolvers - `TransactionFunctionalTestCase` — functional test base ## Guidelines - **Show working code** — real snippets, not fragments out of context. - **Include context** — where and why it's used. - **Show variations** — multiple examples where they exist. - **Note the preferred approach** with evidence. - **Include tests** — show how the pattern is tested. - **Full paths from the repository root, with line numbers.** - **Prefer project examples**; cite the `vendor/shopsys/` base to complete the extension chain. ## What NOT to do - Don't show broken or deprecated patterns. - Don't include overly complex examples. - Don't miss the test examples. - Don't show patterns without context or recommend without evidence. - Don't treat `vendor/shopsys/` as editable — it's read-only reference.
View on GitHub