diff --git a/.cursor/config.json b/.cursor/config.json new file mode 100644 index 0000000..0ac8626 --- /dev/null +++ b/.cursor/config.json @@ -0,0 +1,15 @@ +{ + "rules": { + "*.php": [ + ".cursor/rules/php.template.mdc", + ".cursor/rules/php.mdc", + ".cursor/rules/global.mdc" + ], + "*.md": [".cursor/rules/md.mdc"], + "*.js": [".cursor/rules/node.mdc", ".cursor/rules/global.mdc"], + "*.jsx": [".cursor/rules/node.mdc", ".cursor/rules/global.mdc"], + "*.ts": [".cursor/rules/node.mdc", ".cursor/rules/global.mdc"], + "*.tsx": [".cursor/rules/node.mdc", ".cursor/rules/global.mdc"], + "*": [".cursor/rules/global.mdc"] + } +} \ No newline at end of file diff --git a/.cursor/rules/feature.mdc b/.cursor/rules/feature.mdc new file mode 100644 index 0000000..bb3ab32 --- /dev/null +++ b/.cursor/rules/feature.mdc @@ -0,0 +1,111 @@ +--- +description: +globs: +alwaysApply: false +--- +# Rules for developing a new feature + +## Feature Development Workflow + +### 1. User Story Creation +- When a new feature is requested, first create a user story: + - Format: "As a {role} I want {change} because {reason}" + - Include acceptance criteria: + - Functional requirements + - Technical requirements + - Performance requirements + - Security requirements + - Documentation requirements + - Required additional information: + - User roles involved + - Current workflow/process + - Expected outcome + - Integration points + - Security considerations + - Performance expectations + - Data requirements + - UI/UX requirements + - Testing requirements + - Documentation needs + - Present user story to stakeholder for validation + +### 2. Analysis Phase +- Read and analyze existing codebase: + - Review all relevant PHP code in lib/ + - Review all relevant Vue code in src/ + - Identify affected components + - Identify required changes + - Document dependencies + - Note potential impacts + +### 3. Implementation Planning +Create detailed implementation plan including: +- Backend changes: + - New/modified PHP classes + - Database changes + - API endpoints + - Service modifications + - Security considerations +- Frontend changes: + - Component updates + - State management + - API integration + - UI/UX implementation +- Test coverage: + - Unit tests + - Integration tests + - End-to-end tests +- Documentation updates: + - Technical documentation + - User documentation + - API documentation + - Architecture updates +- Present plan to stakeholder for approval + +### 4. Implementation Phase +Only proceed after stakeholder approval: +1. Create/update backend components +2. Create/update frontend components +3. Add/update tests +4. Update documentation +5. Quality checks: + - Run PHP CodeSniffer + - Run PHPStan + - Run ESLint + - Run TypeScript checks + - Run unit tests + - Fix any issues + - Repeat until all checks pass + +### 5. Documentation Requirements +- Update relevant documentation in website/docs: + - Technical documentation + - User documentation + - API documentation if applicable + - Update diagrams + - Add code examples + - Document configuration + - Document dependencies + +### 6. Quality Assurance +- Automated checks: + - PHP CodeSniffer compliance + - PHPStan level 8 compliance + - Psalm compliance + - ESLint compliance + - TypeScript strict mode compliance + - Unit test coverage + - Integration test coverage +- Manual checks: + - Code review guidelines + - Security review + - Performance testing + - Accessibility testing + +### 7. Version Control +- Commit messages must: + - Reference user story + - Describe changes clearly + - Include documentation updates + - Note breaking changes + - Include test coverage \ No newline at end of file diff --git a/.cursor/rules/global.mdc b/.cursor/rules/global.mdc new file mode 100644 index 0000000..6498b81 --- /dev/null +++ b/.cursor/rules/global.mdc @@ -0,0 +1,123 @@ +--- +description: +globs: +alwaysApply: true +--- +# Gneral rules for workin add Conduction as an AI + +You are a senior programmer at an innovation-oriented development company. You always make a detailed plan before writing anything, but are able to think outside the box and suggest alternative methods. + +## Documentation +- Icons should be part of https://pictogrammers.com/library/mdi/ +- Layout should follow https://docs.nextcloud.com/server/latest/developer_manual/design/layoutcomponents.html +- Components can be used from https://nextcloud-vue-components.netlify.app/ + +## App Structure +## App Structure +- Root Directory: + - `appinfo/` - Nextcloud app configuration + - `tests/` - Test files + - `composer.json` - PHP dependencies + - `package.json` - Node.js dependencies + - `phpunit.xml` - PHPUnit configuration + - `phpcs.xml` - PHP CodeSniffer configuration + - `.eslintrc.js` - ESLint configuration + - `tsconfig.json` - TypeScript configuration + - `webpack.config.js` - Webpack configuration + +## Version Control +- Use meaningful commit messages +- Reference issue numbers in commits +- Keep commits focused and atomic +- Update documentation in same commit as code changes + +## Code Quality +- Write self-documenting code +- Include comments for complex logic +- Follow language-specific best practices +- Maintain consistent code style +- Write testable code + +## Testing +- Write tests for new functionality +- Update tests when modifying existing code +- Maintain high test coverage +- Document test scenarios + +## Security +- Follow security best practices +- Document security considerations +- Keep dependencies up to date +- Review security implications of changes + +## Performance +- Consider performance implications +- Document performance considerations +- Include performance metrics where relevant + +## Accessibility +- Follow accessibility guidelines +- Document accessibility features +- Test with accessibility tools + +## Internationalization +- Support multiple languages +- Document translation requirements +- Use proper i18n practices + +## Project Structure +- Follow consistent directory structure +- Organize files logically +- Use appropriate file extensions +- Keep related files together +- Maintain clear separation of concerns + +## Development Workflow +- Use development, staging, and production environments +- Implement continuous integration +- Use automated testing +- Regular dependency updates +- Security scanning +- Performance monitoring + +## Accessibility +- Follow WCAG guidelines +- Test with screen readers +- Ensure keyboard navigation +- Maintain proper contrast ratios +- Provide alternative text for images + +## Internationalization +- Use translation files +- Handle different date formats +- Consider RTL languages +- Use appropriate character encoding + +## Security +- Regular security audits +- Keep dependencies updated +- Implement proper access controls +- Regular penetration testing + +## Performance +- Optimize load times +- Implement caching +- Minimize resource usage +- Regular performance testing +- Monitor metrics + +## Maintenance +- Regular code cleanup +- Remove unused code +- Update outdated dependencies +- Monitor error logs +- Regular backups + +## Special Considerations +- Never use backticks (`) in documentation or code edits +- Always use single quotes (') for code examples +- Always run quality checks before requesting review +- Fix all linter and test issues before completion +- Document all decisions and assumptions +- Keep stakeholder informed of progress +- Update project documentation as needed \ No newline at end of file diff --git a/.cursor/rules/md.mdc b/.cursor/rules/md.mdc new file mode 100644 index 0000000..bd7e1bf --- /dev/null +++ b/.cursor/rules/md.mdc @@ -0,0 +1,55 @@ +--- +description: Rules for PHP files +globs: *.md +alwaysApply: false +--- +# Markdown Coding Standards and Best Practices + +## App Structure +- Documentation: + - All documentation resides in the `website/docs/` directory + +## Markdown styling +- We use docusaurus for document generatione +- We use docusaurus mermaid for shema's in the documentation +- We use redocusaurus to describe in the documentation + +## Documentation +- All documentation is maintained in website/docs using Docusaurus +- Documentation must be updated whenever functionality is added or modified +- Use Mermaid diagrams for visualizing: + - Flow charts + - Sequence diagrams + - Class diagrams + - Entity relationships + - State diagrams + - User journeys + - Gantt charts + +### Documentation Requirements +- Every new feature must include: + - Technical documentation explaining the implementation + - User documentation if it affects user interaction + - API documentation if it exposes endpoints + - Updated architecture diagrams if it changes system structure + - Mermaid diagrams for complex flows or relationships +- Every modified feature must update: + - Existing documentation to reflect changes + - Related diagrams and visualizations + - Version history in the relevant docs + +### Mermaid Diagram Guidelines +- Use Mermaid for all technical diagrams +- Keep diagrams simple and focused +- Include diagram source in markdown for future updates +- Use consistent styling across diagrams +- Add descriptive titles and legends +- Document diagram conventions in website/docs/contributing + +### Documentation Style +- Write in clear, concise language +- Use proper markdown formatting +- Include code examples where relevant +- Use single quotes (') instead of backticks (`) for inline code +- Keep documentation up to date with code changes +- Include version information when relevant \ No newline at end of file diff --git a/.cursor/rules/node.mdc b/.cursor/rules/node.mdc new file mode 100644 index 0000000..9a9e661 --- /dev/null +++ b/.cursor/rules/node.mdc @@ -0,0 +1,91 @@ +--- +description: +globs: *.js,*.ts,*vue +alwaysApply: false +--- +# Node.js Coding Standards and Best Practices + +Always use linting afther creating or altering a file + +## App Structure +- Frontend (Vue.js): + - All frontend code resides in the `src/` directory + - Directory structure: + - `src/components/` - Vue components + - `src/views/` - Vue views/pages + - `src/store/` - Vuex store modules + - `src/router/` - Vue router configuration + - `src/assets/` - Static assets (images, fonts, etc.) + - `src/styles/` - Global styles and CSS + - `src/utils/` - Utility functions + - `src/api/` - API client and services + - `src/types/` - TypeScript type definitions + - `src/composables/` - Vue composables + - `src/middleware/` - Router middleware + - `src/plugins/` - Vue plugins + - `src/locales/` - Translation files + +## File Structure +- Use ES modules (import/export) syntax +- Follow a consistent directory structure +- Separate concerns into appropriate modules +- Use index.js files for module exports + +## Code Style +- Use ESLint with recommended rules +- Use Prettier for code formatting +- Use TypeScript for type safety +- Follow Airbnb JavaScript Style Guide +- Use async/await for asynchronous operations +- Use const/let instead of var +- Use arrow functions where appropriate + +## Documentation +- Use JSDoc for function documentation +- Include @param, @returns, and @throws annotations +- Document complex algorithms +- Keep README.md up to date +- Document environment variables + +## Testing +- Use Jest for testing +- Write unit tests for all functions +- Use test-driven development (TDD) when possible +- Mock external dependencies +- Test error cases and edge conditions + +## Error Handling +- Use try/catch blocks appropriately +- Create custom error classes +- Log errors with proper context +- Handle async errors properly +- Use error boundaries in React components + +## Security +- Use environment variables for secrets +- Implement proper authentication +- Use HTTPS for all external requests +- Sanitize user input +- Follow OWASP security guidelines +- Keep dependencies updated + +## Performance +- Use proper caching strategies +- Implement rate limiting +- Optimize database queries +- Use compression where appropriate +- Monitor memory usage + +## Dependencies +- Use npm or yarn for package management +- Keep package.json up to date +- Use exact versions in package.json +- Document all dependencies +- Regular security audits + +## React Specific +- Use functional components with hooks +- Follow React best practices +- Implement proper prop types +- Use React.memo for performance +- Follow component composition patterns \ No newline at end of file diff --git a/.cursor/rules/php.mdc b/.cursor/rules/php.mdc new file mode 100644 index 0000000..4571342 --- /dev/null +++ b/.cursor/rules/php.mdc @@ -0,0 +1,440 @@ +--- +description: Rules for PHP files +globs: ["*.php"] +alwaysApply: false +--- +# PHP Coding Standards + +## General Rules +- Use spaces for indentation, not tabs +- Indent with 4 spaces +- Line length should not exceed 150 characters +- Files must end with a single blank line +- No trailing whitespace at the end of lines +- Use single quotes for strings unless double quotes are needed +- Add docblocks to all methods, classes, and properties +- Add return types to all methods +- Add type hints to all methods +- Add default values to all methods where appropriate +- Add phpstan and psalm annotations to all methods +- Add phpunit tests to all methods +- Add inline comments to explain complex logic +- Use readonly properties where appropriate + +## Class Structure +- Class files should begin with a docblock containing: + - Class name + - Category + - Package + - Author + - Copyright + - License + - Version + - Link to the application + +## Method Structure +- One blank line between methods +- Opening brace on same line as method declaration +- Closing brace must be followed by one blank line +- Method parameters should be properly aligned +- Type declarations should be used whenever possible +- Return type declarations should be used + +## Control Structures +- Opening brace on same line +- One space after keywords (if, for, while, etc) +- No space after function name in function calls +- Spaces around operators +- One space after commas in function calls +- Use elseif instead of else if +- Add end comments for long control structures + +## Arrays +- Multi-line arrays should have each element on its own line +- Array elements should be properly aligned +- Trailing comma after last element +- Use short array syntax [] + +## Error Handling +- Use try-catch blocks appropriately +- Document thrown exceptions in docblocks +- Add meaningful error messages + +## Documentation +- All classes must have complete docblocks +- All methods must have complete docblocks +- Complex logic should have inline comments +- Use proper alignment in docblocks +- Add @param, @return, and @throws tags as needed + +## Naming Conventions +- Classes: PascalCase +- Methods: camelCase +- Properties: camelCase +- Constants: UPPER_SNAKE_CASE +- Variables: camelCase + +## File Structure +- One class per file +- Namespace declaration first +- Use statements after namespace +- Class declaration after use statements +- Proper file and directory naming + +## Testing +- All public methods should have unit tests +- Test class names should end with Test +- Test methods should begin with test +- Use meaningful test method names +- Add docblocks to test methods + +## Security +- Validate all input +- Escape all output +- Use prepared statements for SQL +- Follow OWASP security guidelines +- Document security considerations + +## Performance +- Optimize database queries +- Use caching where appropriate +- Minimize file operations +- Document performance considerations + +## Maintenance +- Remove unused code +- Keep dependencies updated +- Monitor error logs +- Regular backups +- Document maintenance procedures + +## Class and Interface Rules +- All classes and interfaces must have a complete docblock containing: + - Description + - Package + - Category + - Author + - Copyright + - License + - Version + - Link + - Since + +## Code Style +- Follow PSR-12 coding standards +- Multi-line control structures must have: + - First expression on the line after the opening parenthesis + - Closing parenthesis on the line after the last expression + - Proper indentation for all lines + +## Properties +- All properties must have type declarations +- Use readonly properties where appropriate +- All properties must have docblocks with type information + +## Methods +- All methods must have: + - Return type declarations + - Parameter type declarations + - Default values for optional parameters + - Complete docblocks including: + - Description + - @param annotations with types and descriptions + - @return annotation with type and description + - @throws annotation for any exceptions + - PHPStan and Psalm annotations where appropriate + +## Documentation +- All code changes must be documented in Docusaurus +- Documentation files must be in the website/docs folder +- Use single quotes (') instead of backticks (`) in documentation +- Technical documentation must include: + - Class purpose and responsibility + - Method descriptions and usage examples + - Configuration options + - Dependencies and requirements + +## Quality Checks +- All code must pass: + - PHP_CodeSniffer (PSR-12) with zero errors or warnings + - PHPStan (Level 5) with zero errors + - Psalm (Level 5) with zero errors + - PHPUnit tests with 100% pass rate and at least 80% code coverage +- Configuration files: + - Use phpcs.xml for phpcs configuration + - Use phpstan.neon for PHPStan configuration + - Use psalm.xml for Psalm configuration + - Both should be present in the project root +- Set up pre-commit hooks to automatically run checks +- For CI/CD pipelines, these checks should be part of the build process + +## Example Class Structure +```php + + * @copyright Copyright (C) 2024 Conduction B.V. All rights reserved. + * @license EUPL 1.2 + * @version 1.0.0 + * @link https://openregister.app + * + * @since 1.0.0 - Description of when this class was added + */ +class ExampleClass +{ + /** + * Description of the property. + * + * @var string + */ + private readonly string $property; + + /** + * Constructor. + * + * @param string $property Description of the parameter + */ + public function __construct(string $property) + { + $this->property = $property; + } + + /** + * Description of what the method does. + * + * @param string $param Description of the parameter + * @param int $optionalParam Description of the optional parameter + * + * @return bool Description of the return value + * + * @throws \Exception When something goes wrong + * + * @psalm-pure + * @phpstan-return bool + */ + public function exampleMethod(string $param, int $optionalParam = 0): bool + { + // Method implementation + return true; + } +} +``` + +## App Structure +- Backend (PHP): + - All PHP code resides in the `lib/` directory + - Directory structure follows PSR-4 autoloading: + - `lib/Controller/` - Application controllers + - `lib/Service/` - Business logic and services + - `lib/Db/` - Database entities and mappers + - `lib/Exception/` - Custom exceptions + - `lib/Migration/` - Database migrations + - `lib/Helper/` - Helper classes and utilities + - `lib/Event/` - Event classes + - `lib/EventListener/` - Event listeners + - `lib/Command/` - Console commands + - `lib/Cron/` - Cron jobs + - `lib/Settings/` - Application settings + - `lib/AppInfo/` - App information and registration + - `lib/Http/` - HTTP related classes + - `lib/Validator/` - Validation classes + - `lib/Factory/` - Factory classes + - `lib/Provider/` - Service providers + - `lib/Twig/` - Twig extensions and runtime + +## File Structure +- All PHP files should start with a docblock containing: + - Class name + - Category + - Package + - Author + - Copyright + - License + - Version + - Link to application + +## Code Style (PHPCS Rules) +- Use the phpcs.xml in the root as standard when doing phpcs checks +- Follow PEAR standard with specific customizations: + - Line length: max 125 chars (soft limit), 150 chars (hard limit) + - No Yoda conditions + - Use short array syntax [] + - One argument per line in multi-line function calls + - No inline control structures + - No multiple statements on one line + - Space after type casting + - No underscore prefix for private methods/properties + - Inline comments must end in full-stops, exclamation marks, or question marks + - Implicit true comparisons prohibited; use === true instead + - Operator ! prohibited; use === false instead + + +### Spacing Rules +- Array bracket spacing (Squiz) +- Function declaration argument spacing (Squiz) +- Control structure spacing (Squiz) +- Function spacing: 1 line between functions +- Member var spacing (Squiz) +- Operator spacing (Squiz) +- No superfluous whitespace + +### Commenting Rules +- Block comments properly aligned (Squiz) +- DocComment alignment (Squiz) +- Empty catch must have comment +- Proper inline comment formatting +- Long condition closing comments +- Variable comments required + +### Forbidden Functions/Patterns +- sizeof (use count) +- delete (use unset) +- print (use echo) +- is_null +- create_function +- var_dump +- No inline if statements + +### Array Formatting +- Custom array indentation rules +- No long array syntax +- Proper key/value alignment + +## Method Requirements +- All methods, classes, and properties MUST have docblocks +- All methods MUST have: + - Return type declarations + - Parameter type hints + - Default values for optional parameters + - PHPStan and Psalm annotations + - PHPUnit tests + - Inline comments explaining each logical step + - Docblocks containing: + - @param annotations with types and descriptions + - @return annotation with type and description + - @throws annotations for all possible exceptions + - @since annotation with version number + - @deprecated annotation if applicable +- Properties MUST: + - Have docblocks with type information + - Use readonly modifier when the property should not be modified after construction + - Include visibility modifier (public, protected, private) + - Have proper type hints +- Classes MUST: + - Have complete docblocks as per template + - Follow single responsibility principle + - Use proper inheritance and interfaces + - Have descriptive names matching their purpose + +## Documentation +- Use Docusaurus for documentation +- Technical and user documentation should be in website/docs folder +- All code changes must be documented +- Use single quotes (') instead of backticks (`) in documentation + +## Testing +- Write PHPUnit tests for all methods +- Tests should be placed in tests/ directory +- Test names should be descriptive and follow the pattern test[MethodName]_[Scenario] + +## Error Handling +- Use appropriate exception types +- Include meaningful error messages +- Log errors appropriately +- Handle edge cases + +## Security +- Never expose sensitive data +- Use prepared statements for database queries +- Validate all input +- Sanitize all output +- Follow OWASP security guidelines + +## Performance +- Optimize database queries +- Use caching where appropriate +- Minimize memory usage +- Consider scalability + +## Dependencies +- Use Composer for dependency management +- Keep dependencies up to date +- Document all external dependencies +- Use specific version constraints + +## Automatic Code Quality Checks + +### PHP_CodeSniffer (PHPCS) +- All code must pass PHPCS checks using PSR-12 standard +- Run PHPCS before committing code changes: + ```bash + # Check coding standard violations + phpcs --standard=PSR12 file_or_directory_to_check.php + + # Automatically fix coding standard violations + phpcbf --standard=PSR12 file_or_directory_to_check.php + ``` +- Common standards to check: + - PSR-12 (Preferred) + - PSR-2 (Legacy) + - PSR-1 (Basic) + +### PHPStan +- All code must pass PHPStan analysis at level 5 or higher +- Run PHPStan before committing code changes: + ```bash + # Run PHPStan on specific files + vendor/bin/phpstan analyse lib/Service/YourService.php + + # Run PHPStan on entire lib directory + vendor/bin/phpstan analyse lib/ + ``` +- Use PHPStan annotations to improve type checking: + - `@phpstan-param` - Specify more detailed parameter types + - `@phpstan-return` - Specify more detailed return types + - `@phpstan-var` - Specify more detailed property types + - `@phpstan-template` - For generic classes + - `@phpstan-type` - To define complex types + +### Psalm +- All code must pass Psalm analysis at level 5 or higher +- Run Psalm before committing code changes: + ```bash + # Run Psalm on specific files + vendor/bin/psalm lib/Service/YourService.php --no-cache + + # Run Psalm on entire lib directory + vendor/bin/psalm --no-cache + ``` +- Use Psalm annotations for better type checking: + - `@psalm-param` - Specify more detailed parameter types + - `@psalm-return` - Specify more detailed return types + - `@psalm-var` - Specify more detailed property types + - `@psalm-pure` - Mark methods as pure (no side effects) + - `@psalm-immutable` - Mark classes as immutable + +### Verification Workflow +1. Write or modify code according to standards +2. Run PHPCS to check and fix formatting issues: + ```bash + phpcbf --standard=PSR12 path/to/your/file.php + phpcs --standard=PSR12 path/to/your/file.php + ``` +3. Run PHPStan to check for type errors and logical issues: + ```bash + vendor/bin/phpstan analyse path/to/your/file.php + ``` +4. Run Psalm for additional static analysis: + ```bash + vendor/bin/psalm path/to/your/file.php --no-cache + ``` +5. Fix any identified issues +6. Run unit tests to ensure functionality +7. Commit only after all checks pass \ No newline at end of file diff --git a/.cursor/rules/php.template.mdc b/.cursor/rules/php.template.mdc new file mode 100644 index 0000000..72d211b --- /dev/null +++ b/.cursor/rules/php.template.mdc @@ -0,0 +1,79 @@ +--- +description: +globs: ["*.php"] +alwaysApply: false +--- + +/** + * {{#if (contains filepath "lib/Db")}}Entity class representing a database object. + * + * This class handles database operations and entity mapping.{{else if (contains filepath "lib/Service")}}Service class for handling business logic. + * + * This class provides service layer functionality.{{else if (contains filepath "lib/Controller")}}Controller class for handling HTTP requests. + * + * This class handles incoming HTTP requests and returns appropriate responses.{{else}}Class description goes here. + * + * Detailed description goes here.{{/if}} + * + * @category {{#if (contains filepath "lib/Service")}}Service + {{else if (contains filepath "lib/Controller")}}Controller + {{else if (contains filepath "lib/Db/Mapper")}}Mapper + {{else if (contains filepath "lib/Db")}}Entity + {{else if (contains filepath "lib/Command")}}Command + {{else if (contains filepath "lib/Cron")}}Cron + {{else if (contains filepath "lib/Event")}}Event + {{else if (contains filepath "lib/EventListener")}}EventListener + {{else if (contains filepath "lib/Exception")}}Exception + {{else if (contains filepath "lib/Factory")}}Factory + {{else if (contains filepath "lib/Helper")}}Helper + {{else if (contains filepath "lib/Http")}}Response + {{else if (contains filepath "lib/Listener")}}Listener + {{else if (contains filepath "lib/Migration")}}Migration + {{else if (contains filepath "lib/Model")}}Model + {{else if (contains filepath "lib/Provider")}}Provider + {{else if (contains filepath "lib/Repository")}}Repository + {{else if (contains filepath "lib/Settings")}}Settings + {{else if (contains filepath "lib/Sections")}}Section + {{else if (contains filepath "lib/Twig/Runtime")}}Runtime + {{else if (contains filepath "lib/Twig")}}Extension + {{else if (contains filepath "lib/Action")}}Action + {{else if (contains filepath "lib/AppInfo")}}AppInfo + {{else if (contains filepath "lib/Validator")}}Validator + {{else if (contains filepath "tests")}}Test + {{else}}Component{{/if}} + * @package OpenConnector + * @author Conduction Development Team + * @copyright 2024 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * @version {{version info.xml "//version"}} + * @link https://OpenConnector.app + * + * @since {{version info.xml "//version"}} - Description of when this class was added + * @deprecated Optional: version number - Reason for deprecation + * + * @api Optional: Mark as public API + * @internal Optional: Mark as internal/private API + * @final Optional: Mark as final/not extendable + * @template Optional: For generic classes + * + * @see \Related\Class::method() Optional: Reference to related code + * @uses \Other\Class Optional: List other classes this one uses + * @used-by \Another\Class Optional: List classes that use this one + * + * @psalm-immutable Optional: Mark class as immutable + * @psalm-pure Optional: Mark methods as pure (no side effects) + * @psalm-suppress PropertyNotSetInConstructor + * + * @phpstan-type Optional: Define complex types + * @phpstan-import Optional: Import types from other classes + * @phpstan-template Optional: For generic classes + * + * @method string methodName(int $param) Optional: For magic methods + * @property string $propertyName Optional: For magic properties + * @property-read string $readOnlyProperty Optional: For read-only properties + * @property-write string $writeOnlyProperty Optional: For write-only properties + * + * @mixin \Other\Class Optional: For classes that use traits or composition + * @implements \Interface\Name Optional: Document implemented interfaces + * @extends \Parent\Class Optional: Document parent class + */ \ No newline at end of file diff --git a/.cursorrules b/.cursorrules deleted file mode 100644 index 6b0037a..0000000 --- a/.cursorrules +++ /dev/null @@ -1,8 +0,0 @@ -- add docblocks to all methods, classes, and properties -- add return types to all methods -- add type hints to all methods -- add default values to all methods -- add phpstan and psalm annotations to all methods -- add phpunit tests to all methods -- add inline comments to all logic explaining the steps being taken -- use readonly properties where appropriate diff --git a/.github/DOCUMENTATION_ISSUE_TEMPLATE.md b/.github/DOCUMENTATION_ISSUE_TEMPLATE.md new file mode 100644 index 0000000..7220505 --- /dev/null +++ b/.github/DOCUMENTATION_ISSUE_TEMPLATE.md @@ -0,0 +1,24 @@ +--- +title: Documentation Build Failed +assignees: rubenvdlinde +labels: documentation, bug +--- + +The documentation build has failed in the documentation workflow. + +### Details +- Triggered by: @{{ payload.sender.login }} +- Commit: {{ sha }} +- Workflow run: {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} + +### Error Information +Please check the workflow logs for detailed error information. + +### Next Steps +1. Review the workflow logs +2. Check the Docusaurus build output +3. Verify documentation changes +4. Fix any identified issues +5. Push changes to trigger a new build + +[View Workflow Logs]({{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}) \ No newline at end of file diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000..d5c8ae4 --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,63 @@ +name: Documentation + +on: + push: + branches: + - documentation + pull_request: + branches: + - documentation + +jobs: + deploy: + name: Deploy Documentation + runs-on: ubuntu-latest + steps: + # https://github.com/marketplace/actions/checkout + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # Verify directory structure + - name: List directory structure + run: | + ls -la + ls -la website/ + + # Generate SVG files using PlantUML + - name: plantuml + id: plantuml + uses: grassedge/generate-plantuml-action@v1.5 + with: + message: "Render PlantUML files" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Node.js 18 + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Install dependencies and build + timeout-minutes: 3 + run: | + cd website + npm run ci + + # Deploy to GitHub Pages + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./website/build + user_name: ${{ github.actor }} + user_email: ${{ github.event.pusher.email || github.actor }} + + # https://github.com/marketplace/actions/create-an-issue + - name: Create issue on failure + if: failure() + uses: JasonEtco/create-an-issue@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + filename: .github/DOCUMENTATION_ISSUE_TEMPLATE.md diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index ccb5590..8db82c2 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -1,36 +1,74 @@ + * @copyright 2024 Ruben Linde + * @license https://www.gnu.org/licenses/agpl-3.0.html GNU AGPL v3 or later + * @link https://larpingapp.com + * + * @phpversion 8.2 + */ + namespace OCA\LarpingApp\AppInfo; use OCP\AppFramework\App; +use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\AppFramework\Bootstrap\IBootContext; -use OCP\AppFramework\Bootstrap\IBootstrap; /** - * Class Application + * Main application class for LarpingApp * - * @package OCA\LarpingApp\AppInfo + * @category Application + * @package OCA\LarpingApp\AppInfo + * @author Ruben Linde + * @license https://www.gnu.org/licenses/agpl-3.0.html GNU AGPL v3 or later + * @link https://larpingapp.com */ class Application extends App implements IBootstrap { - public const APP_ID = 'larpingapp'; - - /** - * Constructor - * - * @param array $urlParams - */ - public function __construct(array $urlParams = []) - { - parent::__construct(appName: self::APP_ID, urlParams: $urlParams); - } - - public function register(IRegistrationContext $context): void - { - } - - public function boot(IBootContext $context): void - { - } + /** + * Application ID + */ + public const APP_ID = 'larpingapp'; + + /** + * Constructor for the application + * + * @param array $urlParams URL parameters + */ + public function __construct(array $urlParams = []) + { + parent::__construct(self::APP_ID, $urlParams); + } + + /** + * Register application services + * + * @param IRegistrationContext $context Registration context + * + * @return void + */ + public function register(IRegistrationContext $context): void + { + // Register services here + } + + /** + * Boot the application + * + * @param IBootContext $context Boot context + * + * @return void + */ + public function boot(IBootContext $context): void + { + // Boot services here + } } diff --git a/lib/Controller/ObjectsController.php b/lib/Controller/ObjectsController.php index f1c12b9..e3aec11 100644 --- a/lib/Controller/ObjectsController.php +++ b/lib/Controller/ObjectsController.php @@ -16,27 +16,26 @@ class ObjectsController extends Controller { public function __construct( - $appName, - IRequest $request, - private readonly ObjectService $objectService, - private readonly CharacterService $characterService - ) - { + $appName, + IRequest $request, + private readonly ObjectService $objectService, + private readonly CharacterService $characterService + ) { parent::__construct($appName, $request); } - /** - * Return (and search) all objects - * - * @NoAdminRequired - * @NoCSRFRequired + /** + * Return (and search) all objects + * + * @NoAdminRequired + * @NoCSRFRequired * * @param string $objectType The type of object to return - * - * @return JSONResponse - */ - public function index(string $objectType): JSONResponse - { + * + * @return JSONResponse + */ + public function index(string $objectType): JSONResponse + { // Retrieve all request parameters $requestParams = $this->request->getParams(); @@ -48,18 +47,18 @@ public function index(string $objectType): JSONResponse // Return JSON response return new JSONResponse($data); - } - - /** - * Read a single object - * - * @NoAdminRequired - * @NoCSRFRequired - * - * @return JSONResponse - */ - public function show(string $objectType, string $id): JSONResponse - { + } + + /** + * Read a single object + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @return JSONResponse + */ + public function show(string $objectType, string $id): JSONResponse + { try { // Get extend parameter if present $extend = $requestParams['extend'] ?? $requestParams['_extend'] ?? []; @@ -78,18 +77,18 @@ public function show(string $objectType, string $id): JSONResponse 400 ); } - } - - /** - * Create an object - * - * @NoAdminRequired - * @NoCSRFRequired - * - * @return JSONResponse - */ - public function create(string $objectType): JSONResponse - { + } + + /** + * Create an object + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @return JSONResponse + */ + public function create(string $objectType): JSONResponse + { try { // Get all parameters from the request $data = $this->request->getParams(); @@ -120,18 +119,18 @@ public function create(string $objectType): JSONResponse 400 ); } - } - - /** - * Update an object - * - * @NoAdminRequired - * @NoCSRFRequired - * - * @return JSONResponse - */ - public function update(string $objectType, string $id): JSONResponse - { + } + + /** + * Update an object + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @return JSONResponse + */ + public function update(string $objectType, string $id): JSONResponse + { try { // Get all parameters from the request $data = $this->request->getParams(); @@ -161,18 +160,18 @@ public function update(string $objectType, string $id): JSONResponse 400 ); } - } - - /** - * Delete an object - * - * @NoAdminRequired - * @NoCSRFRequired - * - * @return JSONResponse - */ - public function destroy(string $objectType, string $id): JSONResponse - { + } + + /** + * Delete an object + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @return JSONResponse + */ + public function destroy(string $objectType, string $id): JSONResponse + { try { // Delete the object $result = $this->objectService->deleteObject($objectType, $id); @@ -185,15 +184,15 @@ public function destroy(string $objectType, string $id): JSONResponse 400 ); } - } + } - /** + /** * Get audit trail for a specific object * * @NoAdminRequired * @NoCSRFRequired - * - * @return JSONResponse + * + * @return JSONResponse */ public function getAuditTrail(string $objectType, string $id): JSONResponse { @@ -253,8 +252,8 @@ public function getUses(string $objectType, string $id): JSONResponse * @NoAdminRequired * @NoCSRFRequired * - * @param string $objectType The type of object to lock - * @param string $id The ID of the object to lock + * @param string $objectType The type of object to lock + * @param string $id The ID of the object to lock * @return JSONResponse */ public function lock(string $objectType, string $id): JSONResponse @@ -285,8 +284,8 @@ public function lock(string $objectType, string $id): JSONResponse * @NoAdminRequired * @NoCSRFRequired * - * @param string $objectType The type of object to unlock - * @param string $id The ID of the object to unlock + * @param string $objectType The type of object to unlock + * @param string $id The ID of the object to unlock * @return JSONResponse */ public function unlock(string $objectType, string $id): JSONResponse @@ -308,8 +307,8 @@ public function unlock(string $objectType, string $id): JSONResponse * @NoAdminRequired * @NoCSRFRequired * - * @param string $objectType The type of object to check - * @param string $id The ID of the object to check + * @param string $objectType The type of object to check + * @param string $id The ID of the object to check * @return JSONResponse */ public function isLocked(string $objectType, string $id): JSONResponse @@ -331,8 +330,8 @@ public function isLocked(string $objectType, string $id): JSONResponse * @NoAdminRequired * @NoCSRFRequired * - * @param string $objectType The type of object to revert - * @param string $id The ID of the object to revert + * @param string $objectType The type of object to revert + * @param string $id The ID of the object to revert * @return JSONResponse */ public function revert(string $objectType, string $id): JSONResponse diff --git a/lib/Db/Ability.php b/lib/Db/Ability.php index 62a30f7..ff25c58 100644 --- a/lib/Db/Ability.php +++ b/lib/Db/Ability.php @@ -1,74 +1,84 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; use DateTime; use JsonSerializable; use OCP\AppFramework\Db\Entity; +/** + * @method string getName() + * @method void setName(string $name) + * @method string getDescription() + * @method void setDescription(string $description) + */ class Ability extends Entity implements JsonSerializable { + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $description; - protected ?string $name = null; - protected ?string $description = null; - protected ?int $base = 0; - protected ?bool $allowedNegative = false; - public function __construct() { - $this->addType(fieldName: 'name', type: 'string'); - $this->addType(fieldName: 'description', type: 'string'); - $this->addType(fieldName: 'base', type: 'string'); - $this->addType(fieldName: 'allowedNegative', type: 'boolean'); - - } - - public function getJsonFields(): array - { - return array_keys( - array_filter($this->getFieldTypes(), function ($field) { - return $field === 'json'; - }) - ); - } - - public function hydrate(array $object): self - { - $jsonFields = $this->getJsonFields(); - - foreach($object as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === []) { - $value = []; - } - - $method = 'set'.ucfirst($key); - - try { - $this->$method($value); - } catch (\Exception $exception) { -// ("Error writing $key"); - } - } - - return $this; - } - - public function jsonSerialize(): array - { - $array = [ - 'id' => $this->id, - 'name' => $this->name, - 'description' => $this->description, - 'base' => $this->base, - 'allowedNegative' => $this->allowedNegative, - ]; + /** + * Constructor to set the defaults + */ + public function __construct() + { + $this->addType('name', 'string'); + $this->addType('description', 'string'); + } - $jsonFields = $this->getJsonFields(); + /** + * Get the fields that should be serialized to JSON + * + * @return array + */ + public function getJsonFields(): array + { + return [ + 'id', + 'name', + 'description' + ]; + } - foreach ($array as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === null) { - $array[$key] = []; - } - } + /** + * Hydrate the entity from an array of data + * + * @param array $data + * @return void + */ + public function hydrate(array $data): void + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } - return $array; - } + /** + * Serialize the entity to JSON + * + * @return array + */ + public function jsonSerialize(): array + { + $data = []; + foreach ($this->getJsonFields() as $field) { + $data[$field] = $this->$field; + } + return $data; + } } diff --git a/lib/Db/AbilityMapper.php b/lib/Db/AbilityMapper.php index bb9b0ef..58d513a 100644 --- a/lib/Db/AbilityMapper.php +++ b/lib/Db/AbilityMapper.php @@ -1,83 +1,94 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; -use OCA\LarpingApp\Db\Ability; -use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +/** + * @template-extends QBMapper + * @package OCA\LarpingApp\Db + */ class AbilityMapper extends QBMapper { - /** - * The name of the database table for abilities - * - * @var string - * @psalm-var string - * @phpstan-var string - */ - private const TABLE_NAME = 'larpingapp_abilities'; - - public function __construct(IDBConnection $db) - { - parent::__construct($db, 'larpingapp_abilities'); - } - - public function find(int $id): Ability - { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from('larpingapp_abilities') - ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) - ); - - return $this->findEntity(query: $qb); - } + /** + * @param IDBConnection $db Database connection + */ + public function __construct(IDBConnection $db) + { + parent::__construct($db, 'larpingapp_abilities', Ability::class); + } - public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array - { - $qb = $this->db->getQueryBuilder(); + /** + * Find an ability by ID + * + * @param int $id The ability ID + * @return Ability + * @throws \OCP\AppFramework\Db\DoesNotExistException + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException + */ + public function find(int $id): Ability + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); + return $this->findEntity($qb); + } - $qb->select('*') - ->from('larpingapp_abilities') - ->setMaxResults($limit) - ->setFirstResult($offset); + /** + * Find all abilities for a user + * + * @param string $userId The user ID + * @return Ability[] + */ + public function findAll(string $userId): array + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId))); + + return $this->findEntities($qb); + } - foreach($filters as $filter => $value) { - if ($value === 'IS NOT NULL') { - $qb->andWhere($qb->expr()->isNotNull($filter)); - } elseif ($value === 'IS NULL') { - $qb->andWhere($qb->expr()->isNull($filter)); - } else { - $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); - } + /** + * Create a new ability from array data + * + * @param array $data The ability data + * @return Ability + */ + public function createFromArray(array $data): Ability + { + $ability = new Ability(); + foreach ($data as $key => $value) { + $ability->$key = $value; } + return $this->insert($ability); + } - if (!empty($searchConditions)) { - $qb->andWhere('(' . implode(' OR ', $searchConditions) . ')'); - foreach ($searchParams as $param => $value) { - $qb->setParameter($param, $value); - } + /** + * Update an ability from array data + * + * @param int $id The ability ID + * @param array $data The updated ability data + * @return Ability + */ + public function updateFromArray(int $id, array $data): Ability + { + $ability = $this->find($id); + foreach ($data as $key => $value) { + $ability->$key = $value; } - - return $this->findEntities(query: $qb); - } - - public function createFromArray(array $object): Ability - { - $ability = new Ability(); - $ability->hydrate(object: $object); - return $this->insert(entity: $ability); - } - - public function updateFromArray(int $id, array $object): Ability - { - $ability = $this->find($id); - $ability->hydrate($object); - - return $this->update($ability); - } + return $this->update($ability); + } } diff --git a/lib/Db/Character.php b/lib/Db/Character.php index c3039c6..a33d328 100644 --- a/lib/Db/Character.php +++ b/lib/Db/Character.php @@ -1,111 +1,84 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; use DateTime; use JsonSerializable; use OCP\AppFramework\Db\Entity; +/** + * @method string getName() + * @method void setName(string $name) + * @method string getDescription() + * @method void setDescription(string $description) + */ class Character extends Entity implements JsonSerializable { - protected ?string $name = null; - protected ?string $ocName = null; - protected ?string $description = null; - protected ?string $background = null; - protected ?string $itemsAndMoney = null; - protected ?string $notice = null; - protected ?string $faith = null; - protected ?string $slNotesPublic = null; - protected ?string $slNotesPrivate = null; - protected ?string $card = null; - protected ?array $stats = []; - protected ?int $gold = null; - protected ?int $silver = null; - protected ?int $copper = null; - protected ?array $events = []; - protected ?array $skills = []; - protected ?array $items = []; - protected ?array $conditions = null; - protected ?string $type = 'player'; - protected ?string $approved = 'no'; - - public function __construct() { - $this->addType('name', 'string'); - $this->addType('ocName', 'string'); - $this->addType('description', 'string'); - $this->addType('background', 'string'); - $this->addType('itemsAndMoney', 'string'); - $this->addType('notice', 'string'); - $this->addType('faith', 'string'); - $this->addType('slNotesPublic', 'string'); - $this->addType('slNotesPrivate', 'string'); - $this->addType('card', 'string'); - $this->addType('stats', 'json'); - $this->addType('gold', 'integer'); - $this->addType('silver', 'integer'); - $this->addType('copper', 'integer'); - $this->addType('events', 'json'); - $this->addType('skills', 'json'); - $this->addType('items', 'json'); - $this->addType('conditions', 'json'); - $this->addType('type', 'string'); - $this->addType('approved', 'string'); - } - - public function getJsonFields(): array - { - return array_keys( - array_filter($this->getFieldTypes(), function ($field) { - return $field === 'json'; - }) - ); - } - - public function hydrate(array $object): self - { - $jsonFields = $this->getJsonFields(); - - foreach($object as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === []) { - $value = []; - } + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $description; - $method = 'set'.ucfirst($key); + /** + * Constructor to set the defaults + */ + public function __construct() + { + $this->addType('name', 'string'); + $this->addType('description', 'string'); + } - try { - $this->$method($value); - } catch (\Exception $exception) { -// ("Error writing $key"); - } - } + /** + * Get the fields that should be serialized to JSON + * + * @return array + */ + public function getJsonFields(): array + { + return [ + 'id', + 'name', + 'description' + ]; + } - return $this; - } + /** + * Hydrate the entity from an array of data + * + * @param array $data + * @return void + */ + public function hydrate(array $data): void + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } - public function jsonSerialize(): array - { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'ocName' => $this->ocName, - 'description' => $this->description, - 'background' => $this->background, - 'itemsAndMoney' => $this->itemsAndMoney, - 'notice' => $this->notice, - 'faith' => $this->faith, - 'slNotesPublic' => $this->slNotesPublic, - 'slNotesPrivate' => $this->slNotesPrivate, - 'card' => $this->card, - 'stats' => $this->stats, - 'gold' => $this->gold, - 'silver' => $this->silver, - 'copper' => $this->copper, - 'events' => $this->events, - 'skills' => $this->skills, - 'items' => $this->items, - 'conditions' => $this->conditions, - 'type' => $this->type, - 'approved' => $this->approved, - ]; - } + /** + * Serialize the entity to JSON + * + * @return array + */ + public function jsonSerialize(): array + { + $data = []; + foreach ($this->getJsonFields() as $field) { + $data[$field] = $this->$field; + } + return $data; + } } diff --git a/lib/Db/CharacterMapper.php b/lib/Db/CharacterMapper.php index 94532a1..19395ba 100644 --- a/lib/Db/CharacterMapper.php +++ b/lib/Db/CharacterMapper.php @@ -1,5 +1,13 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; use OCA\LarpingApp\Db\Character; @@ -8,67 +16,81 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +/** + * @template-extends QBMapper + * @package OCA\LarpingApp\Db + */ class CharacterMapper extends QBMapper { - public function __construct(IDBConnection $db) - { - parent::__construct($db, 'larpingapp_characters'); - } - - public function find(int $id): Character - { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from('larpingapp_characters') - ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) - ); - - return $this->findEntity(query: $qb); - } + /** + * @param IDBConnection $db Database connection + */ + public function __construct(IDBConnection $db) + { + parent::__construct($db, 'larpingapp_characters', Character::class); + } - public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array - { - $qb = $this->db->getQueryBuilder(); + /** + * Find a character by ID + * + * @param int $id The character ID + * @return Character + * @throws \OCP\AppFramework\Db\DoesNotExistException + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException + */ + public function find(int $id): Character + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); + return $this->findEntity($qb); + } - $qb->select('*') - ->from('larpingapp_characters') - ->setMaxResults($limit) - ->setFirstResult($offset); + /** + * Find all characters for a user + * + * @param string $userId The user ID + * @return Character[] + */ + public function findAll(string $userId): array + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId))); + + return $this->findEntities($qb); + } - foreach($filters as $filter => $value) { - if ($value === 'IS NOT NULL') { - $qb->andWhere($qb->expr()->isNotNull($filter)); - } elseif ($value === 'IS NULL') { - $qb->andWhere($qb->expr()->isNull($filter)); - } else { - $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); - } + /** + * Create a new character from array data + * + * @param array $data The character data + * @return Character + */ + public function createFromArray(array $data): Character + { + $character = new Character(); + foreach ($data as $key => $value) { + $character->$key = $value; } + return $this->insert($character); + } - if (!empty($searchConditions)) { - $qb->andWhere('(' . implode(' OR ', $searchConditions) . ')'); - foreach ($searchParams as $param => $value) { - $qb->setParameter($param, $value); - } + /** + * Update a character from array data + * + * @param int $id The character ID + * @param array $data The updated character data + * @return Character + */ + public function updateFromArray(int $id, array $data): Character + { + $character = $this->find($id); + foreach ($data as $key => $value) { + $character->$key = $value; } - - return $this->findEntities(query: $qb); - } - - public function createFromArray(array $object): Character - { - $character = new Character(); - $character->hydrate(object: $object); - return $this->insert(entity: $character); - } - - public function updateFromArray(int $id, array $object): Character - { - $character = $this->find($id); - $character->hydrate($object); - - return $this->update($character); - } + return $this->update($character); + } } diff --git a/lib/Db/Condition.php b/lib/Db/Condition.php index 72870ce..011f0fc 100644 --- a/lib/Db/Condition.php +++ b/lib/Db/Condition.php @@ -1,66 +1,84 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; use DateTime; use JsonSerializable; use OCP\AppFramework\Db\Entity; +/** + * @method string getName() + * @method void setName(string $name) + * @method string getDescription() + * @method void setDescription(string $description) + */ class Condition extends Entity implements JsonSerializable { - protected ?string $name = null; - protected ?string $description = null; - protected ?string $effect = null; - protected ?array $effects = []; - protected ?bool $unique = false; + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $description; - public function __construct() { + /** + * Constructor to set the defaults + */ + public function __construct() + { $this->addType('name', 'string'); $this->addType('description', 'string'); - $this->addType('effect', 'string'); - $this->addType('effects', 'array'); - $this->addType('unique', 'boolean'); } - public function getJsonFields(): array - { - return array_keys( - array_filter($this->getFieldTypes(), function ($field) { - return $field === 'json'; - }) - ); - } - - public function hydrate(array $object): self - { - $jsonFields = $this->getJsonFields(); - - foreach($object as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === []) { - $value = []; - } - - $method = 'set'.ucfirst($key); - - try { - $this->$method($value); - } catch (\Exception $exception) { -// ("Error writing $key"); - } - } + /** + * Get the fields that should be serialized to JSON + * + * @return array + */ + public function getJsonFields(): array + { + return [ + 'id', + 'name', + 'description' + ]; + } - return $this; - } + /** + * Hydrate the entity from an array of data + * + * @param array $data + * @return void + */ + public function hydrate(array $data): void + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } + /** + * Serialize the entity to JSON + * + * @return array + */ public function jsonSerialize(): array { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'description' => $this->description, - 'effect' => $this->effect, - 'effects' => $this->effects, - 'unique' => $this->unique, - ]; + $data = []; + foreach ($this->getJsonFields() as $field) { + $data[$field] = $this->$field; + } + return $data; } } \ No newline at end of file diff --git a/lib/Db/ConditionMapper.php b/lib/Db/ConditionMapper.php index 8e8c1f2..757493b 100644 --- a/lib/Db/ConditionMapper.php +++ b/lib/Db/ConditionMapper.php @@ -1,5 +1,13 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; use OCA\LarpingApp\Db\Condition; @@ -8,67 +16,81 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +/** + * @template-extends QBMapper + * @package OCA\LarpingApp\Db + */ class ConditionMapper extends QBMapper { - public function __construct(IDBConnection $db) - { - parent::__construct($db, 'larpingapp_conditions'); - } - - public function find(int $id): Condition - { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from('larpingapp_conditions') - ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) - ); - - return $this->findEntity(query: $qb); - } + /** + * @param IDBConnection $db Database connection + */ + public function __construct(IDBConnection $db) + { + parent::__construct($db, 'larpingapp_conditions', Condition::class); + } - public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array - { - $qb = $this->db->getQueryBuilder(); + /** + * Find a condition by ID + * + * @param int $id The condition ID + * @return Condition + * @throws \OCP\AppFramework\Db\DoesNotExistException + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException + */ + public function find(int $id): Condition + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); + return $this->findEntity($qb); + } - $qb->select('*') - ->from('larpingapp_conditions') - ->setMaxResults($limit) - ->setFirstResult($offset); + /** + * Find all conditions for a user + * + * @param string $userId The user ID + * @return Condition[] + */ + public function findAll(string $userId): array + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId))); + + return $this->findEntities($qb); + } - foreach($filters as $filter => $value) { - if ($value === 'IS NOT NULL') { - $qb->andWhere($qb->expr()->isNotNull($filter)); - } elseif ($value === 'IS NULL') { - $qb->andWhere($qb->expr()->isNull($filter)); - } else { - $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); - } + /** + * Create a new condition from array data + * + * @param array $data The condition data + * @return Condition + */ + public function createFromArray(array $data): Condition + { + $condition = new Condition(); + foreach ($data as $key => $value) { + $condition->$key = $value; } + return $this->insert($condition); + } - if (!empty($searchConditions)) { - $qb->andWhere('(' . implode(' OR ', $searchConditions) . ')'); - foreach ($searchParams as $param => $value) { - $qb->setParameter($param, $value); - } + /** + * Update a condition from array data + * + * @param int $id The condition ID + * @param array $data The updated condition data + * @return Condition + */ + public function updateFromArray(int $id, array $data): Condition + { + $condition = $this->find($id); + foreach ($data as $key => $value) { + $condition->$key = $value; } - - return $this->findEntities(query: $qb); - } - - public function createFromArray(array $object): Condition - { - $condition = new Condition(); - $condition->hydrate(object: $object); - return $this->insert(entity: $condition); - } - - public function updateFromArray(int $id, array $object): Condition - { - $condition = $this->find($id); - $condition->hydrate($object); - - return $this->update($condition); - } + return $this->update($condition); + } } diff --git a/lib/Db/Effect.php b/lib/Db/Effect.php index 1774e54..1face37 100644 --- a/lib/Db/Effect.php +++ b/lib/Db/Effect.php @@ -1,69 +1,84 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; use DateTime; use JsonSerializable; use OCP\AppFramework\Db\Entity; +/** + * @method string getName() + * @method void setName(string $name) + * @method string getDescription() + * @method void setDescription(string $description) + */ class Effect extends Entity implements JsonSerializable { - protected ?string $name = null; - protected ?string $description = null; - protected ?string $statId = null; - protected ?int $modifier = null; - protected ?string $modification = 'positive'; - protected ?string $cumulative = 'non-cumulative'; - - public function __construct() { - $this->addType('name', 'string'); - $this->addType('description', 'string'); - $this->addType('statId', 'string'); - $this->addType('modifier', 'integer'); - $this->addType('modification', 'string'); - $this->addType('cumulative', 'string'); - } - - public function getJsonFields(): array - { - return array_keys( - array_filter($this->getFieldTypes(), function ($field) { - return $field === 'json'; - }) - ); - } - - public function hydrate(array $object): self - { - $jsonFields = $this->getJsonFields(); - - foreach($object as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === []) { - $value = []; - } + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $description; - $method = 'set'.ucfirst($key); + /** + * Constructor to set the defaults + */ + public function __construct() + { + $this->addType('name', 'string'); + $this->addType('description', 'string'); + } - try { - $this->$method($value); - } catch (\Exception $exception) { -// ("Error writing $key"); - } - } + /** + * Get the fields that should be serialized to JSON + * + * @return array + */ + public function getJsonFields(): array + { + return [ + 'id', + 'name', + 'description' + ]; + } - return $this; - } + /** + * Hydrate the entity from an array of data + * + * @param array $data + * @return void + */ + public function hydrate(array $data): void + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } - public function jsonSerialize(): array - { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'description' => $this->description, - 'statId' => $this->statId, - 'modifier' => $this->modifier, - 'modification' => $this->modification, - 'cumulative' => $this->cumulative, - ]; - } + /** + * Serialize the entity to JSON + * + * @return array + */ + public function jsonSerialize(): array + { + $data = []; + foreach ($this->getJsonFields() as $field) { + $data[$field] = $this->$field; + } + return $data; + } } \ No newline at end of file diff --git a/lib/Db/EffectMapper.php b/lib/Db/EffectMapper.php index 566234e..6ce0416 100644 --- a/lib/Db/EffectMapper.php +++ b/lib/Db/EffectMapper.php @@ -1,5 +1,13 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; use OCA\LarpingApp\Db\Effect; @@ -8,67 +16,81 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +/** + * @template-extends QBMapper + * @package OCA\LarpingApp\Db + */ class EffectMapper extends QBMapper { - public function __construct(IDBConnection $db) - { - parent::__construct($db, 'larpingapp_effects'); - } - - public function find(int $id): Effect - { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from('larpingapp_effects') - ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) - ); - - return $this->findEntity(query: $qb); - } + /** + * @param IDBConnection $db Database connection + */ + public function __construct(IDBConnection $db) + { + parent::__construct($db, 'larpingapp_effects', Effect::class); + } - public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array - { - $qb = $this->db->getQueryBuilder(); + /** + * Find an effect by ID + * + * @param int $id The effect ID + * @return Effect + * @throws \OCP\AppFramework\Db\DoesNotExistException + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException + */ + public function find(int $id): Effect + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); + return $this->findEntity($qb); + } - $qb->select('*') - ->from('larpingapp_effects') - ->setMaxResults($limit) - ->setFirstResult($offset); + /** + * Find all effects for a user + * + * @param string $userId The user ID + * @return Effect[] + */ + public function findAll(string $userId): array + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId))); + + return $this->findEntities($qb); + } - foreach($filters as $filter => $value) { - if ($value === 'IS NOT NULL') { - $qb->andWhere($qb->expr()->isNotNull($filter)); - } elseif ($value === 'IS NULL') { - $qb->andWhere($qb->expr()->isNull($filter)); - } else { - $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); - } + /** + * Create a new effect from array data + * + * @param array $data The effect data + * @return Effect + */ + public function createFromArray(array $data): Effect + { + $effect = new Effect(); + foreach ($data as $key => $value) { + $effect->$key = $value; } + return $this->insert($effect); + } - if (!empty($searchConditions)) { - $qb->andWhere('(' . implode(' OR ', $searchConditions) . ')'); - foreach ($searchParams as $param => $value) { - $qb->setParameter($param, $value); - } + /** + * Update an effect from array data + * + * @param int $id The effect ID + * @param array $data The updated effect data + * @return Effect + */ + public function updateFromArray(int $id, array $data): Effect + { + $effect = $this->find($id); + foreach ($data as $key => $value) { + $effect->$key = $value; } - - return $this->findEntities(query: $qb); - } - - public function createFromArray(array $object): Effect - { - $effect = new Effect(); - $effect->hydrate(object: $object); - return $this->insert(entity: $effect); - } - - public function updateFromArray(int $id, array $object): Effect - { - $effect = $this->find($id); - $effect->hydrate($object); - - return $this->update($effect); - } + return $this->update($effect); + } } diff --git a/lib/Db/Event.php b/lib/Db/Event.php index ed19ed9..d009fa0 100644 --- a/lib/Db/Event.php +++ b/lib/Db/Event.php @@ -1,72 +1,111 @@ + * @copyright 2024 Ruben Linde + * @license AGPL-3.0-or-later https://www.gnu.org/licenses/agpl-3.0.en.html + * + * @php-version 8.2 + */ + namespace OCA\LarpingApp\Db; use DateTime; use JsonSerializable; use OCP\AppFramework\Db\Entity; +/** + * Event entity class + * + * @category Apps + * @package LarpingApp + * @author Ruben Linde + * @copyright 2024 Ruben Linde + * @license AGPL-3.0-or-later https://www.gnu.org/licenses/agpl-3.0.en.html + * @link https://larpingapp.com + */ class Event extends Entity implements JsonSerializable { - protected ?string $name = null; - protected ?string $description = null; - protected ?array $players = []; - protected ?array $effects = []; - protected ?string $startDate = null; - protected ?string $endDate = null; - protected ?string $location = null; - - public function __construct() { - $this->addType('name', 'string'); - $this->addType('description', 'string'); - $this->addType('players', 'array'); - $this->addType('effects', 'json'); - $this->addType('startDate', 'string'); - $this->addType('endDate', 'string'); - $this->addType('location', 'string'); - } - - public function getJsonFields(): array - { - return array_keys( - array_filter($this->getFieldTypes(), function ($field) { - return $field === 'json'; - }) - ); - } - - public function hydrate(array $object): self - { - $jsonFields = $this->getJsonFields(); + /** + * Event properties + */ + protected $title; + protected $description; + protected $startDate; + protected $endDate; + protected $userId; - foreach($object as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === []) { - $value = []; - } + /** + * Constructor to set the defaults + */ + public function __construct() + { + $this->addType('title', 'string'); + $this->addType('description', 'string'); + $this->addType('startDate', 'datetime'); + $this->addType('endDate', 'datetime'); + $this->addType('userId', 'string'); + } - $method = 'set'.ucfirst($key); + /** + * Get the fields that should be serialized to JSON + * + * @return array + */ + public function getJsonFields(): array + { + return [ + 'id', + 'title', + 'description', + 'startDate', + 'endDate', + 'userId' + ]; + } - try { - $this->$method($value); - } catch (\Exception $exception) { -// ("Error writing $key"); - } - } + /** + * Hydrate the entity from an array of data + * + * @param array $data + * @return void + */ + public function hydrate(array $data): void + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } - return $this; - } + /** + * Serialize the entity to JSON + * + * @return array + */ + public function jsonSerialize(): array + { + $data = []; + foreach ($this->getJsonFields() as $field) { + $data[$field] = $this->$field; + } + return $data; + } - public function jsonSerialize(): array - { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'description' => $this->description, - 'players' => $this->players, - 'effects' => $this->effects, - 'startDate' => $this->startDate, - 'endDate' => $this->endDate, - 'location' => $this->location, - ]; - } + /** + * Convert to array + * + * @param array $params Optional parameters to include + * + * @return array The event data as array + */ + public function toArray(array $params = []): array + { + // ... existing code ... + } } \ No newline at end of file diff --git a/lib/Db/EventMapper.php b/lib/Db/EventMapper.php index 9adb7ba..42316f7 100644 --- a/lib/Db/EventMapper.php +++ b/lib/Db/EventMapper.php @@ -1,5 +1,20 @@ + * @copyright 2024 Ruben Linde + * @license https://www.gnu.org/licenses/agpl-3.0.html GNU AGPL v3 or later + * @link https://larpingapp.com + * + * @phpversion 8.2 + */ + namespace OCA\LarpingApp\Db; use OCA\LarpingApp\Db\Event; @@ -8,43 +23,81 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +/** + * Mapper class for Event entities + * + * @category Database + * @package OCA\LarpingApp\Db + * @author Ruben Linde + * @license https://www.gnu.org/licenses/agpl-3.0.html GNU AGPL v3 or later + * @link https://larpingapp.com + */ class EventMapper extends QBMapper { - public function __construct(IDBConnection $db) - { - parent::__construct($db, 'larpingapp_events'); - } - - public function find(int $id): Event - { - $qb = $this->db->getQueryBuilder(); + /** + * Constructor for EventMapper + * + * @param IDBConnection $db Database connection + */ + public function __construct(IDBConnection $db) + { + parent::__construct($db, 'larpingapp_events', Event::class); + } - $qb->select('*') - ->from('larpingapp_events') - ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) - ); + /** + * Find an event by ID + * + * @param int $id The event ID + * @return Event + * @throws \OCP\AppFramework\Db\DoesNotExistException + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException + */ + public function find(int $id): Event + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); + + return $this->findEntity($qb); + } - return $this->findEntity(query: $qb); - } + /** + * Find all events matching the given criteria + * + * @param int|null $limit Maximum number of results + * @param int|null $offset Result offset + * @param array|null $filters Additional filters + * @param array|null $searchConditions Search conditions + * @param array|null $searchParams Search parameters + * @return Event[] + */ + public function findAll( + ?int $limit = null, + ?int $offset = null, + ?array $filters = [], + ?array $searchConditions = [], + ?array $searchParams = [] + ): array { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()); - public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array - { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from('larpingapp_events') - ->setMaxResults($limit) - ->setFirstResult($offset); + if ($limit !== null) { + $qb->setMaxResults($limit); + } + if ($offset !== null) { + $qb->setFirstResult($offset); + } - foreach($filters as $filter => $value) { - if ($value === 'IS NOT NULL') { - $qb->andWhere($qb->expr()->isNotNull($filter)); - } elseif ($value === 'IS NULL') { - $qb->andWhere($qb->expr()->isNull($filter)); - } else { - $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); - } + foreach ($filters as $filter => $value) { + if ($value === 'IS NOT NULL') { + $qb->andWhere($qb->expr()->isNotNull($filter)); + } elseif ($value === 'IS NULL') { + $qb->andWhere($qb->expr()->isNull($filter)); + } else { + $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); + } } if (!empty($searchConditions)) { @@ -54,21 +107,37 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters } } - return $this->findEntities(query: $qb); - } - - public function createFromArray(array $object): Event - { - $event = new Event(); - $event->hydrate(object: $object); - return $this->insert(entity: $event); - } + return $this->findEntities($qb); + } - public function updateFromArray(int $id, array $object): Event - { - $event = $this->find($id); - $event->hydrate($object); + /** + * Create a new event from array data + * + * @param array $data The event data + * @return Event + */ + public function createFromArray(array $data): Event + { + $event = new Event(); + foreach ($data as $key => $value) { + $event->$key = $value; + } + return $this->insert($event); + } - return $this->update($event); - } + /** + * Update an event from array data + * + * @param int $id The event ID + * @param array $data The updated event data + * @return Event + */ + public function updateFromArray(int $id, array $data): Event + { + $event = $this->find($id); + foreach ($data as $key => $value) { + $event->$key = $value; + } + return $this->update($event); + } } diff --git a/lib/Db/Item.php b/lib/Db/Item.php index 3fda657..ed9c340 100644 --- a/lib/Db/Item.php +++ b/lib/Db/Item.php @@ -1,66 +1,83 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; -use DateTime; use JsonSerializable; use OCP\AppFramework\Db\Entity; +/** + * @method string getName() + * @method void setName(string $name) + * @method string getDescription() + * @method void setDescription(string $description) + */ class Item extends Entity implements JsonSerializable { - protected ?string $name = null; - protected ?string $description = null; - protected ?string $effect = null; - protected ?array $effects = null; - protected ?bool $unique = true; - - public function __construct() { - $this->addType('name', 'string'); - $this->addType('description', 'string'); - $this->addType('effect', 'string'); - $this->addType('effects', 'json'); - $this->addType('unique', 'boolean'); - } - - public function getJsonFields(): array - { - return array_keys( - array_filter($this->getFieldTypes(), function ($field) { - return $field === 'json'; - }) - ); - } - - public function hydrate(array $object): self - { - $jsonFields = $this->getJsonFields(); - - foreach($object as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === []) { - $value = []; - } + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $description; - $method = 'set'.ucfirst($key); + /** + * Constructor to set the defaults + */ + public function __construct() + { + $this->addType('name', 'string'); + $this->addType('description', 'string'); + } - try { - $this->$method($value); - } catch (\Exception $exception) { -// ("Error writing $key"); - } - } + /** + * Get the fields that should be serialized to JSON + * + * @return array + */ + public function getJsonFields(): array + { + return [ + 'id', + 'name', + 'description' + ]; + } - return $this; - } + /** + * Hydrate the entity from an array of data + * + * @param array $data + * @return void + */ + public function hydrate(array $data): void + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } - public function jsonSerialize(): array - { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'description' => $this->description, - 'effect' => $this->effect, - 'effects' => $this->effects, - 'unique' => $this->unique, - ]; - } + /** + * Serialize the entity to JSON + * + * @return array + */ + public function jsonSerialize(): array + { + $data = []; + foreach ($this->getJsonFields() as $field) { + $data[$field] = $this->$field; + } + return $data; + } } \ No newline at end of file diff --git a/lib/Db/ItemMapper.php b/lib/Db/ItemMapper.php index 62fcde8..b063153 100644 --- a/lib/Db/ItemMapper.php +++ b/lib/Db/ItemMapper.php @@ -1,5 +1,19 @@ + * @copyright 2024 Ruben Linde + * @license AGPL-3.0-or-later https://www.gnu.org/licenses/agpl-3.0.en.html + * + * @php-version 8.2 + */ + namespace OCA\LarpingApp\Db; use OCA\LarpingApp\Db\Item; @@ -8,67 +22,89 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +/** + * Data mapper for Item entities + * + * @category Apps + * @package LarpingApp + * @author Ruben Linde + * @copyright 2024 Ruben Linde + * @license AGPL-3.0-or-later https://www.gnu.org/licenses/agpl-3.0.en.html + * @link https://larpingapp.com + */ class ItemMapper extends QBMapper { - public function __construct(IDBConnection $db) - { - parent::__construct($db, 'larpingapp_items'); - } - - public function find(int $id): Item - { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from('larpingapp_items') - ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) - ); - - return $this->findEntity(query: $qb); - } + /** + * Constructor + * + * @param IDBConnection $db Database connection + */ + public function __construct(IDBConnection $db) + { + parent::__construct($db, 'larpingapp_items', Item::class); + } - public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array - { - $qb = $this->db->getQueryBuilder(); + /** + * Find an item by ID + * + * @param int $id The item ID + * + * @throws \OCP\AppFramework\Db\DoesNotExistException + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException + * + * @return Item The found item + */ + public function find(int $id): Item + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); + return $this->findEntity($qb); + } - $qb->select('*') - ->from('larpingapp_items') - ->setMaxResults($limit) - ->setFirstResult($offset); + /** + * Find all items + * + * @return Item[] Array of items + */ + public function findAll(): array + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()); + + return $this->findEntities($qb); + } - foreach($filters as $filter => $value) { - if ($value === 'IS NOT NULL') { - $qb->andWhere($qb->expr()->isNotNull($filter)); - } elseif ($value === 'IS NULL') { - $qb->andWhere($qb->expr()->isNull($filter)); - } else { - $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); - } + /** + * Create a new item from array data + * + * @param array $data The item data + * @return Item + */ + public function createFromArray(array $data): Item + { + $item = new Item(); + foreach ($data as $key => $value) { + $item->$key = $value; } + return $this->insert($item); + } - if (!empty($searchConditions)) { - $qb->andWhere('(' . implode(' OR ', $searchConditions) . ')'); - foreach ($searchParams as $param => $value) { - $qb->setParameter($param, $value); - } + /** + * Update an item from array data + * + * @param int $id The item ID + * @param array $data The updated item data + * @return Item + */ + public function updateFromArray(int $id, array $data): Item + { + $item = $this->find($id); + foreach ($data as $key => $value) { + $item->$key = $value; } - - return $this->findEntities(query: $qb); - } - - public function createFromArray(array $object): Item - { - $item = new Item(); - $item->hydrate(object: $object); - return $this->insert(entity: $item); - } - - public function updateFromArray(int $id, array $object): Item - { - $item = $this->find($id); - $item->hydrate($object); - - return $this->update($item); - } + return $this->update($item); + } } diff --git a/lib/Db/Player.php b/lib/Db/Player.php index 9e97da5..0f57d03 100644 --- a/lib/Db/Player.php +++ b/lib/Db/Player.php @@ -1,89 +1,84 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; use DateTime; use JsonSerializable; use OCP\AppFramework\Db\Entity; +/** + * @method string getName() + * @method void setName(string $name) + * @method string getDescription() + * @method void setDescription(string $description) + */ class Player extends Entity implements JsonSerializable { - - protected ?string $name = null; - protected ?string $summary = null; - protected ?string $description = null; - protected ?string $image = null; - protected ?string $search = null; - - protected bool $listed = false; - protected ?string $organisation = null; - protected ?array $metadata = null; - - public function __construct() { - $this->addType(fieldName: 'name', type: 'string'); - $this->addType(fieldName: 'summary', type: 'string'); - $this->addType(fieldName: 'description', type: 'string'); - $this->addType(fieldName: 'image', type: 'string'); - $this->addType(fieldName: 'search', type: 'string'); - $this->addType(fieldName: 'listed', type: 'boolean'); - $this->addType(fieldName: 'organisation', type: 'string'); - $this->addType(fieldName: 'metadata', type: 'json'); - - } - - public function getJsonFields(): array - { - return array_keys( - array_filter($this->getFieldTypes(), function ($field) { - return $field === 'json'; - }) - ); - } - - public function hydrate(array $object): self - { - $jsonFields = $this->getJsonFields(); - - foreach($object as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === []) { - $value = []; - } - - $method = 'set'.ucfirst($key); - - try { - $this->$method($value); - } catch (\Exception $exception) { -// ("Error writing $key"); - } - } - - return $this; - } - - public function jsonSerialize(): array - { - $array = [ - 'id' => $this->id, - 'name' => $this->name, - 'summary' => $this->summary, - 'description' => $this->description, - 'image' => $this->image, - 'search' => $this->search, - 'listed' => $this->listed, - 'metadata' => $this->metadata, - 'organisation'=> $this->organisation, - - ]; - - $jsonFields = $this->getJsonFields(); - - foreach ($array as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === null) { - $array[$key] = []; - } - } - - return $array; - } + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $description; + + /** + * Constructor to set the defaults + */ + public function __construct() + { + $this->addType('name', 'string'); + $this->addType('description', 'string'); + } + + /** + * Get the fields that should be serialized to JSON + * + * @return array + */ + public function getJsonFields(): array + { + return [ + 'id', + 'name', + 'description' + ]; + } + + /** + * Hydrate the entity from an array of data + * + * @param array $data + * @return void + */ + public function hydrate(array $data): void + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } + + /** + * Serialize the entity to JSON + * + * @return array + */ + public function jsonSerialize(): array + { + $data = []; + foreach ($this->getJsonFields() as $field) { + $data[$field] = $this->$field; + } + return $data; + } } diff --git a/lib/Db/PlayerMapper.php b/lib/Db/PlayerMapper.php index 96ec91c..eb35d3d 100644 --- a/lib/Db/PlayerMapper.php +++ b/lib/Db/PlayerMapper.php @@ -1,5 +1,19 @@ + * @copyright 2024 Ruben Linde + * @license AGPL-3.0-or-later https://www.gnu.org/licenses/agpl-3.0.en.html + * + * @php-version 8.2 + */ + namespace OCA\LarpingApp\Db; use OCA\LarpingApp\Db\Player; @@ -8,67 +22,89 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +/** + * Data mapper for Player entities + * + * @category Apps + * @package LarpingApp + * @author Ruben Linde + * @copyright 2024 Ruben Linde + * @license AGPL-3.0-or-later https://www.gnu.org/licenses/agpl-3.0.en.html + * @link https://larpingapp.com + */ class PlayerMapper extends QBMapper { - public function __construct(IDBConnection $db) - { - parent::__construct($db, 'larpingapp_players'); - } - - public function find(int $id): Player - { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from('larpingapp_players') - ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) - ); - - return $this->findEntity(query: $qb); - } + /** + * Constructor + * + * @param IDBConnection $db Database connection + */ + public function __construct(IDBConnection $db) + { + parent::__construct($db, 'larpingapp_players', Player::class); + } - public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array - { - $qb = $this->db->getQueryBuilder(); + /** + * Find a player by ID + * + * @param int $id The player ID + * + * @throws \OCP\AppFramework\Db\DoesNotExistException + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException + * + * @return Player The found player + */ + public function find(int $id): Player + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); + return $this->findEntity($qb); + } - $qb->select('*') - ->from('larpingapp_players') - ->setMaxResults($limit) - ->setFirstResult($offset); + /** + * Find all players + * + * @return Player[] Array of players + */ + public function findAll(): array + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()); + + return $this->findEntities($qb); + } - foreach($filters as $filter => $value) { - if ($value === 'IS NOT NULL') { - $qb->andWhere($qb->expr()->isNotNull($filter)); - } elseif ($value === 'IS NULL') { - $qb->andWhere($qb->expr()->isNull($filter)); - } else { - $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); - } + /** + * Create a new player from array data + * + * @param array $data The player data + * @return Player + */ + public function createFromArray(array $data): Player + { + $player = new Player(); + foreach ($data as $key => $value) { + $player->$key = $value; } + return $this->insert($player); + } - if (!empty($searchConditions)) { - $qb->andWhere('(' . implode(' OR ', $searchConditions) . ')'); - foreach ($searchParams as $param => $value) { - $qb->setParameter($param, $value); - } + /** + * Update a player from array data + * + * @param int $id The player ID + * @param array $data The updated player data + * @return Player + */ + public function updateFromArray(int $id, array $data): Player + { + $player = $this->find($id); + foreach ($data as $key => $value) { + $player->$key = $value; } - - return $this->findEntities(query: $qb); - } - - public function createFromArray(array $object): Player - { - $player = new Player(); - $player->hydrate(object: $object); - return $this->insert(entity: $player); - } - - public function updateFromArray(int $id, array $object): Player - { - $player = $this->find($id); - $player->hydrate($object); - - return $this->update($player); - } + return $this->update($player); + } } diff --git a/lib/Db/Setting.php b/lib/Db/Setting.php index 3cd4e1d..0af5a07 100644 --- a/lib/Db/Setting.php +++ b/lib/Db/Setting.php @@ -1,89 +1,83 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; -use DateTime; use JsonSerializable; use OCP\AppFramework\Db\Entity; +/** + * @method string getName() + * @method void setName(string $name) + * @method string getValue() + * @method void setValue(string $value) + */ class Setting extends Entity implements JsonSerializable { - - protected ?string $title = null; - protected ?string $summary = null; - protected ?string $description = null; - protected ?string $image = null; - protected ?string $search = null; - - protected bool $listed = false; - protected ?string $organisation = null; - protected ?array $metadata = null; - - public function __construct() { - $this->addType(fieldName: 'title', type: 'string'); - $this->addType(fieldName: 'summary', type: 'string'); - $this->addType(fieldName: 'description', type: 'string'); - $this->addType(fieldName: 'image', type: 'string'); - $this->addType(fieldName: 'search', type: 'string'); - $this->addType(fieldName: 'listed', type: 'boolean'); - $this->addType(fieldName: 'organisation', type: 'string'); - $this->addType(fieldName: 'metadata', type: 'json'); - - } - - public function getJsonFields(): array - { - return array_keys( - array_filter($this->getFieldTypes(), function ($field) { - return $field === 'json'; - }) - ); - } - - public function hydrate(array $object): self - { - $jsonFields = $this->getJsonFields(); - - foreach($object as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === []) { - $value = []; - } - - $method = 'set'.ucfirst($key); - - try { - $this->$method($value); - } catch (\Exception $exception) { -// ("Error writing $key"); - } - } - - return $this; - } - - public function jsonSerialize(): array - { - $array = [ - 'id' => $this->id, - 'title' => $this->title, - 'summary' => $this->summary, - 'description' => $this->description, - 'image' => $this->image, - 'search' => $this->search, - 'listed' => $this->listed, - 'metadata' => $this->metadata, - 'organisation'=> $this->organisation, - - ]; - - $jsonFields = $this->getJsonFields(); - - foreach ($array as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === null) { - $array[$key] = []; - } - } - - return $array; - } + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $value; + + /** + * Constructor to set the defaults + */ + public function __construct() + { + $this->addType('name', 'string'); + $this->addType('value', 'string'); + } + + /** + * Get the fields that should be serialized to JSON + * + * @return array + */ + public function getJsonFields(): array + { + return [ + 'id', + 'name', + 'value' + ]; + } + + /** + * Hydrate the entity from an array of data + * + * @param array $data + * @return void + */ + public function hydrate(array $data): void + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } + + /** + * Serialize the entity to JSON + * + * @return array + */ + public function jsonSerialize(): array + { + $data = []; + foreach ($this->getJsonFields() as $field) { + $data[$field] = $this->$field; + } + return $data; + } } diff --git a/lib/Db/SettingMapper.php b/lib/Db/SettingMapper.php index 92c1dcd..b748397 100644 --- a/lib/Db/SettingMapper.php +++ b/lib/Db/SettingMapper.php @@ -1,5 +1,13 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; use OCA\LarpingApp\Db\Setting; @@ -8,67 +16,81 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +/** + * @template-extends QBMapper + * @package OCA\LarpingApp\Db + */ class SettingMapper extends QBMapper { - public function __construct(IDBConnection $db) - { - parent::__construct($db, 'larpingapp_settings'); - } - - public function find(int $id): Setting - { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from('larpingapp_settings') - ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) - ); - - return $this->findEntity(query: $qb); - } + /** + * @param IDBConnection $db Database connection + */ + public function __construct(IDBConnection $db) + { + parent::__construct($db, 'larpingapp_settings', Setting::class); + } - public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array - { - $qb = $this->db->getQueryBuilder(); + /** + * Find a setting by ID + * + * @param int $id The setting ID + * @return Setting + * @throws \OCP\AppFramework\Db\DoesNotExistException + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException + */ + public function find(int $id): Setting + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); + return $this->findEntity($qb); + } - $qb->select('*') - ->from('larpingapp_settings') - ->setMaxResults($limit) - ->setFirstResult($offset); + /** + * Find all settings for a user + * + * @param string $userId The user ID + * @return Setting[] + */ + public function findAll(string $userId): array + { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId))); + + return $this->findEntities($qb); + } - foreach($filters as $filter => $value) { - if ($value === 'IS NOT NULL') { - $qb->andWhere($qb->expr()->isNotNull($filter)); - } elseif ($value === 'IS NULL') { - $qb->andWhere($qb->expr()->isNull($filter)); - } else { - $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); - } + /** + * Create a new setting from array data + * + * @param array $data The setting data + * @return Setting + */ + public function createFromArray(array $data): Setting + { + $setting = new Setting(); + foreach ($data as $key => $value) { + $setting->$key = $value; } + return $this->insert($setting); + } - if (!empty($searchConditions)) { - $qb->andWhere('(' . implode(' OR ', $searchConditions) . ')'); - foreach ($searchParams as $param => $value) { - $qb->setParameter($param, $value); - } + /** + * Update a setting from array data + * + * @param int $id The setting ID + * @param array $data The updated setting data + * @return Setting + */ + public function updateFromArray(int $id, array $data): Setting + { + $setting = $this->find($id); + foreach ($data as $key => $value) { + $setting->$key = $value; } - - return $this->findEntities(query: $qb); - } - - public function createFromArray(array $object): Setting - { - $setting = new Setting(); - $setting->hydrate(object: $object); - return $this->insert(entity: $setting); - } - - public function updateFromArray(int $id, array $object): Setting - { - $setting = $this->find($id); - $setting->hydrate($object); - - return $this->update($setting); - } + return $this->update($setting); + } } diff --git a/lib/Db/Skill.php b/lib/Db/Skill.php index 8c025bc..0a7e6e6 100644 --- a/lib/Db/Skill.php +++ b/lib/Db/Skill.php @@ -1,78 +1,84 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; use DateTime; use JsonSerializable; use OCP\AppFramework\Db\Entity; +/** + * @method string getName() + * @method void setName(string $name) + * @method string getDescription() + * @method void setDescription(string $description) + */ class Skill extends Entity implements JsonSerializable { - protected ?string $name = null; - protected ?string $description = null; - protected ?string $effect = null; - protected ?array $effects = null; - protected ?array $requiredSkills = []; - protected ?array $requiredStats = []; - protected ?array $requiredConditions = []; - protected ?array $requiredEffects = []; - protected ?int $requiredScore = null; - - public function __construct() { - $this->addType('name', 'string'); - $this->addType('description', 'string'); - $this->addType('effect', 'string'); - $this->addType('effects', 'json'); - $this->addType('requiredSkills', 'json'); - $this->addType('requiredStats', 'json'); - $this->addType('requiredConditions', 'json'); - $this->addType('requiredEffects', 'json'); - $this->addType('requiredScore', 'integer'); - } - - public function getJsonFields(): array - { - return array_keys( - array_filter($this->getFieldTypes(), function ($field) { - return $field === 'json'; - }) - ); - } - - public function hydrate(array $object): self - { - $jsonFields = $this->getJsonFields(); - - foreach($object as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === []) { - $value = []; - } + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $description; - $method = 'set'.ucfirst($key); + /** + * Constructor to set the defaults + */ + public function __construct() + { + $this->addType('name', 'string'); + $this->addType('description', 'string'); + } - try { - $this->$method($value); - } catch (\Exception $exception) { -// ("Error writing $key"); - } - } + /** + * Get the fields that should be serialized to JSON + * + * @return array + */ + public function getJsonFields(): array + { + return [ + 'id', + 'name', + 'description' + ]; + } - return $this; - } + /** + * Hydrate the entity from an array of data + * + * @param array $data + * @return void + */ + public function hydrate(array $data): void + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } - public function jsonSerialize(): array - { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'description' => $this->description, - 'effect' => $this->effect, - 'effects' => $this->effects, - 'requiredSkills' => $this->requiredSkills, - 'requiredStats' => $this->requiredStats, - 'requiredConditions' => $this->requiredConditions, - 'requiredEffects' => $this->requiredEffects, - 'requiredScore' => $this->requiredScore, - ]; - } + /** + * Serialize the entity to JSON + * + * @return array + */ + public function jsonSerialize(): array + { + $data = []; + foreach ($this->getJsonFields() as $field) { + $data[$field] = $this->$field; + } + return $data; + } } \ No newline at end of file diff --git a/lib/Db/SkillMapper.php b/lib/Db/SkillMapper.php index 20b2f4e..48b696c 100644 --- a/lib/Db/SkillMapper.php +++ b/lib/Db/SkillMapper.php @@ -1,5 +1,20 @@ + * @copyright 2024 Ruben Linde + * @license https://www.gnu.org/licenses/agpl-3.0.html GNU AGPL v3 or later + * @link https://larpingapp.com + * + * @phpversion 8.2 + */ + namespace OCA\LarpingApp\Db; use OCA\LarpingApp\Db\Skill; @@ -8,43 +23,57 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +/** + * Mapper class for Skill entities + * + * @category Database + * @package OCA\LarpingApp\Db + * @author Ruben Linde + * @license https://www.gnu.org/licenses/agpl-3.0.html GNU AGPL v3 or later + * @link https://larpingapp.com + */ class SkillMapper extends QBMapper { - public function __construct(IDBConnection $db) - { - parent::__construct($db, 'larpingapp_skills'); - } + /** + * Constructor for SkillMapper + * + * @param IDBConnection $db Database connection + */ + public function __construct(IDBConnection $db) + { + parent::__construct($db, 'larpingapp_skills', Skill::class); + } - public function find(int $id): Skill - { - $qb = $this->db->getQueryBuilder(); + public function find(int $id): Skill + { + $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from('larpingapp_skills') - ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) - ); + $qb->select('*') + ->from('larpingapp_skills') + ->where( + $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) + ); - return $this->findEntity(query: $qb); - } + return $this->findEntity(query: $qb); + } - public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array - { - $qb = $this->db->getQueryBuilder(); + public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array + { + $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from('larpingapp_skills') - ->setMaxResults($limit) - ->setFirstResult($offset); + $qb->select('*') + ->from('larpingapp_skills') + ->setMaxResults($limit) + ->setFirstResult($offset); foreach($filters as $filter => $value) { - if ($value === 'IS NOT NULL') { - $qb->andWhere($qb->expr()->isNotNull($filter)); - } elseif ($value === 'IS NULL') { - $qb->andWhere($qb->expr()->isNull($filter)); - } else { - $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); - } + if ($value === 'IS NOT NULL') { + $qb->andWhere($qb->expr()->isNotNull($filter)); + } elseif ($value === 'IS NULL') { + $qb->andWhere($qb->expr()->isNull($filter)); + } else { + $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); + } } if (!empty($searchConditions)) { @@ -54,21 +83,21 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters } } - return $this->findEntities(query: $qb); - } + return $this->findEntities(query: $qb); + } - public function createFromArray(array $object): Skill - { - $skill = new Skill(); - $skill->hydrate(object: $object); - return $this->insert(entity: $skill); - } + public function createFromArray(array $object): Skill + { + $skill = new Skill(); + $skill->hydrate(object: $object); + return $this->insert(entity: $skill); + } - public function updateFromArray(int $id, array $object): Skill - { - $skill = $this->find($id); - $skill->hydrate($object); + public function updateFromArray(int $id, array $object): Skill + { + $skill = $this->find($id); + $skill->hydrate($object); - return $this->update($skill); - } + return $this->update($skill); + } } diff --git a/lib/Db/Template.php b/lib/Db/Template.php index fef383c..246ed8f 100644 --- a/lib/Db/Template.php +++ b/lib/Db/Template.php @@ -1,60 +1,83 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + */ + namespace OCA\LarpingApp\Db; -use DateTime; use JsonSerializable; use OCP\AppFramework\Db\Entity; +/** + * @method string getName() + * @method void setName(string $name) + * @method string getDescription() + * @method void setDescription(string $description) + */ class Template extends Entity implements JsonSerializable { - protected ?string $name = null; - protected ?string $description = null; - protected ?string $template = null; - - public function __construct() { - $this->addType('name', 'string'); - $this->addType('description', 'string'); - $this->addType('template', 'string'); - } - - public function getJsonFields(): array - { - return array_keys( - array_filter($this->getFieldTypes(), function ($field) { - return $field === 'json'; - }) - ); - } - - public function hydrate(array $object): self - { - $jsonFields = $this->getJsonFields(); - - foreach($object as $key => $value) { - if (in_array($key, $jsonFields) === true && $value === []) { - $value = []; - } - - $method = 'set'.ucfirst($key); - - try { - $this->$method($value); - } catch (\Exception $exception) { -// ("Error writing $key"); - } - } - - return $this; - } - - public function jsonSerialize(): array - { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'description' => $this->description, - 'template' => $this->template, - ]; - } + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $description; + + /** + * Constructor to set the defaults + */ + public function __construct() + { + $this->addType('name', 'string'); + $this->addType('description', 'string'); + } + + /** + * Get the fields that should be serialized to JSON + * + * @return array + */ + public function getJsonFields(): array + { + return [ + 'id', + 'name', + 'description' + ]; + } + + /** + * Hydrate the entity from an array of data + * + * @param array $data + * @return void + */ + public function hydrate(array $data): void + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } + + /** + * Serialize the entity to JSON + * + * @return array + */ + public function jsonSerialize(): array + { + $data = []; + foreach ($this->getJsonFields() as $field) { + $data[$field] = $this->$field; + } + return $data; + } } \ No newline at end of file diff --git a/lib/Db/TemplateMapper.php b/lib/Db/TemplateMapper.php index ae15588..b72f3d9 100644 --- a/lib/Db/TemplateMapper.php +++ b/lib/Db/TemplateMapper.php @@ -1,5 +1,18 @@ + * @author Ruben Linde + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + */ + namespace OCA\LarpingApp\Db; use OCA\LarpingApp\Db\Template; @@ -8,43 +21,58 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +/** + * @template-extends QBMapper