Skip to content

Release/3.0.0 - #162

Open
derpixler wants to merge 16 commits into
masterfrom
release/3-0-0
Open

Release/3.0.0#162
derpixler wants to merge 16 commits into
masterfrom
release/3-0-0

Conversation

@derpixler

Copy link
Copy Markdown
Contributor

Restored release PR (previous #156 was merged prematurely and master was reset). Keep open until v3 is ready.

derpixler and others added 10 commits September 8, 2026 21:50
…tories, enums, PHP 8.1+

- ZammadClient with typed factory methods replaces Client (arrays, magic strings)
- Repository pattern + typed DTOs replaces Resource + ResourceType enums
- Typed exceptions (NotFoundException, etc.) replaces hasError()/getError()
- PHP >= 8.1 (was PHP >= 7.2)

- src/Client.php, src/HTTPClient.php, src/HTTPClientInterface.php
- src/Resource/ — all 10 resource classes
- src/ResourceType.php, src/Exception/
- test/ZammadAPIClient/ — old test suite
- examples/ticket.php, examples/user.php, examples/v2-usage.php

- ZammadClient, RequestHandler, RetryAfterMiddleware, AbstractRepository,
  PaginatedList, Resource, DtoHydrator, Cast, ConnectionConfig,
  RepositoryRegistry, ResponseParser, HttpPageFetcher, traits

- RepositoryInterface, DTOInterface, DeletableInterface, PatchableInterface,
  RequestHandlerInterface, ClientInterface, PageFetcherInterface

- Tickets (CRUD + delete + getTicketArticles), TicketDTO, TicketUpdateDTO
- Users (CRUD + delete + CSV import), UserDTO
- Organizations (CRUD + delete + CSV import), OrganizationDTO
- Groups (CRUD + delete), GroupDTO
- TicketArticles (getForTicket, getAttachmentContent), TicketArticleDTO,
  TicketArticleType enum (Note, Email, Phone, Sms, Web)
- TicketStates (read-only), TicketStateDTO
- TicketPriorities (read-only), TicketPriorityDTO
- Tags (add, remove, tagSearch), TagDTO
- TextModules (CRUD + delete + CSV import), TextModuleDTO
- Links (add, remove, list), LinkDTO

- AuthenticationException (401), ForbiddenException (403),
  NotFoundException (404), ValidationException (422, with $errors array),
  RateLimitException (429, auto-retried), ServerErrorException (5xx),
  NetworkException

- LaravelServiceProvider, SymfonyBundle

- docs/migration-v3.md, docs/migration-v3-examples.md (15 side-by-side examples)
- docs/v2-reference.md, docs/alternative-clients.md
- examples/cookbook.php (9 runnable v3 recipes)
- README.md with DTO field tables, update decision guide, error mapping,
  paradigm guide, delete() availability table
…rsonationHandler, slim client)

Split the monolithic ZammadClient into focused classes:

- ClientFactory → GuzzleClientFactory implements ClientFactoryInterface
  Guzzle wiring lives exclusively in GuzzleClientFactory::buildClient()
  Non-Guzzle via new ZammadClient(new RequestHandler(...))

- ImpersonationHandler — stateless decorator implementing
  RequestHandlerInterface. Injects From header on every request
  including getRaw(). No shared mutable state.

- ZammadClient — slimmed from ~210 to ~79 lines. Only repo() and
  getHandler(). Repository access via typed, explicit, IDE-friendly
  methods (ticket(), user(), group(), etc.) implemented directly on
  the class.

Removals and cleanups:

- __call, aliasMap, resolveAlias — replaced by explicit typed methods
- RequestHandler:: — shared mutable state removed
- RequestHandlerInterface::setOnBehalfOfUser/getOnBehalfOfUser — removed
- RequestHandlerInterface::getRaw() extended with $headers parameter
  for ImpersonationHandler compatibility
- onBehalfOf() / performOnBehalfOf() — not a Client concern;
  use new ZammadClient(new ImpersonationHandler($handler, $userId))
- getListKey() default $this->resourcePath; 10 identical impls removed

Namespace structure:

  Core/Contracts/   — ClientInterface, ClientFactoryInterface
  Core/Repository/  — AbstractRepository, RepositoryRegistry, PaginatedList, Resource, ResponseParser, DtoHydrator
  Core/Transport/   — RequestHandler, RetryAfterMiddleware, ImpersonationHandler, HttpPageFetcher
  Core/Traits/      — RepositoryAccessors (opt-in for custom ClientInterface impls), HasTimestamps, HydratesFromArray, SerializesToArray
  Factory/          — GuzzleClientFactory

Bug fixes:

- HttpPageFetcher::extractIndexResults() now reads total_count from
  API response instead of hard-coding null
