somafsdsdf

Untitled

Aug 28th, 2025
101
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 29.87 KB | None | 0 0
  1. # JumUm Development Guidelines
  2.  
  3. ## Project Overview
  4. 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.
  5.  
  6. ## Build/Configuration Instructions
  7.  
  8. ### Prerequisites
  9. - PHP 8.3 or higher
  10. - Node.js with npm/yarn
  11. - Docker and Docker Compose
  12. - Composer
  13.  
  14. ### Required PHP Extensions
  15. - ext-ctype, ext-http, ext-iconv, ext-pcntl, ext-redis, ext-simplexml, ext-xml
  16.  
  17. ### Environment Setup
  18.  
  19. #### Using Docker (Recommended)
  20. 1. Copy environment variables from `.env.dist` to `.env`
  21. 2. Configure database credentials in `.env`:
  22. ```
  23. PSQGL_DATABASE_NAME=JumUm
  24. PSQGL_DATABASE_USER=JumUm
  25. PSQGL_DATABASE_PASSWORD=your_password
  26. ```
  27. 3. Start services: `docker-compose up -d`
  28.  
  29. #### Services Included
  30. - **PostgreSQL (TimescaleDB)**: Main database on port 5432
  31. - **Redis**: Cache and session storage on port 6379
  32. - **Elasticsearch**: Search engine on port 9200
  33. - **SMTP Server**: Email handling
  34. - **PHP-FPM**: PHP processing
  35. - **Nginx**: Web server on port 80
  36. - **Redis Commander**: Redis management UI on port 8081
  37.  
  38. ### Installation Steps
  39. 1. Install PHP dependencies: `composer install`
  40. 2. Install Node.js dependencies: `npm install`
  41. 3. Build frontend assets: `npm run build` (production) or `npm run dev` (development)
  42. 4. Run database migrations: `php bin/console doctrine:migrations:migrate`
  43.  
  44. ### Development Commands
  45. - **Frontend Development**: `npm run watch` (auto-rebuild on changes)
  46. - **Dev Server**: `npm run dev-server` (with hot reload)
  47. - **Translation Export**: `npm run export-translations`
  48.  
  49. ## Testing Information
  50.  
  51. ### PHP Testing (PHPUnit)
  52.  
  53. #### Configuration
  54. - Configuration file: `phpunit.xml.dist`
  55. - Test directory: `tests/`
  56. - Coverage reports: `clover.xml`
  57.  
  58. #### Running Tests
  59. ```bash
  60. # Run all tests
  61. ./vendor/bin/phpunit
  62.  
  63. # Run specific test file
  64. ./vendor/bin/phpunit tests/Unit/ExampleTest.php
  65.  
  66. # Run with coverage (requires Xdebug)
  67. ./vendor/bin/phpunit --coverage-html coverage/
  68. ```
  69.  
  70. #### Creating Unit Tests
  71. - Place unit tests in `tests/Unit/`
  72. - Use namespace `JumUm\Tests\Unit`
  73. - Extend `PHPUnit\Framework\TestCase`
  74. - Example test structure:
  75. ```php
  76. <?php
  77. declare(strict_types=1);
  78.  
  79. namespace JumUm\Tests\Unit;
  80.  
  81. use PHPUnit\Framework\TestCase;
  82.  
  83. class ExampleTest extends TestCase
  84. {
  85. public function testSomething(): void
  86. {
  87. $this->assertTrue(true);
  88. }
  89. }
  90. ```
  91.  
  92. ### Behavior-Driven Development (Behat)
  93.  
  94. #### Configuration
  95. - Configuration file: `behat.yml.dist`
  96. - Context files: `tests/Behat/`
  97. - Features: Organized by domain (Payments, Customers, Subscriptions, etc.)
  98.  
  99. #### Running Behat Tests
  100. ```bash
  101. # Run all Behat tests
  102. ./vendor/bin/behat
  103.  
  104. # Run specific feature
  105. ./vendor/bin/behat features/payments.feature
  106. ```
  107.  
  108. #### Context Organization
  109. Contexts are organized by domain areas:
  110. - API contexts for API testing
  111. - App contexts for application testing
  112. - Portal contexts for customer portal testing
  113. - Main contexts for core functionality
  114.  
  115. ### JavaScript Testing (Vitest)
  116.  
  117. #### Configuration
  118. - Configuration file: `vitest.config.ts`
  119. - Test files: `assets/**/*.{test,spec}.{js,ts,jsx,tsx}`
  120. - Environment: jsdom for DOM testing
  121.  
  122. #### Running JavaScript Tests
  123. ```bash
  124. # Run with Vitest (recommended)
  125. npx vitest run
  126.  
  127. # Run specific test
  128. npx vitest run assets/example.test.js
  129.  
  130. # Watch mode
  131. npx vitest
  132. ```
  133.  
  134. #### Creating JavaScript Tests
  135. - Place tests in `assets/` directory
  136. - Use `.test.js` or `.spec.js` suffix
  137. - Example test structure:
  138. ```javascript
  139. import { describe, it, expect } from 'vitest'
  140.  
  141. describe('Component Tests', () => {
  142. it('should test functionality', () => {
  143. expect(true).toBe(true)
  144. })
  145. })
  146. ```
  147.  
  148. **Note**: The project has Jest configured but Vitest is the preferred testing framework for new tests.
  149.  
  150. ## Code Style and Development Practices
  151.  
  152. ### PHP Code Style
  153.  
  154. #### PHP-CS-Fixer Configuration
  155. - Configuration file: `.php-cs-fixer.dist.php`
  156. - Based on Symfony coding standards
  157. - Additional rules enforced:
  158. - `ordered_imports`: Alphabetical import ordering
  159. - `ordered_attributes`: Consistent attribute ordering
  160. - `ordered_class_elements`: Consistent class member ordering
  161. - `yoda_style`: Yoda conditions (e.g., `null === $var`)
  162. - Copyright header requirement
  163.  
  164. #### Running PHP-CS-Fixer
  165. ```bash
  166. # Fix code style issues
  167. ./vendor/bin/php-cs-fixer fix
  168.  
  169. # Dry run (check without fixing)
  170. ./vendor/bin/php-cs-fixer fix --dry-run --diff
  171. ```
  172.  
  173. #### Code Organization Patterns
  174. - **Domain-Driven Design**: Code organized by business domains
  175. - **Clean Architecture**: Separation of concerns with clear boundaries
  176. - **Directory Structure**:
  177. - `Controller/`: HTTP request handlers
  178. - `Entity/`: Domain entities
  179. - `DataMappers/`: Data access layer
  180. - `Dto/`: Data transfer objects
  181. - `Event/`: Domain events
  182. - `Command/`: Console commands
  183. - `Background/`: Background job handlers
  184.  
  185. #### DTO (Data Transfer Object) Standards
  186. - **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.
  187. - **Readonly Classes**: All DTOs MUST be declared as `readonly` classes
  188. - **Constructor Promotion**: All properties MUST use constructor promotion syntax
  189. - **Example Structure**:
  190. ```php
  191. <?php
  192. declare(strict_types=1);
  193.  
  194. namespace JumUm\Dto\Request\Api;
  195.  
  196. use Symfony\Component\Validator\Constraints as Assert;
  197.  
  198. readonly class ExampleDto
  199. {
  200. public function __construct(
  201. #[Assert\NotBlank]
  202. public string $name,
  203. #[Assert\Type('integer')]
  204. public ?int $age = null,
  205. ) {
  206. }
  207. }
  208. ```
  209.  
  210. ### Frontend Code Style
  211. - **Vue.js 3**: Composition API preferred
  212. - **Tailwind CSS**: Utility-first CSS framework
  213. - **TypeScript**: Preferred for new components
  214. - **Component Organization**: Feature-based structure
  215.  
  216. ### Vue.js Development Guidelines
  217.  
  218. #### Creating Vue Views and Components
  219.  
  220. **Component Location**:
  221. - **Public Views**: Place in `assets/public/views/` for customer-facing components
  222. - **Internal Views**: Place in `assets/JumUm/views/` for admin/internal application components
  223. - **Reusable Components**: Place in `assets/JumUm/components/` or `assets/public/components/`
  224.  
  225. **Component Structure**:
  226. - Use Vue 3 Composition API for new components
  227. - Follow PascalCase naming convention for component files
  228. - Organize components by feature/domain (e.g., `Customer/`, `Subscription/`, `Payment/`)
  229.  
  230. #### Localization Requirements
  231.  
  232. **Translation Files Location**:
  233. - **Source Files**: `assets/JumUm/translations/en/` (JavaScript files organized by feature)
  234. - **Compiled Files**: `assets/JumUm/translations/` (JSON files for each language)
  235.  
  236. **String Localization Rules**:
  237. - **MANDATORY**: All user-visible strings in Vue templates MUST use localization IDs via `$t()` function
  238. - **NO hardcoded strings**: Never use hardcoded text in templates
  239. - **Localization Key Format**: Use dot notation matching component hierarchy (e.g., `app.customer.list.title`)
  240.  
  241. **Required Language Support**:
  242. All localization strings must be translated into:
  243. - **English** (en) - Primary language
  244. - **German** (de)
  245. - **Spanish** (es)
  246. - **Dutch** (nl)
  247. - **Portuguese** (pt)
  248.  
  249. **Example Usage**:
  250. ```vue
  251. <template>
  252. <!-- CORRECT: Using localization ID -->
  253. <h1>{{ $t('app.customer.list.title') }}</h1>
  254. <button>{{ $t('app.customer.list.create_btn') }}</button>
  255.  
  256. <!-- INCORRECT: Hardcoded strings -->
  257. <h1>Customer List</h1>
  258. <button>Create Customer</button>
  259. </template>
  260. ```
  261.  
  262. **Translation File Structure**:
  263. ```javascript
  264. // assets/JumUm/translations/en/app/customer/list.js
  265. export const CUSTOMER_LIST_TRANSLATIONS = {
  266. title: "Customer List",
  267. create_btn: "Create Customer",
  268. no_customers: "No customers found",
  269. filter: {
  270. name: "Name",
  271. email: "Email"
  272. }
  273. }
  274. ```
  275.  
  276. **Translation Organization Pattern**:
  277.  
  278. The translation system follows a hierarchical organization pattern with multiple levels of aggregation:
  279.  
  280. 1. **Functionality Level**: Individual JavaScript files contain translations for specific functionality
  281. ```javascript
  282. // assets/JumUm/translations/en/app/customer/list.js
  283. export const CUSTOMER_LIST_TRANSLATIONS = {
  284. title: "Customers",
  285. // other translations...
  286. }
  287. ```
  288.  
  289. 2. **Feature Level**: Index files in each feature directory import and aggregate functionality translations
  290. ```javascript
  291. // assets/JumUm/translations/en/app/customer/index.js
  292. import {CUSTOMER_LIST_TRANSLATIONS} from "./list";
  293. import {CUSTOMER_CREATE_TRANSLATIONS} from './create';
  294. // other imports...
  295.  
  296. export const CUSTOMER_TRANSLATIONS = {
  297. list: CUSTOMER_LIST_TRANSLATIONS,
  298. create: CUSTOMER_CREATE_TRANSLATIONS,
  299. // other translations...
  300. }
  301. ```
  302.  
  303. 3. **Application Level**: The app index file imports and aggregates all feature translations
  304. ```javascript
  305. // assets/JumUm/translations/en/app/index.js
  306. import {CUSTOMER_TRANSLATIONS} from "./customer";
  307. import {PRODUCT_TRANSLATIONS} from "./product";
  308. // other imports...
  309.  
  310. export const APP_TRANSLATIONS = {
  311. customer: CUSTOMER_TRANSLATIONS,
  312. product: PRODUCT_TRANSLATIONS,
  313. // other translations...
  314. };
  315. ```
  316.  
  317. 4. **Top Level**: The language file imports and aggregates all application translations
  318. ```javascript
  319. // assets/JumUm/translations/en.js
  320. import {APP_TRANSLATIONS} from "./en/app";
  321. import {PUBLIC_TRANSLATIONS} from "./en/public";
  322. // other imports...
  323.  
  324. export const ENGLISH_TRANSLATIONS = {
  325. app: APP_TRANSLATIONS,
  326. public: PUBLIC_TRANSLATIONS,
  327. // other translations...
  328. }
  329. ```
  330.  
  331. 5. **Compilation**: JavaScript translations are compiled into JSON files for use in the application
  332. - Compilation is done using the `export-translations` script in package.json
  333. - The script imports the top-level translations and writes them to JSON files
  334. - Each supported language has its own JSON file (en.json, de.json, etc.)
  335.  
  336. This hierarchical organization allows for:
  337. - Clear separation of translations by functionality and feature
  338. - Easy maintenance and updates of translations
  339. - Consistent naming and organization across the application
  340. - Efficient compilation into JSON files for runtime use
  341.  
  342. ### Development Standards
  343.  
  344. #### Naming Conventions
  345. - **PHP Classes**: PascalCase
  346. - **PHP Methods/Variables**: camelCase
  347. - **Constants**: UPPER_SNAKE_CASE
  348. - **Database Tables**: snake_case
  349. - **Vue Components**: PascalCase
  350.  
  351. #### Architecture Principles
  352. - **Single Responsibility**: Each class has one reason to change
  353. - **Dependency Injection**: Use Symfony's DI container
  354. - **Event-Driven**: Use domain events for decoupling
  355. - **API-First**: Design APIs before implementation
  356.  
  357. #### Cloud Namespace Usage Guidelines
  358.  
  359. **Overview**:
  360. 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.
  361.  
  362. **When to Use Cloud Namespace**:
  363. - **Multi-tenant Functionality**: When a component needs tenant-aware behavior
  364. - **Cloud-specific Features**: When implementing features exclusive to the cloud version
  365. - **Service Replacements**: When replacing a JumUm service with cloud-specific logic
  366. - **URL Generation**: When generating tenant-specific URLs or links
  367. - **Data Isolation**: When implementing tenant data separation
  368.  
  369. **Namespace Structure**:
  370. The Cloud namespace mirrors the JumUm namespace structure:
  371. ```
  372. Cloud\
  373. ├── Controller\ # Cloud-specific controllers
  374. ├── Entity\ # Cloud-specific entities (e.g., Tenant)
  375. ├── Repository\ # Cloud-aware repositories
  376. ├── Service\ # Multi-tenant services
  377. ├── EventListener\ # Cloud-specific event listeners
  378. ├── Messenger\ # Tenant-aware message handling
  379. └── ... # Other domain areas
  380. ```
  381.  
  382. **Implementation Pattern**:
  383. Cloud classes should implement JumUm interfaces to ensure compatibility:
  384.  
  385. ```php
  386. <?php
  387. namespace Cloud\YourDomain;
  388.  
  389. use JumUm\YourDomain\YourServiceInterface;
  390. use Parthenon\MultiTenancy\TenantProvider\TenantProviderInterface;
  391.  
  392. class YourService implements YourServiceInterface
  393. {
  394. public function __construct(
  395. private TenantProviderInterface $tenantProvider,
  396. // other dependencies
  397. ) {
  398. }
  399.  
  400. public function someMethod(): string
  401. {
  402. $tenant = $this->tenantProvider->getCurrentTenant();
  403.  
  404. // Implement tenant-aware logic
  405. return $this->generateTenantSpecificResult($tenant);
  406. }
  407. }
  408. ```
  409.  
  410. **Service Registration**:
  411. Register Cloud services to replace JumUm services in `config/services.yaml`:
  412. ```yaml
  413. services:
  414. # Replace JumUm service with Cloud version
  415. JumUm\YourDomain\YourServiceInterface:
  416. class: Cloud\YourDomain\YourService
  417. arguments:
  418. $tenantProvider: '@parthenon.multi_tenancy.tenant_provider'
  419. ```
  420.  
  421. **Common Use Cases**:
  422.  
  423. **URL Generation**:
  424. ```php
  425. // Cloud version adds tenant subdomain to URLs
  426. namespace Cloud\Checkout;
  427.  
  428. class PortalLinkGenerator implements PortalLinkGeneratorInterface
  429. {
  430. public function generatePayLink(Checkout $checkout): string
  431. {
  432. $tenant = $this->tenantProvider->getCurrentTenant();
  433. return $this->urlGenerator->generate(
  434. 'portal_pay_checkout',
  435. ['slug' => $checkout->getSlug(), 'tenant' => $tenant->getSubdomain()],
  436. UrlGeneratorInterface::ABSOLUTE_URL
  437. );
  438. }
  439. }
  440. ```
  441.  
  442. **Repository Extensions**:
  443. ```php
  444. // Cloud repositories add tenant filtering
  445. namespace Cloud\Repository\Orm;
  446.  
  447. class CustomerRepository extends JumUmCustomerRepository
  448. {
  449. public function findByTenant(TenantInterface $tenant): array
  450. {
  451. return $this->createQueryBuilder('c')
  452. ->where('c.tenant = :tenant')
  453. ->setParameter('tenant', $tenant)
  454. ->getQuery()
  455. ->getResult();
  456. }
  457. }
  458. ```
  459.  
  460. **Best Practices**:
  461. - Always implement the corresponding JumUm interface
  462. - Use dependency injection for `TenantProviderInterface`
  463. - Maintain the same method signatures as JumUm counterparts
  464. - Add tenant-specific logic without breaking existing functionality
  465. - Follow the same coding standards as JumUm components
  466. - Include proper error handling for tenant-related operations
  467.  
  468. **Testing Cloud Components**:
  469. - Create unit tests in `tests/Unit/Cloud/`
  470. - Mock `TenantProviderInterface` in tests
  471. - Test both tenant-aware and fallback behaviors
  472. - Ensure compatibility with JumUm interfaces
  473.  
  474. #### Database Practices
  475. - **Migrations**: All schema changes via Doctrine migrations
  476. - **Multi-tenancy**: Separate migration directory for tenant-specific changes
  477. - **TimescaleDB**: Optimized for time-series data
  478.  
  479. ### Debugging and Development Tools
  480.  
  481. #### Symfony Profiler
  482. - Available in development environment
  483. - Access via `/_profiler` route
  484. - Includes database queries, performance metrics, and request details
  485.  
  486. #### Redis Commander
  487. - Web UI available at `http://localhost:8081`
  488. - Credentials: root/root
  489. - Monitor cache and session data
  490.  
  491. #### Elasticsearch
  492. - Direct access on port 9200
  493. - Use for search functionality debugging
  494.  
  495. ### Integration Guidelines
  496.  
  497. #### Payment Providers
  498. - **Stripe**: Primary payment processor
  499. - **Interoperability Layer**: Abstraction for multiple providers
  500. - **Webhook Handling**: Secure webhook processing
  501.  
  502. #### Third-party Services
  503. - **Email**: Multiple providers (Mailgun, Postmark, etc.)
  504. - **PDF Generation**: Multiple engines (mPDF, Snappy)
  505. - **CRM Integration**: HubSpot, Salesforce support
  506.  
  507. #### CRM Integration Development
  508.  
  509. **Overview**:
  510. 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.
  511.  
  512. **Core Architecture**:
  513.  
  514. **Required Interfaces**:
  515. - `CrmIntegrationInterface`: Main integration contract
  516. - `IntegrationInterface`: Base integration interface
  517. - `CustomerServiceInterface`: Customer data operations
  518. - `EmailServiceInterface`: Email logging operations
  519.  
  520. **Integration Structure**:
  521. ```php
  522. <?php
  523. namespace JumUm\Integrations\Crm\YourCrm;
  524.  
  525. use JumUm\Integrations\Crm\CrmIntegrationInterface;
  526. use JumUm\Integrations\IntegrationInterface;
  527. use JumUm\Integrations\IntegrationType;
  528. use JumUm\Integrations\AuthenticationType;
  529.  
  530. class YourCrmIntegration implements CrmIntegrationInterface, IntegrationInterface
  531. {
  532. public function getCustomerService(): CustomerServiceInterface
  533. {
  534. return new CustomerService($this->getClient());
  535. }
  536.  
  537. public function getEmailService(): EmailServiceInterface
  538. {
  539. return new EmailService($this->getClient());
  540. }
  541.  
  542. public function getType(): IntegrationType
  543. {
  544. return IntegrationType::CRM;
  545. }
  546.  
  547. public function getName(): string
  548. {
  549. return 'your_crm_name';
  550. }
  551.  
  552. public function getAuthenticationType(): AuthenticationType
  553. {
  554. return AuthenticationType::OAUTH; // or API_KEY
  555. }
  556.  
  557. // Additional required methods...
  558. }
  559. ```
  560.  
  561. **Customer Service Implementation**:
  562. ```php
  563. <?php
  564. namespace JumUm\Integrations\Crm\YourCrm;
  565.  
  566. use JumUm\Integrations\Crm\CustomerServiceInterface;
  567. use JumUm\Entity\Customer;
  568.  
  569. class CustomerService implements CustomerServiceInterface
  570. {
  571. public function registerCompany(Customer $customer): CustomerRegistration
  572. {
  573. // Create company in CRM
  574. // Return CustomerRegistration with reference and contactReference
  575. }
  576.  
  577. public function registerContact(Customer $customer): ContactRegistration
  578. {
  579. // Create contact in CRM
  580. // Return ContactRegistration with reference
  581. }
  582.  
  583. public function update(Customer $customer): void
  584. {
  585. // Update existing customer in CRM
  586. }
  587.  
  588. public function search(Customer $customer): ?CustomerProfile
  589. {
  590. // Search for customer in CRM by email
  591. // Return CustomerProfile with CRM data or null if not found
  592. }
  593. }
  594. ```
  595.  
  596. **Email Service Implementation**:
  597. ```php
  598. <?php
  599. namespace JumUm\Integrations\Crm\YourCrm;
  600.  
  601. use JumUm\Integrations\Crm\EmailServiceInterface;
  602. use JumUm\Entity\Customer;
  603. use JumUm\Notification\Email\Email;
  604.  
  605. class EmailService implements EmailServiceInterface
  606. {
  607. public function registerEmail(Customer $customer, Email $email): void
  608. {
  609. // Log email activity in CRM system
  610. }
  611. }
  612. ```
  613.  
  614. **Required DTOs**:
  615.  
  616. All DTOs must be `readonly` classes using constructor promotion:
  617.  
  618. ```php
  619. // CustomerRegistration.php
  620. readonly class CustomerRegistration
  621. {
  622. public function __construct(
  623. public string $reference, // CRM company ID
  624. public string $contactReference, // CRM contact ID
  625. ) {}
  626. }
  627.  
  628. // ContactRegistration.php
  629. readonly class ContactRegistration
  630. {
  631. public function __construct(
  632. public string $reference, // CRM contact ID
  633. ) {}
  634. }
  635.  
  636. // CustomerProfile.php
  637. readonly class CustomerProfile
  638. {
  639. public function __construct(
  640. public string $reference,
  641. public ?string $contactReference,
  642. public ?string $name,
  643. public ?string $city,
  644. public ?string $state,
  645. public ?string $postCode,
  646. public ?string $country,
  647. ) {}
  648. }
  649. ```
  650.  
  651. **Authentication Patterns**:
  652.  
  653. **OAuth Integration**:
  654. ```php
  655. public function getOauthConfig(): OauthConfig
  656. {
  657. return new OauthConfig(
  658. $this->clientId,
  659. $this->clientSecret,
  660. $this->redirectUri,
  661. 'https://your-crm.com/oauth/authorize',
  662. 'https://your-crm.com/oauth/token',
  663. '',
  664. 'required scopes'
  665. );
  666. }
  667.  
  668. public function getAccountId(): string
  669. {
  670. // Return unique account identifier from CRM
  671. }
  672. ```
  673.  
  674. **Environment Configuration**:
  675. ```env
  676. YOUR_CRM_CLIENT_ID=your_client_id
  677. YOUR_CRM_CLIENT_SECRET=your_client_secret
  678. YOUR_CRM_REDIRECT_URI=https://your-app.com/oauth/callback
  679. ```
  680.  
  681. **Service Registration**:
  682.  
  683. Register your integration in `config/services.yaml`:
  684. ```yaml
  685. services:
  686. JumUm\Integrations\Crm\YourCrm\YourCrmIntegration:
  687. arguments:
  688. $clientId: '%env(YOUR_CRM_CLIENT_ID)%'
  689. $clientSecret: '%env(YOUR_CRM_CLIENT_SECRET)%'
  690. $redirectUri: '%env(YOUR_CRM_REDIRECT_URI)%'
  691. tags:
  692. - { name: 'JumUm.integration' }
  693. ```
  694.  
  695. **Testing Requirements**:
  696.  
  697. Create unit tests for your integration:
  698. ```php
  699. <?php
  700. namespace JumUm\Tests\Unit\Integrations\Crm\YourCrm;
  701.  
  702. use PHPUnit\Framework\TestCase;
  703. use JumUm\Integrations\Crm\YourCrm\YourCrmIntegration;
  704.  
  705. class YourCrmIntegrationTest extends TestCase
  706. {
  707. public function testGetType(): void
  708. {
  709. $integration = new YourCrmIntegration(/* dependencies */);
  710. $this->assertEquals(IntegrationType::CRM, $integration->getType());
  711. }
  712.  
  713. public function testGetName(): void
  714. {
  715. $integration = new YourCrmIntegration(/* dependencies */);
  716. $this->assertEquals('your_crm_name', $integration->getName());
  717. }
  718. }
  719. ```
  720.  
  721. **Error Handling**:
  722. - Use `CrmIntegrationFailure` webhook payload for integration failures
  723. - Implement proper logging using `LoggerAwareTrait`
  724. - Handle API rate limits and connection errors gracefully
  725.  
  726. **Directory Structure**:
  727. ```
  728. src/JumUm/Integrations/Crm/YourCrm/
  729. ├── YourCrmIntegration.php
  730. ├── CustomerService.php
  731. ├── EmailService.php
  732. └── Client/ (if needed for API wrapper)
  733. ```
  734.  
  735. **Best Practices**:
  736. - Use readonly DTOs with constructor promotion
  737. - Implement proper OAuth token refresh handling
  738. - Cache API responses when appropriate
  739. - Follow the existing HubSpot integration as a reference
  740. - Use dependency injection for all external dependencies
  741. - Implement comprehensive error handling and logging
  742.  
  743. ### Performance Considerations
  744. - **Caching**: Redis for application cache
  745. - **Database**: TimescaleDB for time-series optimization
  746. - **Asset Optimization**: Webpack Encore for frontend builds
  747. - **Background Jobs**: Symfony Messenger for async processing
  748.  
  749. ### Security Practices
  750. - **Input Validation**: Symfony Validator component
  751. - **CSRF Protection**: Built-in Symfony protection
  752. - **API Authentication**: Token-based authentication
  753. - **Data Encryption**: Sensitive data encryption at rest
  754.  
  755. #### Repository Design Patterns
  756.  
  757. **Overview**:
  758. 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.
  759.  
  760. **Repository Structure**:
  761. ```
  762. JumUm\Repository\
  763. ├── {Entity}RepositoryInterface.php # Domain-specific repository interfaces
  764. ├── {Entity}Repository.php # Service repository implementations
  765. └── Orm\
  766. └── {Entity}Repository.php # ORM-specific repository implementations
  767. ```
  768.  
  769. **Repository Layers**:
  770. - **Interface Layer**: Defines the contract for repository operations (`{Entity}RepositoryInterface`)
  771. - **Service Layer**: Implements domain-specific logic and extends `DoctrineCrudRepository` (`{Entity}Repository`)
  772. - **ORM Layer**: Handles direct database interactions and extends `CustomServiceRepository` (`Orm\{Entity}Repository`)
  773.  
  774. **Repository Inheritance Hierarchy**:
  775. 1. Repository interfaces extend base interfaces:
  776. ```php
  777. interface CustomerRepositoryInterface extends \Parthenon\Billing\Repository\CustomerRepositoryInterface
  778. {
  779. // Domain-specific method declarations
  780. }
  781. ```
  782.  
  783. 2. Service repositories extend base repositories and implement interfaces:
  784. ```php
  785. class CustomerRepository extends DoctrineCrudRepository implements CustomerRepositoryInterface
  786. {
  787. // Domain-specific method implementations
  788. }
  789. ```
  790.  
  791. 3. ORM repositories extend Doctrine's repository base:
  792. ```php
  793. class CustomerRepository extends CustomServiceRepository
  794. {
  795. // Database-specific implementations
  796. }
  797. ```
  798.  
  799. **Repository Configuration**:
  800. Repositories are registered in the dependency injection container in `config/services/core/main.yaml`:
  801.  
  802. 1. Interface to implementation binding:
  803. ```yaml
  804. JumUm\Repository\CustomerRepositoryInterface: '@JumUm\Repository\CustomerRepository'
  805. ```
  806.  
  807. 2. Service repository configuration:
  808. ```yaml
  809. JumUm\Repository\CustomerRepository:
  810. arguments:
  811. $entityRepository: '@app.repository.orm.customer'
  812. ```
  813.  
  814. 3. ORM repository registration:
  815. ```yaml
  816. app.repository.orm.customer:
  817. class: JumUm\Repository\Orm\CustomerRepository
  818. ```
  819.  
  820. **Repository Usage Patterns**:
  821. 1. **Constructor Injection**: Repositories are injected via constructor:
  822. ```php
  823. public function __construct(
  824. private CustomerRepositoryInterface $customerRepository,
  825. // other dependencies
  826. ) {
  827. }
  828. ```
  829.  
  830. 2. **Interface Dependency**: Always depend on repository interfaces, not implementations:
  831. ```php
  832. use JumUm\Repository\CustomerRepositoryInterface;
  833.  
  834. class CustomerService
  835. {
  836. public function __construct(private CustomerRepositoryInterface $customerRepository)
  837. {
  838. }
  839. }
  840. ```
  841.  
  842. 3. **Domain-Specific Methods**: Repositories provide domain-specific query methods:
  843. ```php
  844. // Finding by domain criteria
  845. $customer = $customerRepository->findByEmail('[email protected]');
  846.  
  847. // Domain-specific operations
  848. $customerRepository->wipeCustomerSupportReferences();
  849. ```
  850.  
  851. **Creating New Repositories**:
  852.  
  853. 1. **Define the Interface**:
  854. ```php
  855. <?php
  856.  
  857. namespace JumUm\Repository;
  858.  
  859. use JumUm\Entity\YourEntity;
  860. use Parthenon\Common\Repository\RepositoryInterface;
  861.  
  862. interface YourEntityRepositoryInterface extends RepositoryInterface
  863. {
  864. public function findByUniqueIdentifier(string $identifier): YourEntity;
  865.  
  866. // Other domain-specific methods
  867. }
  868. ```
  869.  
  870. 2. **Create the ORM Repository**:
  871. ```php
  872. <?php
  873.  
  874. namespace JumUm\Repository\Orm;
  875.  
  876. use Parthenon\Common\Repository\CustomServiceRepository;
  877.  
  878. class YourEntityRepository extends CustomServiceRepository
  879. {
  880. // ORM-specific implementations if needed
  881. }
  882. ```
  883.  
  884. 3. **Implement the Service Repository**:
  885. ```php
  886. <?php
  887.  
  888. namespace JumUm\Repository;
  889.  
  890. use JumUm\Entity\YourEntity;
  891. use Parthenon\Athena\Repository\DoctrineCrudRepository;
  892. use Parthenon\Common\Exception\NoEntityFoundException;
  893.  
  894. class YourEntityRepository extends DoctrineCrudRepository implements YourEntityRepositoryInterface
  895. {
  896. public function findByUniqueIdentifier(string $identifier): YourEntity
  897. {
  898. $entity = $this->entityRepository->findOneBy(['uniqueIdentifier' => $identifier]);
  899.  
  900. if (!$entity instanceof YourEntity) {
  901. throw new NoEntityFoundException(sprintf("No entity found for identifier '%s'", $identifier));
  902. }
  903.  
  904. return $entity;
  905. }
  906.  
  907. // Other domain-specific method implementations
  908. }
  909. ```
  910.  
  911. 4. **Register in Service Container**:
  912. ```yaml
  913. # config/services/core/main.yaml
  914.  
  915. JumUm\Repository\YourEntityRepositoryInterface: '@JumUm\Repository\YourEntityRepository'
  916.  
  917. JumUm\Repository\YourEntityRepository:
  918. arguments:
  919. $entityRepository: '@app.repository.orm.your_entity'
  920.  
  921. app.repository.orm.your_entity:
  922. class: JumUm\Repository\Orm\YourEntityRepository
  923. ```
  924.  
  925. **Best Practices**:
  926. - Always define and depend on repository interfaces, not implementations
  927. - Keep domain logic in service repositories, database logic in ORM repositories
  928. - Use constructor injection for repository dependencies
  929. - Follow consistent naming conventions for repository methods
  930. - Document exceptions that may be thrown by repository methods
  931. - Use proper type declarations for parameters and return types
  932. - Implement proper error handling with domain-specific exceptions
  933. - Write unit tests for repository implementations
  934. - Extend DoctrineRepository or DoctrineCrudRepository from Parthenon.
  935.  
  936. **Cloud Multi-tenancy Support**:
  937. For multi-tenant environments, extend repositories in the Cloud namespace:
  938.  
  939. ```php
  940. <?php
  941.  
  942. namespace Cloud\Repository\Orm;
  943.  
  944. use JumUm\Repository\Orm\YourEntityRepository as JumUmYourEntityRepository;
  945. use Parthenon\MultiTenancy\Entity\TenantInterface;
  946.  
  947. class YourEntityRepository extends JumUmYourEntityRepository
  948. {
  949. public function findByTenant(TenantInterface $tenant): array
  950. {
  951. return $this->createQueryBuilder('e')
  952. ->where('e.tenant = :tenant')
  953. ->setParameter('tenant', $tenant)
  954. ->getQuery()
  955. ->getResult();
  956. }
  957. }
  958. ```
  959.  
  960. **Testing Repositories**:
  961. - Create unit tests in `tests/Unit/Repository/`
  962. - Use in-memory SQLite database for repository tests
  963. - Test both success and failure scenarios
  964. - Mock the entity manager for pure unit tests
  965.  
  966. This document should be updated as the project evolves and new patterns or tools are introduced.
  967.  
  968. # Git Usage during Development
  969.  
  970. You must follow ./git_usage.md for all tasks.
  971.  
  972. # Documentation
  973.  
  974. * Documentation must be written in British English and not American English.
  975. * Documentation must be written in Markdown format.
  976. * Documentation in public_doc/user must be written in a way that is easy to understand for non-technical users.
  977. * Documentation in public_doc/technical must be written in a way that is easy to understand for technical users.
  978. * Documentation must be thorough and complete
  979. * Documentation should include examples where appropriate
  980. * Documentation should be written in a way that is easy to read and understand
  981. * Documentation should be written in a way that is easy to navigate
Advertisement
Add Comment
Please, Sign In to add comment