Skip to content

Release/3 0 0#156

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

Release/3 0 0#156
derpixler wants to merge 9 commits into
masterfrom
release/3-0-0

Conversation

@derpixler

Copy link
Copy Markdown
Contributor

No description provided.

derpixler and others added 9 commits July 20, 2026 09:37
…tories, enums, PHP 8.1+

## Breaking Changes (v2 → v3)

### Architecture
- 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)

### Files removed (~3,900 LOC)
- 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

## What's new (~10,200 LOC)

### Core (src/Core/)
- ZammadClient, RequestHandler, RetryAfterMiddleware, AbstractRepository,
  PaginatedList, Resource, DtoHydrator, Cast, ConnectionConfig,
  RepositoryRegistry, ResponseParser, HttpPageFetcher, traits

### Contracts (src/Core/Contracts/)
- RepositoryInterface, DTOInterface, DeletableInterface, PatchableInterface,
  RequestHandlerInterface, ClientInterface, PageFetcherInterface

### Endpoints — 10 repositories + typed DTOs (src/Endpoints/)
- 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

### Exceptions — typed hierarchy (src/Exceptions/)
- AuthenticationException (401), ForbiddenException (403),
  NotFoundException (404), ValidationException (422, with $errors array),
  RateLimitException (429, auto-retried), ServerErrorException (5xx),
  NetworkException

### Bridge (src/Bridge/)
- LaravelServiceProvider, SymfonyBundle

### Tests — 227 unit tests, 12 integration test classes

### Documentation
- 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.
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