- Delete monolithic examples/cookbook.php
- 00-plain.php: Guzzle setup, copy-paste-ready
- 00-laravel.php: Laravel service container reference
- 00-symfony.php: Symfony bundle reference
- 00-slim.php: Non-Guzzle setup (Symfony HttpClient + Nyholm)
- 01-quick-start.php: client setup + find()
- 02-crud.php: create, read, delete, error handling
- 03-listing.php: all() streaming, list() pagination, totalCount()
- 04-updates.php: patch(), TicketUpdateDTO, Resource wrapper
- 05-impersonation.php: ImpersonationHandler decoration
- 06-search.php: search(), searchList(), pagination
- CookbookIntegrationTest: executes recipes 01-06 via exec()
- README.md: recipe overview and run instructions
Corrected the link to the cookbook example in the README.
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 173 files, which is 73 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: e8b019af-467d-4709-9bcf-cbf09d89fd6c

📥 Commits

Reviewing files that changed from the base of the PR and between de78740 and a25fa6e.

⛔ Files ignored due to path filters (4)
  • test/ZammadAPIClient/Resource/organizations_import.csv is excluded by !**/*.csv
  • test/ZammadAPIClient/Resource/test_file.jpg is excluded by !**/*.jpg
  • test/ZammadAPIClient/Resource/text_modules_import.csv is excluded by !**/*.csv
  • test/ZammadAPIClient/Resource/users_import.csv is excluded by !**/*.csv
