Skip to content
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ba89b89
Add ApplicationSettings bounded context for issue #67
claude Nov 21, 2025
73d9662
Refactor ApplicationSettings for multi-scope support (issue #67)
claude Nov 21, 2025
52211f9
Refactor ApplicationSettings: Add tracking fields and event system (i…
claude Nov 21, 2025
e76e82c
Add soft-delete support and OnApplicationDelete UseCase (issue #67)
claude Nov 21, 2025
4cf3494
Fix: Add backward compatibility methods to Repository
claude Nov 21, 2025
704c22d
Add getEvents() method to AggregateRoot for testing
claude Nov 21, 2025
ea01d31
Align event handling with existing patterns
claude Nov 21, 2025
4f25ed8
Fix linter errors and move documentation
claude Nov 21, 2025
c656e51
Refactor ApplicationSettings: cleanup and improve architecture
claude Nov 21, 2025
4969132
Final refactoring: simplify Delete UseCase and add comprehensive tests
claude Nov 21, 2025
02e878e
Apply Rector and PHP-CS-Fixer code improvements
claude Nov 21, 2025
fc72254
Fix PHPStan errors in InstallSettingsTest
claude Nov 21, 2025
93bf656
Remove redundant repository methods and simplify API
claude Nov 21, 2025
f855474
Refactor ApplicationSettings: simplify API and add SettingsFetcher se…
claude Nov 21, 2025
20cffda
Refactor SettingsFetcher: rename getSetting to getItem and add except…
claude Nov 21, 2025
6450f18
Add findAllForInstallationByKey method to optimize SettingsFetcher
claude Nov 21, 2025
b85a052
Refactor ApplicationSettings: rename entity, separate Create/Update, …
claude Nov 21, 2025
92d8b1d
Enhance SettingsFetcher: add logger, rename method, add deserializati…
claude Nov 21, 2025
09ff03d
Apply Rector automatic code improvements
claude Nov 21, 2025
88ea683
Improve exception handling and serialization in ApplicationSettings
claude Nov 21, 2025
9e6ae22
Refactor ApplicationSettingsItem to generate UUID internally
claude Nov 21, 2025
b858438
Update Makefile to use Docker for composer license checker
mesilov Nov 23, 2025
35e7f52
Add TODO comments for SDK interface gaps and refactor Bitrix24 accoun…
mesilov Nov 23, 2025
df4a879
Refactor test variable names for clarity and update lambda typing in …
mesilov Nov 23, 2025
c665594
Refactor TODO comment formatting and lambda syntax in account filtering
mesilov Nov 23, 2025
2399fde
Update license-check workflow to use `composer-license-checker` directly
mesilov Nov 23, 2025
38bbbc3
Translate ApplicationSettings documentation to English, update code e…
mesilov Nov 23, 2025
8d3c5e6
Refactor repository tests: add contract tests for consistency, move i…
mesilov Nov 23, 2025
4e7521f
Update README.md: adjust formatting, add new ApplicationSettings sect…
mesilov Nov 23, 2025
a06a148
Merge remote-tracking branch 'origin/dev' into claude/fix-bitrix24-is…
mesilov Nov 23, 2025
ce211c1
Add PHP 8.4 support in composer requirements and GitHub workflows
mesilov Nov 23, 2025
f187837
Remove outdated TODO comment and redundant PHPStan ignore directive i…
mesilov Nov 23, 2025
6346c29
Refactor ApplicationSettings tests: introduce `flushChanges` method f…
mesilov Nov 23, 2025
50f36ab
Refactor repository methods: add `#[\Override]` annotations and remov…
mesilov Nov 23, 2025
1e7a1e1
Update CLAUDE.md: add steps for running linters and tests after each …
mesilov Nov 23, 2025
218b3f8
Refactor ApplicationSettingsItemRepository: remove EntityRepository i…
mesilov Nov 23, 2025
8ba250e
Replace custom exceptions with SDK standard exceptions for consistenc…
mesilov Nov 24, 2025
aa39517
Rename `InstallSettings` to `DefaultSettingsInstaller` for improved s…
mesilov Nov 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/license-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
run: "composer update --no-interaction --no-progress --no-suggest"

- name: "composer-license-checker"
run: "make lint-allowed-licenses"
run: "php vendor/bin/composer-license-checker"

- name: "is allowed licenses check succeeded"
if: ${{ success() }}
Expand Down
93 changes: 93 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,96 @@



## 0.1.2
### Added
- **ApplicationSettings bounded context** for application configuration management — [#67](https://github.com/mesilov/bitrix24-php-lib/issues/67)
- Full CRUD functionality with CQRS pattern (Create, Update, Delete use cases)
- Multi-scope support: Global, Departmental, and Personal settings with cascading resolution
- **SettingsFetcher service** with automatic deserialization support
- Cascading resolution logic (Personal → Departmental → Global)
- JSON deserialization to objects using Symfony Serializer
- Comprehensive logging with LoggerInterface
- **InstallSettings service** for bulk creation of default settings
- Soft-delete support with `ApplicationSettingStatus` enum (Active/Deleted)
- Event system with `ApplicationSettingsItemChangedEvent` for change tracking
- CLI command `app:settings:list` for viewing settings with scope filtering
- InMemory repository implementation for fast unit testing
- Unique constraint on (installation_id, key, user_id, department_id)
- Tracking fields: `changedByBitrix24UserId`, `isRequired`
- Database schema updates
- Table `application_settings` with UUID v7 IDs
- Scope fields: `b24_user_id`, `b24_department_id`
- Status field with index for query optimization
- Timestamp tracking: `created_at_utc`, `updated_at_utc`
- Comprehensive test coverage
- Unit tests for entity validation and business logic
- Functional tests for repository operations and use case handlers
- Tests for all scope types and soft-delete behavior

### Changed
- **Refactored ApplicationSettings entity naming**
- Renamed `ApplicationSetting` → `ApplicationSettingsItem`
- Renamed all interfaces and events accordingly
- Updated table name from `application_setting` → `application_settings`
- **Separated Create/Update use cases**
- Create UseCase now only creates new settings (throws exception if exists)
- Update UseCase for modifying existing settings (throws exception if not found)
- Update automatically emits `ApplicationSettingsItemChangedEvent`
- **Simplified repository API**
- Removed 6 redundant methods, kept only `findAllForInstallation()`
- Renamed `findAll()` → `findAllForInstallationByKey()` to avoid conflicts
- All find methods now filter by `status=Active` by default
- Added optimized `findAllForInstallationByKey()` method
- **Enhanced SettingsFetcher**
- Renamed `getSetting()` → `getItem()`
- Renamed `getSettingValue()` → `getValue()`
- Added automatic deserialization with type-safe generics
- Non-nullable return types with exception throwing
- **ApplicationSettingsItem improvements**
- UUID v7 generation moved inside entity constructor
- Key validation: only lowercase latin letters and dots
- Scope methods: `isGlobal()`, `isPersonal()`, `isDepartmental()`
- `updateValue()` method emits change events
- **Makefile improvements**
- Updated to use Docker for `composer-license-checker`
- Aligns with other linting and analysis workflows
- **Code quality improvements**
- Applied Rector automatic refactoring (arrow functions, type hints, naming)
- Added `#[\Override]` attributes to overridden methods
- Applied PHP-CS-Fixer formatting consistently
- Added symfony/property-access dependency for ObjectNormalizer
- **Documentation improvements**
- Translated ApplicationSettings documentation to English
- Updated all code examples to reflect current codebase
- Corrected exception class names (SettingsItemAlreadyExistsException, SettingsItemNotFoundException)
- Improved best practices and security sections

### Fixed
- **PHPStan level 5 errors related to SDK interface compatibility** — [#67](https://github.com/mesilov/bitrix24-php-lib/issues/67)
- Removed invalid `#[\Override]` attributes from extension methods in `ApplicationInstallationRepository`
- Fixed `findByMemberId()` call with incorrect parameter count in `OnAppInstall\Handler`
- Added `@phpstan-ignore-next-line` comments for methods not yet available in SDK interface
- Added TODO comments to track SDK interface extension requirements
- **Doctrine XML mapping**
- Fixed `enumType` → `enum-type` syntax for Doctrine ORM 3 compatibility
- **Repository method naming conflicts**
- Renamed methods to avoid conflicts with EntityRepository base class
- **Exception handling**
- Added `SettingsItemAlreadyExistsException` for Create use case
- Added `SettingsItemNotFoundException` for Get/Delete operations
- Updated all handlers to throw specific exceptions

### Removed
- **Get UseCase** - replaced with `SettingsFetcher` service (UseCases now only for data modification)
- **Redundant repository methods**
- `findGlobalByKey()`, `findPersonalByKey()`, `findDepartmentalByKey()`
- `findAllGlobal()`, `findAllPersonal()`, `findAllDepartmental()`
- `deleteByApplicationInstallationId()`
- `softDeleteByApplicationInstallationId()`
- **Hard delete from Delete UseCase** - replaced with soft-delete pattern
- **Entity getStatus() method** - use `isActive()` instead for better encapsulation
- **Static getRecommendedDefaults()** - developers should define their own defaults

## 0.1.1
### Added
- Change php version requirements — [#44](https://github.com/mesilov/bitrix24-php-lib/pull/44)
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ composer:

# check allowed licenses
lint-allowed-licenses:
vendor/bin/composer-license-checker
docker-compose run --rm php-cli php vendor/bin/composer-license-checker
# linters
lint-phpstan:
docker-compose run --rm php-cli php vendor/bin/phpstan analyse --memory-limit 2G
Expand Down
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,18 @@
"symfony/dotenv": "^7"
},
"require-dev": {
"lendable/composer-license-checker": "^1.2",
"doctrine/migrations": "^3",
"fakerphp/faker": "^1",
"friendsofphp/php-cs-fixer": "^3.64",
"lendable/composer-license-checker": "^1.2",
"monolog/monolog": "^3",
"fakerphp/faker": "^1",
"phpstan/phpstan": "^1",
"phpunit/phpunit": "^11",
"doctrine/migrations": "^3",
"psalm/phar": "^5",
"rector/rector": "^1",
"roave/security-advisories": "dev-master",
"symfony/debug-bundle": "^7",
"symfony/property-access": "^7.3",
"symfony/stopwatch": "^7"
},
"autoload": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xs="https://www.w3.org/2001/XMLSchema"
xmlns:orm="https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Bitrix24\Lib\ApplicationSettings\Entity\ApplicationSettingsItem"
table="application_settings">
<id name="id" type="uuid" column="id">

</id>

<field name="applicationInstallationId" type="uuid" column="application_installation_id" nullable="false"/>

<field name="key" type="string" column="key" length="255" nullable="false"/>

<field name="value" type="text" column="value" nullable="false"/>

<field name="b24UserId" type="integer" column="b24_user_id" nullable="true"/>

<field name="b24DepartmentId" type="integer" column="b24_department_id" nullable="true"/>

<field name="changedByBitrix24UserId" type="integer" column="changed_by_b24_user_id" nullable="true"/>

<field name="isRequired" type="boolean" column="is_required" nullable="false"/>

<field name="status" enum-type="string" column="status" nullable="false"/>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Map status enum using enum class, not string literal

The XML mapping declares status with enum-type="string", but ApplicationSettingsItem::$status is a typed ApplicationSettingStatus enum (see src/ApplicationSettings/Entity/ApplicationSettingsItem.php:30‑39). Doctrine expects enum-type to point at the enum FQCN; using the literal string will hydrate database values as plain strings and will either raise a mapping/type error or fail assigning to the enum-typed property whenever settings are loaded from the database. Any read of application settings will break until the mapping references ApplicationSettingStatus (or a custom type) instead of string.

Useful? React with 👍 / 👎.


<field name="createdAt" type="carbon_immutable" column="created_at_utc" precision="3" nullable="false"/>

<field name="updatedAt" type="carbon_immutable" column="updated_at_utc" precision="3" nullable="false"/>

<unique-constraints>
<unique-constraint columns="application_installation_id,key,b24_user_id,b24_department_id" name="unique_app_setting_scope"/>
</unique-constraints>
Comment on lines +30 to +32

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Soft-deleted settings still block re-create via unique key

The unique constraint on (application_installation_id,key,b24_user_id,b24_department_id) applies regardless of status, but the Delete handler only soft-deletes by setting status to Deleted (src/ApplicationSettings/UseCase/Delete/Handler.php:53‑55). After a setting is deleted, the row remains and the unique constraint still matches, so a subsequent Create for the same key/scope will hit a database unique-constraint violation even though code only checks active records. Include status in the unique key or hard-delete soft-deleted rows to allow recreating settings after deletion.

Useful? React with 👍 / 👎.


<indexes>
<index name="idx_application_installation_id" columns="application_installation_id"/>
<index name="idx_b24_user_id" columns="b24_user_id"/>
<index name="idx_b24_department_id" columns="b24_department_id"/>
<index name="idx_key" columns="key"/>
<index name="idx_status" columns="status"/>
</indexes>
</entity>
</doctrine-mapping>
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,12 @@ public function findByExternalId(string $externalId): array
/**
* Find application installation by application token.
*
* TODO: Create issue in b24-php-sdk to add this method to ApplicationInstallationRepositoryInterface
*
* @param non-empty-string $applicationToken
*
* @throws InvalidArgumentException
*/
#[\Override]
public function findByApplicationToken(string $applicationToken): ?ApplicationInstallationInterface
{
if ('' === trim($applicationToken)) {
Expand All @@ -132,7 +133,9 @@ public function findByApplicationToken(string $applicationToken): ?ApplicationIn
;
}

#[\Override]
/**
* TODO: Create issue in b24-php-sdk to add this method to ApplicationInstallationRepositoryInterface.
*/
public function findByBitrix24AccountMemberId(string $memberId): ?ApplicationInstallationInterface
{
if ('' === trim($memberId)) {
Expand Down
1 change: 1 addition & 0 deletions src/ApplicationInstallations/UseCase/Install/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
]);

/** @var null|AggregateRootEventsEmitterInterface|ApplicationInstallationInterface $activeInstallation */
/** @phpstan-ignore-next-line Method exists in implementation but not in SDK interface - TODO: see ApplicationInstallationRepository */
$activeInstallation = $this->applicationInstallationRepository->findByBitrix24AccountMemberId($command->memberId);

Check failure on line 48 in src/ApplicationInstallations/UseCase/Install/Handler.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.3, highest, ubuntu-latest)

No error to ignore is reported on line 48.

if (null !== $activeInstallation) {
$entitiesToFlush = [];
Expand Down
16 changes: 11 additions & 5 deletions src/ApplicationInstallations/UseCase/OnAppInstall/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
]);

/** @var null|AggregateRootEventsEmitterInterface|ApplicationInstallationInterface $applicationInstallation */
/** @phpstan-ignore-next-line Method exists in implementation but not in SDK interface - TODO: see ApplicationInstallationRepository */
$applicationInstallation = $this->applicationInstallationRepository->findByBitrix24AccountMemberId($command->memberId);

Check failure on line 43 in src/ApplicationInstallations/UseCase/OnAppInstall/Handler.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.3, highest, ubuntu-latest)

No error to ignore is reported on line 43.

$applicationStatus = new ApplicationStatus($command->applicationStatus);

Expand Down Expand Up @@ -67,18 +68,23 @@
$memberId,
Bitrix24AccountStatus::active,
null,
null,
true
null
);

// Filter for master accounts only
$masterAccounts = array_filter(
$bitrix24Accounts,
fn (Bitrix24AccountInterface $bitrix24Account): bool => $bitrix24Account->isMasterAccount()
);

if ([] === $bitrix24Accounts) {
if ([] === $masterAccounts) {
throw new Bitrix24AccountNotFoundException('Bitrix24 account not found for member ID '.$memberId);
}

if (1 !== count($bitrix24Accounts)) {
if (1 !== count($masterAccounts)) {
throw new MultipleBitrix24AccountsFoundException('Multiple Bitrix24 accounts found for member ID '.$memberId);
}

return reset($bitrix24Accounts);
return reset($masterAccounts);
}
}
1 change: 1 addition & 0 deletions src/ApplicationInstallations/UseCase/Uninstall/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
]);

/** @var AggregateRootEventsEmitterInterface|ApplicationInstallationInterface $activeInstallation */
/** @phpstan-ignore-next-line Method exists in implementation but not in SDK interface - TODO: see ApplicationInstallationRepository */
$activeInstallation = $this->applicationInstallationRepository->findByApplicationToken($command->applicationToken);

Check failure on line 47 in src/ApplicationInstallations/UseCase/Uninstall/Handler.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.3, highest, ubuntu-latest)

No error to ignore is reported on line 47.

if (null !== $activeInstallation) {
$this->logger->info(
Expand Down
Loading
Loading