Data model & raw payloads
Every resource object is provider-agnostic: it carries the ProviderName it came from, a normalized ResourceState enum instead of a raw open/opened/OPEN string, and a raw() accessor returning the exact decoded provider payload for any field the object doesn’t model. raw is never serialized into toArray() or toJson():
use RoundlyConsulting\Git\Enums\ResourceState;
$pr = Git::github()->repo('acme/api')->pullRequest(1)->get();
$pr->state; // ResourceState::Open — identical across GitHub/GitLab/Bitbucket
$pr->state === ResourceState::Merged;
$pr->draft; // bool
$pr->provider; // ProviderName::Github
$pr->raw()['mergeable']; // any unmodelled provider field, still reachable
$pr->toArray(); // canonical shape — raw is never serialized
$pr->toJson();State mapping
Pull requests and issues normalize their state the same way on every forge; unmatched values resolve to Unknown rather than throwing, and the original string stays reachable through raw():
| ResourceState | GitHub | GitLab | Bitbucket |
|---|---|---|---|
Open | open | opened | OPEN |
Closed | closed | closed, locked | DECLINED, SUPERSEDED |
Merged | merged | merged | MERGED |
Unknown | anything else | anything else | anything else |
Draft status is its own boolean — PullRequest::$draft, read from the forge’s draft field on all three forges, REST and webhooks alike — rather than a state.
Head commit, visibility and links
PullRequest::$headSha and $headRepository (owner/name of the head branch’s repository — a fork’s, for a pull request from a fork) and Repository::$private, $language and $webUrl are nullable and filled from REST and webhook payloads alike. They come after raw in the constructor, so positional new PullRequest(…) and new Repository(…) calls keep working, and each one is in toArray() and JSON:
| Field | GitHub | GitLab | Bitbucket |
|---|---|---|---|
PullRequest::$headSha | head.sha | sha (REST), last_commit.id (hook) | source.commit.hash — a 12-character short hash |
PullRequest::$headRepository | head.repo.full_name (null for a deleted fork) | null — only a project id is carried | source.repository.full_name |
Repository::$private | private | visibility other than public — internal counts as private; a hook’s visibility_level is read the same way | is_private |
Repository::$language | language | null — not in the project payload | language (empty → null) |
Repository::$webUrl | html_url | web_url | links.html.href |
Enums
Every enum — Feature, ProviderName, ResourceState, MergeMethod, ReviewEvent, DiffSide, CommentTarget, ComparisonStatus, ActivityType, WorkflowStatus and WorkflowConclusion — shares the enums-for-laravel helpers: values(), labels(), options(), validationRule() and case lookups:
use RoundlyConsulting\Git\Enums\ProviderName;
ProviderName::values(); // Collection: github, gitlab, bitbucket
ProviderName::options(); // {value, label, name} options for a select
ProviderName::validationRule(); // "in:github,gitlab,bitbucket"
ProviderName::Github->label(); // "GitHub"
$request->validate(['provider' => ['required', ProviderName::validationRule()]]);Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.