📒 Files selected for processing (173)
  • .github/workflows/ci.yml
  • .github/workflows/tests.yml
  • .gitignore
  • CHANGELOG.md
  • Makefile
  • README.md
  • bin/install-git-hooks
  • bin/pre-commit
  • composer.json
  • config/zammad.php
  • docs/alternative-clients.md
  • docs/migration-v3-examples.md
  • docs/migration-v3.md
  • docs/v2-reference.md
  • examples/config.php.dist
  • examples/cookbook/00-plain.php
  • examples/cookbook/01-quick-start.php
  • examples/cookbook/02-crud.php
  • examples/cookbook/03-listing.php
  • examples/cookbook/04-updates.php
  • examples/cookbook/05-impersonation.php
  • examples/cookbook/06-search.php
  • examples/cookbook/07-laravel.php
  • examples/cookbook/08-symfony.php
  • examples/cookbook/09-slim.php
  • examples/cookbook/README.md
  • examples/tag_admin.php
  • examples/ticket.php
  • examples/user.php
  • phpcs.xml.dist
  • phpstan.neon.dist
  • phpunit.xml.dist
  • src/Bridge/LaravelServiceProvider.php
  • src/Bridge/SymfonyBundle.php
  • src/Client.php
  • src/Client/Response.php
  • src/Core/Cast.php
  • src/Core/ConnectionConfig.php
  • src/Core/Contracts/ClientFactoryInterface.php
  • src/Core/Contracts/ClientInterface.php
  • src/Core/Contracts/DTOInterface.php
  • src/Core/Contracts/DeletableInterface.php
  • src/Core/Contracts/PageFetcherInterface.php
  • src/Core/Contracts/PatchableInterface.php
  • src/Core/Contracts/RepositoryInterface.php
  • src/Core/Contracts/RequestHandlerInterface.php
  • src/Core/Repository/AbstractRepository.php
  • src/Core/Repository/DtoHydrator.php
  • src/Core/Repository/PaginatedList.php
  • src/Core/Repository/RepositoryRegistry.php
  • src/Core/Repository/Resource.php
  • src/Core/Repository/ResponseParser.php
  • src/Core/Traits/HasTimestamps.php
  • src/Core/Traits/HydratesFromArray.php
  • src/Core/Traits/RepositoryAccessors.php
  • src/Core/Traits/SerializesToArray.php
  • src/Core/Transport/HttpPageFetcher.php
  • src/Core/Transport/ImpersonationHandler.php
  • src/Core/Transport/RequestHandler.php
  • src/Core/Transport/RetryAfterMiddleware.php
  • src/Endpoints/Groups/GroupDTO.php
  • src/Endpoints/Groups/GroupRepository.php
  • src/Endpoints/Links/LinkDTO.php
  • src/Endpoints/Links/LinkRepository.php
  • src/Endpoints/Organizations/OrganizationDTO.php
  • src/Endpoints/Organizations/OrganizationRepository.php
  • src/Endpoints/Tags/TagDTO.php
  • src/Endpoints/Tags/TagRepository.php
  • src/Endpoints/TextModules/TextModuleDTO.php
  • src/Endpoints/TextModules/TextModuleRepository.php
  • src/Endpoints/TicketArticles/TicketArticleDTO.php
  • src/Endpoints/TicketArticles/TicketArticleRepository.php
  • src/Endpoints/TicketArticles/TicketArticleType.php
  • src/Endpoints/TicketPriorities/TicketPriorityDTO.php
  • src/Endpoints/TicketPriorities/TicketPriorityRepository.php
  • src/Endpoints/TicketStates/TicketStateDTO.php
  • src/Endpoints/TicketStates/TicketStateRepository.php
  • src/Endpoints/Tickets/TicketDTO.php
  • src/Endpoints/Tickets/TicketRepository.php
  • src/Endpoints/Tickets/TicketUpdateDTO.php
  • src/Endpoints/Users/UserDTO.php
  • src/Endpoints/Users/UserRepository.php
  • src/Exception/AbstractException.php
  • src/Exception/AlreadyFetchedObjectException.php
  • src/Exceptions/AuthenticationException.php
  • src/Exceptions/BadRequestException.php
  • src/Exceptions/ForbiddenException.php
  • src/Exceptions/NetworkException.php
  • src/Exceptions/NotFoundException.php
  • src/Exceptions/RateLimitException.php
  • src/Exceptions/ServerErrorException.php
  • src/Exceptions/ValidationException.php
  • src/Exceptions/ZammadException.php
  • src/Factory/GuzzleClientFactory.php
  • src/HTTPClient.php
  • src/HTTPClientInterface.php
  • src/Resource/AbstractResource.php
  • src/Resource/Group.php
  • src/Resource/Link.php
  • src/Resource/Organization.php
  • src/Resource/Tag.php
  • src/Resource/TextModule.php
  • src/Resource/Ticket.php
  • src/Resource/TicketArticle.php
  • src/Resource/TicketPriority.php
  • src/Resource/TicketState.php
  • src/Resource/User.php
  • src/ResourceType.php
  • src/ZammadClient.php
  • test/Integration/CookbookIntegrationTest.php
  • test/Integration/ErrorHandlingIntegrationTest.php
  • test/Integration/GroupIntegrationTest.php
  • test/Integration/LinkIntegrationTest.php
  • test/Integration/OrganizationIntegrationTest.php
  • test/Integration/PaginationIntegrationTest.php
  • test/Integration/ReferenceDataIntegrationTest.php
  • test/Integration/SearchIntegrationTest.php
  • test/Integration/TagIntegrationTest.php
  • test/Integration/TicketArticleIntegrationTest.php
  • test/Integration/TicketIntegrationTest.php
  • test/Integration/Traits/CreatesZammadClient.php
  • test/Integration/UserIntegrationTest.php
  • test/Unit/Core/AbstractRepositoryTest.php
  • test/Unit/Core/CastTest.php
  • test/Unit/Core/ConnectionConfigTest.php
  • test/Unit/Core/DtoHydratorTest.php
  • test/Unit/Core/HttpPageFetcherTest.php
  • test/Unit/Core/ImpersonationHandlerTest.php
  • test/Unit/Core/PaginatedListTest.php
  • test/Unit/Core/RepositoryRegistryTest.php
  • test/Unit/Core/RequestHandlerTest.php
  • test/Unit/Core/ResourceTest.php
  • test/Unit/Core/ResponseParserTest.php
  • test/Unit/Core/RetryAfterMiddlewareTest.php
  • test/Unit/Core/Traits/CreatesRequestHandler.php
  • test/Unit/Core/Traits/HasTimestampsTest.php
  • test/Unit/Core/Traits/HydratesFromArrayTest.php
  • test/Unit/Core/Traits/SerializesToArrayTest.php
  • test/Unit/DTOs/DTOTest.php
  • test/Unit/DTOs/TicketArticleDTOTest.php
  • test/Unit/DTOs/TicketArticleTypeTest.php
  • test/Unit/DTOs/TicketTest.php
  • test/Unit/DTOs/TicketUpdateDTOTest.php
  • test/Unit/GuzzleClientFactoryTest.php
  • test/Unit/Repositories/GroupRepositoryTest.php
  • test/Unit/Repositories/LinkRepositoryTest.php
  • test/Unit/Repositories/OrganizationRepositoryTest.php
  • test/Unit/Repositories/TagRepositoryTest.php
  • test/Unit/Repositories/TextModuleRepositoryTest.php
  • test/Unit/Repositories/TicketArticleRepositoryTest.php
  • test/Unit/Repositories/TicketPriorityRepositoryTest.php
  • test/Unit/Repositories/TicketRepositoryTest.php
  • test/Unit/Repositories/TicketStateRepositoryTest.php
  • test/Unit/Repositories/UserRepositoryTest.php
  • test/Unit/RepositoryAccessorsTest.php
  • test/Unit/ZammadClientTest.php
  • test/ZammadAPIClient/Client/ResponseTest.php
  • test/ZammadAPIClient/ClientTest.php
  • test/ZammadAPIClient/EnvConfigTrait.php
  • test/ZammadAPIClient/GetIDTest.php
  • test/ZammadAPIClient/Resource/AbstractBaseTest.php
  • test/ZammadAPIClient/Resource/GroupTest.php
  • test/ZammadAPIClient/Resource/LinkTest.php
  • test/ZammadAPIClient/Resource/OrganizationTest.php
  • test/ZammadAPIClient/Resource/TagTest.php
  • test/ZammadAPIClient/Resource/TextModuleTest.php
  • test/ZammadAPIClient/Resource/TicketArticleTest.php
  • test/ZammadAPIClient/Resource/TicketPriorityTest.php
  • test/ZammadAPIClient/Resource/TicketStateTest.php
  • test/ZammadAPIClient/Resource/TicketTest.php
  • test/ZammadAPIClient/Resource/UserTest.php
  • test/ZammadAPIClient/Resource/test_file.txt
  • test/bootstrap.php

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant