Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # JumUm Development Guidelines
- ## Project Overview
- JumUm is a comprehensive billing and subscription management system built with Symfony 7.2 (PHP 8.3+) backend and Vue.js 3 frontend. The project follows Domain-Driven Design principles with clean architecture patterns.
- ## Build/Configuration Instructions
- ### Prerequisites
- - PHP 8.3 or higher
- - Node.js with npm/yarn
- - Docker and Docker Compose
- - Composer
- ### Required PHP Extensions
- - ext-ctype, ext-http, ext-iconv, ext-pcntl, ext-redis, ext-simplexml, ext-xml
- ### Environment Setup
- #### Using Docker (Recommended)
- 1. Copy environment variables from `.env.dist` to `.env`
- 2. Configure database credentials in `.env`:
- ```
- PSQGL_DATABASE_NAME=JumUm
- PSQGL_DATABASE_USER=JumUm
- PSQGL_DATABASE_PASSWORD=your_password
- ```
- 3. Start services: `docker-compose up -d`
- #### Services Included
- - **PostgreSQL (TimescaleDB)**: Main database on port 5432
- - **Redis**: Cache and session storage on port 6379
- - **Elasticsearch**: Search engine on port 9200
- - **SMTP Server**: Email handling
- - **PHP-FPM**: PHP processing
- - **Nginx**: Web server on port 80
- - **Redis Commander**: Redis management UI on port 8081
- ### Installation Steps
- 1. Install PHP dependencies: `composer install`
- 2. Install Node.js dependencies: `npm install`
- 3. Build frontend assets: `npm run build` (production) or `npm run dev` (development)
- 4. Run database migrations: `php bin/console doctrine:migrations:migrate`
- ### Development Commands
- - **Frontend Development**: `npm run watch` (auto-rebuild on changes)
- - **Dev Server**: `npm run dev-server` (with hot reload)
- - **Translation Export**: `npm run export-translations`
- ## Testing Information
- ### PHP Testing (PHPUnit)
- #### Configuration
- - Configuration file: `phpunit.xml.dist`
- - Test directory: `tests/`
- - Coverage reports: `clover.xml`
- #### Running Tests
- ```bash
- # Run all tests
- ./vendor/bin/phpunit
- # Run specific test file
- ./vendor/bin/phpunit tests/Unit/ExampleTest.php
- # Run with coverage (requires Xdebug)
- ./vendor/bin/phpunit --coverage-html coverage/
- ```
- #### Creating Unit Tests
- - Place unit tests in `tests/Unit/`
- - Use namespace `JumUm\Tests\Unit`
- - Extend `PHPUnit\Framework\TestCase`
- - Example test structure:
- ```php
- <?php
- declare(strict_types=1);
- namespace JumUm\Tests\Unit;
- use PHPUnit\Framework\TestCase;
- class ExampleTest extends TestCase
- {
- public function testSomething(): void
- {
- $this->assertTrue(true);
- }
- }
- ```
- ### Behavior-Driven Development (Behat)
- #### Configuration
- - Configuration file: `behat.yml.dist`
- - Context files: `tests/Behat/`
- - Features: Organized by domain (Payments, Customers, Subscriptions, etc.)
- #### Running Behat Tests
- ```bash
- # Run all Behat tests
- ./vendor/bin/behat
- # Run specific feature
- ./vendor/bin/behat features/payments.feature
- ```
- #### Context Organization
- Contexts are organized by domain areas:
- - API contexts for API testing
- - App contexts for application testing
- - Portal contexts for customer portal testing
- - Main contexts for core functionality
- ### JavaScript Testing (Vitest)
- #### Configuration
- - Configuration file: `vitest.config.ts`
- - Test files: `assets/**/*.{test,spec}.{js,ts,jsx,tsx}`
- - Environment: jsdom for DOM testing
- #### Running JavaScript Tests
- ```bash
- # Run with Vitest (recommended)
- npx vitest run
- # Run specific test
- npx vitest run assets/example.test.js
- # Watch mode
- npx vitest
- ```
- #### Creating JavaScript Tests
- - Place tests in `assets/` directory
- - Use `.test.js` or `.spec.js` suffix
- - Example test structure:
- ```javascript
- import { describe, it, expect } from 'vitest'
- describe('Component Tests', () => {
- it('should test functionality', () => {
- expect(true).toBe(true)
- })
- })
- ```
- **Note**: The project has Jest configured but Vitest is the preferred testing framework for new tests.
- ## Code Style and Development Practices
- ### PHP Code Style
- #### PHP-CS-Fixer Configuration
- - Configuration file: `.php-cs-fixer.dist.php`
- - Based on Symfony coding standards
- - Additional rules enforced:
- - `ordered_imports`: Alphabetical import ordering
- - `ordered_attributes`: Consistent attribute ordering
- - `ordered_class_elements`: Consistent class member ordering
- - `yoda_style`: Yoda conditions (e.g., `null === $var`)
- - Copyright header requirement
- #### Running PHP-CS-Fixer
- ```bash
- # Fix code style issues
- ./vendor/bin/php-cs-fixer fix
- # Dry run (check without fixing)
- ./vendor/bin/php-cs-fixer fix --dry-run --diff
- ```
- #### Code Organization Patterns
- - **Domain-Driven Design**: Code organized by business domains
- - **Clean Architecture**: Separation of concerns with clear boundaries
- - **Directory Structure**:
- - `Controller/`: HTTP request handlers
- - `Entity/`: Domain entities
- - `DataMappers/`: Data access layer
- - `Dto/`: Data transfer objects
- - `Event/`: Domain events
- - `Command/`: Console commands
- - `Background/`: Background job handlers
- #### DTO (Data Transfer Object) Standards
- - **Usage**: DTOs are used in the controllers where they are created from the Serializer and the class name is passed to it. The resulting object is of the type of the DTO. And then used within the data mappers to convert into domain entities.
- - **Readonly Classes**: All DTOs MUST be declared as `readonly` classes
- - **Constructor Promotion**: All properties MUST use constructor promotion syntax
- - **Example Structure**:
- ```php
- <?php
- declare(strict_types=1);
- namespace JumUm\Dto\Request\Api;
- use Symfony\Component\Validator\Constraints as Assert;
- readonly class ExampleDto
- {
- public function __construct(
- #[Assert\NotBlank]
- public string $name,
- #[Assert\Type('integer')]
- public ?int $age = null,
- ) {
- }
- }
- ```
- ### Frontend Code Style
- - **Vue.js 3**: Composition API preferred
- - **Tailwind CSS**: Utility-first CSS framework
- - **TypeScript**: Preferred for new components
- - **Component Organization**: Feature-based structure
- ### Vue.js Development Guidelines
- #### Creating Vue Views and Components
- **Component Location**:
- - **Public Views**: Place in `assets/public/views/` for customer-facing components
- - **Internal Views**: Place in `assets/JumUm/views/` for admin/internal application components
- - **Reusable Components**: Place in `assets/JumUm/components/` or `assets/public/components/`
- **Component Structure**:
- - Use Vue 3 Composition API for new components
- - Follow PascalCase naming convention for component files
- - Organize components by feature/domain (e.g., `Customer/`, `Subscription/`, `Payment/`)
- #### Localization Requirements
- **Translation Files Location**:
- - **Source Files**: `assets/JumUm/translations/en/` (JavaScript files organized by feature)
- - **Compiled Files**: `assets/JumUm/translations/` (JSON files for each language)
- **String Localization Rules**:
- - **MANDATORY**: All user-visible strings in Vue templates MUST use localization IDs via `$t()` function
- - **NO hardcoded strings**: Never use hardcoded text in templates
- - **Localization Key Format**: Use dot notation matching component hierarchy (e.g., `app.customer.list.title`)
- **Required Language Support**:
- All localization strings must be translated into:
- - **English** (en) - Primary language
- - **German** (de)
- - **Spanish** (es)
- - **Dutch** (nl)
- - **Portuguese** (pt)
- **Example Usage**:
- ```vue
- <template>
- <!-- CORRECT: Using localization ID -->
- <h1>{{ $t('app.customer.list.title') }}</h1>
- <button>{{ $t('app.customer.list.create_btn') }}</button>
- <!-- INCORRECT: Hardcoded strings -->
- <h1>Customer List</h1>
- <button>Create Customer</button>
- </template>
- ```
- **Translation File Structure**:
- ```javascript
- // assets/JumUm/translations/en/app/customer/list.js
- export const CUSTOMER_LIST_TRANSLATIONS = {
- title: "Customer List",
- create_btn: "Create Customer",
- no_customers: "No customers found",
- filter: {
- name: "Name",
- email: "Email"
- }
- }
- ```
- **Translation Organization Pattern**:
- The translation system follows a hierarchical organization pattern with multiple levels of aggregation:
- 1. **Functionality Level**: Individual JavaScript files contain translations for specific functionality
- ```javascript
- // assets/JumUm/translations/en/app/customer/list.js
- export const CUSTOMER_LIST_TRANSLATIONS = {
- title: "Customers",
- // other translations...
- }
- ```
- 2. **Feature Level**: Index files in each feature directory import and aggregate functionality translations
- ```javascript
- // assets/JumUm/translations/en/app/customer/index.js
- import {CUSTOMER_LIST_TRANSLATIONS} from "./list";
- import {CUSTOMER_CREATE_TRANSLATIONS} from './create';
- // other imports...
- export const CUSTOMER_TRANSLATIONS = {
- list: CUSTOMER_LIST_TRANSLATIONS,
- create: CUSTOMER_CREATE_TRANSLATIONS,
- // other translations...
- }
- ```
- 3. **Application Level**: The app index file imports and aggregates all feature translations
- ```javascript
- // assets/JumUm/translations/en/app/index.js
- import {CUSTOMER_TRANSLATIONS} from "./customer";
- import {PRODUCT_TRANSLATIONS} from "./product";
- // other imports...
- export const APP_TRANSLATIONS = {
- customer: CUSTOMER_TRANSLATIONS,
- product: PRODUCT_TRANSLATIONS,
- // other translations...
- };
- ```
- 4. **Top Level**: The language file imports and aggregates all application translations
- ```javascript
- // assets/JumUm/translations/en.js
- import {APP_TRANSLATIONS} from "./en/app";
- import {PUBLIC_TRANSLATIONS} from "./en/public";
- // other imports...
- export const ENGLISH_TRANSLATIONS = {
- app: APP_TRANSLATIONS,
- public: PUBLIC_TRANSLATIONS,
- // other translations...
- }
- ```
- 5. **Compilation**: JavaScript translations are compiled into JSON files for use in the application
- - Compilation is done using the `export-translations` script in package.json
- - The script imports the top-level translations and writes them to JSON files
- - Each supported language has its own JSON file (en.json, de.json, etc.)
- This hierarchical organization allows for:
- - Clear separation of translations by functionality and feature
- - Easy maintenance and updates of translations
- - Consistent naming and organization across the application
- - Efficient compilation into JSON files for runtime use
- ### Development Standards
- #### Naming Conventions
- - **PHP Classes**: PascalCase
- - **PHP Methods/Variables**: camelCase
- - **Constants**: UPPER_SNAKE_CASE
- - **Database Tables**: snake_case
- - **Vue Components**: PascalCase
- #### Architecture Principles
- - **Single Responsibility**: Each class has one reason to change
- - **Dependency Injection**: Use Symfony's DI container
- - **Event-Driven**: Use domain events for decoupling
- - **API-First**: Design APIs before implementation
- #### Cloud Namespace Usage Guidelines
- **Overview**:
- The `Cloud` namespace is used to provide multi-tenant, cloud-hosted replacements for `JumUm` components. Use the Cloud namespace when creating components that need to be replaceable for the cloud hosted multi-tenant version of JumUm.
- **When to Use Cloud Namespace**:
- - **Multi-tenant Functionality**: When a component needs tenant-aware behavior
- - **Cloud-specific Features**: When implementing features exclusive to the cloud version
- - **Service Replacements**: When replacing a JumUm service with cloud-specific logic
- - **URL Generation**: When generating tenant-specific URLs or links
- - **Data Isolation**: When implementing tenant data separation
- **Namespace Structure**:
- The Cloud namespace mirrors the JumUm namespace structure:
- ```
- Cloud\
- ├── Controller\ # Cloud-specific controllers
- ├── Entity\ # Cloud-specific entities (e.g., Tenant)
- ├── Repository\ # Cloud-aware repositories
- ├── Service\ # Multi-tenant services
- ├── EventListener\ # Cloud-specific event listeners
- ├── Messenger\ # Tenant-aware message handling
- └── ... # Other domain areas
- ```
- **Implementation Pattern**:
- Cloud classes should implement JumUm interfaces to ensure compatibility:
- ```php
- <?php
- namespace Cloud\YourDomain;
- use JumUm\YourDomain\YourServiceInterface;
- use Parthenon\MultiTenancy\TenantProvider\TenantProviderInterface;
- class YourService implements YourServiceInterface
- {
- public function __construct(
- private TenantProviderInterface $tenantProvider,
- // other dependencies
- ) {
- }
- public function someMethod(): string
- {
- $tenant = $this->tenantProvider->getCurrentTenant();
- // Implement tenant-aware logic
- return $this->generateTenantSpecificResult($tenant);
- }
- }
- ```
- **Service Registration**:
- Register Cloud services to replace JumUm services in `config/services.yaml`:
- ```yaml
- services:
- # Replace JumUm service with Cloud version
- JumUm\YourDomain\YourServiceInterface:
- class: Cloud\YourDomain\YourService
- arguments:
- $tenantProvider: '@parthenon.multi_tenancy.tenant_provider'
- ```
- **Common Use Cases**:
- **URL Generation**:
- ```php
- // Cloud version adds tenant subdomain to URLs
- namespace Cloud\Checkout;
- class PortalLinkGenerator implements PortalLinkGeneratorInterface
- {
- public function generatePayLink(Checkout $checkout): string
- {
- $tenant = $this->tenantProvider->getCurrentTenant();
- return $this->urlGenerator->generate(
- 'portal_pay_checkout',
- ['slug' => $checkout->getSlug(), 'tenant' => $tenant->getSubdomain()],
- UrlGeneratorInterface::ABSOLUTE_URL
- );
- }
- }
- ```
- **Repository Extensions**:
- ```php
- // Cloud repositories add tenant filtering
- namespace Cloud\Repository\Orm;
- class CustomerRepository extends JumUmCustomerRepository
- {
- public function findByTenant(TenantInterface $tenant): array
- {
- return $this->createQueryBuilder('c')
- ->where('c.tenant = :tenant')
- ->setParameter('tenant', $tenant)
- ->getQuery()
- ->getResult();
- }
- }
- ```
- **Best Practices**:
- - Always implement the corresponding JumUm interface
- - Use dependency injection for `TenantProviderInterface`
- - Maintain the same method signatures as JumUm counterparts
- - Add tenant-specific logic without breaking existing functionality
- - Follow the same coding standards as JumUm components
- - Include proper error handling for tenant-related operations
- **Testing Cloud Components**:
- - Create unit tests in `tests/Unit/Cloud/`
- - Mock `TenantProviderInterface` in tests
- - Test both tenant-aware and fallback behaviors
- - Ensure compatibility with JumUm interfaces
- #### Database Practices
- - **Migrations**: All schema changes via Doctrine migrations
- - **Multi-tenancy**: Separate migration directory for tenant-specific changes
- - **TimescaleDB**: Optimized for time-series data
- ### Debugging and Development Tools
- #### Symfony Profiler
- - Available in development environment
- - Access via `/_profiler` route
- - Includes database queries, performance metrics, and request details
- #### Redis Commander
- - Web UI available at `http://localhost:8081`
- - Credentials: root/root
- - Monitor cache and session data
- #### Elasticsearch
- - Direct access on port 9200
- - Use for search functionality debugging
- ### Integration Guidelines
- #### Payment Providers
- - **Stripe**: Primary payment processor
- - **Interoperability Layer**: Abstraction for multiple providers
- - **Webhook Handling**: Secure webhook processing
- #### Third-party Services
- - **Email**: Multiple providers (Mailgun, Postmark, etc.)
- - **PDF Generation**: Multiple engines (mPDF, Snappy)
- - **CRM Integration**: HubSpot, Salesforce support
- #### CRM Integration Development
- **Overview**:
- CRM integrations in JumUm allow synchronization of customer data and email logging with external CRM systems. The system uses a plugin-based architecture with standardized interfaces.
- **Core Architecture**:
- **Required Interfaces**:
- - `CrmIntegrationInterface`: Main integration contract
- - `IntegrationInterface`: Base integration interface
- - `CustomerServiceInterface`: Customer data operations
- - `EmailServiceInterface`: Email logging operations
- **Integration Structure**:
- ```php
- <?php
- namespace JumUm\Integrations\Crm\YourCrm;
- use JumUm\Integrations\Crm\CrmIntegrationInterface;
- use JumUm\Integrations\IntegrationInterface;
- use JumUm\Integrations\IntegrationType;
- use JumUm\Integrations\AuthenticationType;
- class YourCrmIntegration implements CrmIntegrationInterface, IntegrationInterface
- {
- public function getCustomerService(): CustomerServiceInterface
- {
- return new CustomerService($this->getClient());
- }
- public function getEmailService(): EmailServiceInterface
- {
- return new EmailService($this->getClient());
- }
- public function getType(): IntegrationType
- {
- return IntegrationType::CRM;
- }
- public function getName(): string
- {
- return 'your_crm_name';
- }
- public function getAuthenticationType(): AuthenticationType
- {
- return AuthenticationType::OAUTH; // or API_KEY
- }
- // Additional required methods...
- }
- ```
- **Customer Service Implementation**:
- ```php
- <?php
- namespace JumUm\Integrations\Crm\YourCrm;
- use JumUm\Integrations\Crm\CustomerServiceInterface;
- use JumUm\Entity\Customer;
- class CustomerService implements CustomerServiceInterface
- {
- public function registerCompany(Customer $customer): CustomerRegistration
- {
- // Create company in CRM
- // Return CustomerRegistration with reference and contactReference
- }
- public function registerContact(Customer $customer): ContactRegistration
- {
- // Create contact in CRM
- // Return ContactRegistration with reference
- }
- public function update(Customer $customer): void
- {
- // Update existing customer in CRM
- }
- public function search(Customer $customer): ?CustomerProfile
- {
- // Search for customer in CRM by email
- // Return CustomerProfile with CRM data or null if not found
- }
- }
- ```
- **Email Service Implementation**:
- ```php
- <?php
- namespace JumUm\Integrations\Crm\YourCrm;
- use JumUm\Integrations\Crm\EmailServiceInterface;
- use JumUm\Entity\Customer;
- use JumUm\Notification\Email\Email;
- class EmailService implements EmailServiceInterface
- {
- public function registerEmail(Customer $customer, Email $email): void
- {
- // Log email activity in CRM system
- }
- }
- ```
- **Required DTOs**:
- All DTOs must be `readonly` classes using constructor promotion:
- ```php
- // CustomerRegistration.php
- readonly class CustomerRegistration
- {
- public function __construct(
- public string $reference, // CRM company ID
- public string $contactReference, // CRM contact ID
- ) {}
- }
- // ContactRegistration.php
- readonly class ContactRegistration
- {
- public function __construct(
- public string $reference, // CRM contact ID
- ) {}
- }
- // CustomerProfile.php
- readonly class CustomerProfile
- {
- public function __construct(
- public string $reference,
- public ?string $contactReference,
- public ?string $name,
- public ?string $city,
- public ?string $state,
- public ?string $postCode,
- public ?string $country,
- ) {}
- }
- ```
- **Authentication Patterns**:
- **OAuth Integration**:
- ```php
- public function getOauthConfig(): OauthConfig
- {
- return new OauthConfig(
- $this->clientId,
- $this->clientSecret,
- $this->redirectUri,
- 'https://your-crm.com/oauth/authorize',
- 'https://your-crm.com/oauth/token',
- '',
- 'required scopes'
- );
- }
- public function getAccountId(): string
- {
- // Return unique account identifier from CRM
- }
- ```
- **Environment Configuration**:
- ```env
- YOUR_CRM_CLIENT_ID=your_client_id
- YOUR_CRM_CLIENT_SECRET=your_client_secret
- YOUR_CRM_REDIRECT_URI=https://your-app.com/oauth/callback
- ```
- **Service Registration**:
- Register your integration in `config/services.yaml`:
- ```yaml
- services:
- JumUm\Integrations\Crm\YourCrm\YourCrmIntegration:
- arguments:
- $clientId: '%env(YOUR_CRM_CLIENT_ID)%'
- $clientSecret: '%env(YOUR_CRM_CLIENT_SECRET)%'
- $redirectUri: '%env(YOUR_CRM_REDIRECT_URI)%'
- tags:
- - { name: 'JumUm.integration' }
- ```
- **Testing Requirements**:
- Create unit tests for your integration:
- ```php
- <?php
- namespace JumUm\Tests\Unit\Integrations\Crm\YourCrm;
- use PHPUnit\Framework\TestCase;
- use JumUm\Integrations\Crm\YourCrm\YourCrmIntegration;
- class YourCrmIntegrationTest extends TestCase
- {
- public function testGetType(): void
- {
- $integration = new YourCrmIntegration(/* dependencies */);
- $this->assertEquals(IntegrationType::CRM, $integration->getType());
- }
- public function testGetName(): void
- {
- $integration = new YourCrmIntegration(/* dependencies */);
- $this->assertEquals('your_crm_name', $integration->getName());
- }
- }
- ```
- **Error Handling**:
- - Use `CrmIntegrationFailure` webhook payload for integration failures
- - Implement proper logging using `LoggerAwareTrait`
- - Handle API rate limits and connection errors gracefully
- **Directory Structure**:
- ```
- src/JumUm/Integrations/Crm/YourCrm/
- ├── YourCrmIntegration.php
- ├── CustomerService.php
- ├── EmailService.php
- └── Client/ (if needed for API wrapper)
- ```
- **Best Practices**:
- - Use readonly DTOs with constructor promotion
- - Implement proper OAuth token refresh handling
- - Cache API responses when appropriate
- - Follow the existing HubSpot integration as a reference
- - Use dependency injection for all external dependencies
- - Implement comprehensive error handling and logging
- ### Performance Considerations
- - **Caching**: Redis for application cache
- - **Database**: TimescaleDB for time-series optimization
- - **Asset Optimization**: Webpack Encore for frontend builds
- - **Background Jobs**: Symfony Messenger for async processing
- ### Security Practices
- - **Input Validation**: Symfony Validator component
- - **CSRF Protection**: Built-in Symfony protection
- - **API Authentication**: Token-based authentication
- - **Data Encryption**: Sensitive data encryption at rest
- #### Repository Design Patterns
- **Overview**:
- JumUm follows a layered repository pattern that provides a clean separation between domain logic and data access. The repository pattern is implemented using Doctrine ORM with a two-layer approach that separates interfaces from implementations and provides additional abstraction for database operations.
- **Repository Structure**:
- ```
- JumUm\Repository\
- ├── {Entity}RepositoryInterface.php # Domain-specific repository interfaces
- ├── {Entity}Repository.php # Service repository implementations
- └── Orm\
- └── {Entity}Repository.php # ORM-specific repository implementations
- ```
- **Repository Layers**:
- - **Interface Layer**: Defines the contract for repository operations (`{Entity}RepositoryInterface`)
- - **Service Layer**: Implements domain-specific logic and extends `DoctrineCrudRepository` (`{Entity}Repository`)
- - **ORM Layer**: Handles direct database interactions and extends `CustomServiceRepository` (`Orm\{Entity}Repository`)
- **Repository Inheritance Hierarchy**:
- 1. Repository interfaces extend base interfaces:
- ```php
- interface CustomerRepositoryInterface extends \Parthenon\Billing\Repository\CustomerRepositoryInterface
- {
- // Domain-specific method declarations
- }
- ```
- 2. Service repositories extend base repositories and implement interfaces:
- ```php
- class CustomerRepository extends DoctrineCrudRepository implements CustomerRepositoryInterface
- {
- // Domain-specific method implementations
- }
- ```
- 3. ORM repositories extend Doctrine's repository base:
- ```php
- class CustomerRepository extends CustomServiceRepository
- {
- // Database-specific implementations
- }
- ```
- **Repository Configuration**:
- Repositories are registered in the dependency injection container in `config/services/core/main.yaml`:
- 1. Interface to implementation binding:
- ```yaml
- JumUm\Repository\CustomerRepositoryInterface: '@JumUm\Repository\CustomerRepository'
- ```
- 2. Service repository configuration:
- ```yaml
- JumUm\Repository\CustomerRepository:
- arguments:
- $entityRepository: '@app.repository.orm.customer'
- ```
- 3. ORM repository registration:
- ```yaml
- app.repository.orm.customer:
- class: JumUm\Repository\Orm\CustomerRepository
- ```
- **Repository Usage Patterns**:
- 1. **Constructor Injection**: Repositories are injected via constructor:
- ```php
- public function __construct(
- private CustomerRepositoryInterface $customerRepository,
- // other dependencies
- ) {
- }
- ```
- 2. **Interface Dependency**: Always depend on repository interfaces, not implementations:
- ```php
- use JumUm\Repository\CustomerRepositoryInterface;
- class CustomerService
- {
- public function __construct(private CustomerRepositoryInterface $customerRepository)
- {
- }
- }
- ```
- 3. **Domain-Specific Methods**: Repositories provide domain-specific query methods:
- ```php
- // Finding by domain criteria
- $customer = $customerRepository->findByEmail('[email protected]');
- // Domain-specific operations
- $customerRepository->wipeCustomerSupportReferences();
- ```
- **Creating New Repositories**:
- 1. **Define the Interface**:
- ```php
- <?php
- namespace JumUm\Repository;
- use JumUm\Entity\YourEntity;
- use Parthenon\Common\Repository\RepositoryInterface;
- interface YourEntityRepositoryInterface extends RepositoryInterface
- {
- public function findByUniqueIdentifier(string $identifier): YourEntity;
- // Other domain-specific methods
- }
- ```
- 2. **Create the ORM Repository**:
- ```php
- <?php
- namespace JumUm\Repository\Orm;
- use Parthenon\Common\Repository\CustomServiceRepository;
- class YourEntityRepository extends CustomServiceRepository
- {
- // ORM-specific implementations if needed
- }
- ```
- 3. **Implement the Service Repository**:
- ```php
- <?php
- namespace JumUm\Repository;
- use JumUm\Entity\YourEntity;
- use Parthenon\Athena\Repository\DoctrineCrudRepository;
- use Parthenon\Common\Exception\NoEntityFoundException;
- class YourEntityRepository extends DoctrineCrudRepository implements YourEntityRepositoryInterface
- {
- public function findByUniqueIdentifier(string $identifier): YourEntity
- {
- $entity = $this->entityRepository->findOneBy(['uniqueIdentifier' => $identifier]);
- if (!$entity instanceof YourEntity) {
- throw new NoEntityFoundException(sprintf("No entity found for identifier '%s'", $identifier));
- }
- return $entity;
- }
- // Other domain-specific method implementations
- }
- ```
- 4. **Register in Service Container**:
- ```yaml
- # config/services/core/main.yaml
- JumUm\Repository\YourEntityRepositoryInterface: '@JumUm\Repository\YourEntityRepository'
- JumUm\Repository\YourEntityRepository:
- arguments:
- $entityRepository: '@app.repository.orm.your_entity'
- app.repository.orm.your_entity:
- class: JumUm\Repository\Orm\YourEntityRepository
- ```
- **Best Practices**:
- - Always define and depend on repository interfaces, not implementations
- - Keep domain logic in service repositories, database logic in ORM repositories
- - Use constructor injection for repository dependencies
- - Follow consistent naming conventions for repository methods
- - Document exceptions that may be thrown by repository methods
- - Use proper type declarations for parameters and return types
- - Implement proper error handling with domain-specific exceptions
- - Write unit tests for repository implementations
- - Extend DoctrineRepository or DoctrineCrudRepository from Parthenon.
- **Cloud Multi-tenancy Support**:
- For multi-tenant environments, extend repositories in the Cloud namespace:
- ```php
- <?php
- namespace Cloud\Repository\Orm;
- use JumUm\Repository\Orm\YourEntityRepository as JumUmYourEntityRepository;
- use Parthenon\MultiTenancy\Entity\TenantInterface;
- class YourEntityRepository extends JumUmYourEntityRepository
- {
- public function findByTenant(TenantInterface $tenant): array
- {
- return $this->createQueryBuilder('e')
- ->where('e.tenant = :tenant')
- ->setParameter('tenant', $tenant)
- ->getQuery()
- ->getResult();
- }
- }
- ```
- **Testing Repositories**:
- - Create unit tests in `tests/Unit/Repository/`
- - Use in-memory SQLite database for repository tests
- - Test both success and failure scenarios
- - Mock the entity manager for pure unit tests
- This document should be updated as the project evolves and new patterns or tools are introduced.
- # Git Usage during Development
- You must follow ./git_usage.md for all tasks.
- # Documentation
- * Documentation must be written in British English and not American English.
- * Documentation must be written in Markdown format.
- * Documentation in public_doc/user must be written in a way that is easy to understand for non-technical users.
- * Documentation in public_doc/technical must be written in a way that is easy to understand for technical users.
- * Documentation must be thorough and complete
- * Documentation should include examples where appropriate
- * Documentation should be written in a way that is easy to read and understand
- * Documentation should be written in a way that is easy to navigate
Advertisement
Add Comment
Please, Sign In to add comment