refactor: split ZammadClient into three concerns (ClientFactory, Impe…#154
Merged
derpixler merged 2 commits intoJul 21, 2026
Merged
Conversation
derpixler
force-pushed
the
epix-79/refactor-zammad-client-class
branch
13 times, most recently
from
July 20, 2026 15:24
135145e to
e4f33d7
Compare
…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
derpixler
force-pushed
the
epix-79/refactor-zammad-client-class
branch
2 times, most recently
from
July 21, 2026 06:15
9831fd0 to
0e413e6
Compare
- 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
derpixler
force-pushed
the
epix-79/refactor-zammad-client-class
branch
from
July 21, 2026 06:28
0e413e6 to
a09735a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
refactor: split ZammadClient into three concerns (ClientFactory, ImpersonationHandler, slim client)
Split the monolithic ZammadClient into focused classes, each with a single responsibility:
ClientFactory — the only place that knows about Guzzle. Static factory methods (withToken, withBasicAuth, withOAuth2, withClient) that produce ZammadClient instances. PSR-18-agnostic withClient() path preserved.
ImpersonationHandler — stateless decorator implementing RequestHandlerInterface. Injects From header on every request including getRaw(). No shared mutable state; no footgun.
ZammadClient — slimmed from ~210 to ~75 lines. Only repo(), getHandler(), onBehalfOf() and performOnBehalfOf(). The latter creates a scoped clone (new self()) so repos within the scope run impersonated while the outer client is never touched.
Removals and cleanups:
Discoverability: ZammadClient docblock references ClientFactory with code example. ClientFactory lives in the same namespace for IDE discovery.
This is a relocation refactoring — Guzzle decoupling and alias duplication are fixed; the repo() service locator question is deferred to phase 2.