NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky

Routy, form requesty, kontroléry, resources podľa roly, filtre zoznamu cez query-builder, policy, limity požiadaviek a endpointy pre hostí — vypnuté, kým ich nezapnete:

TICKETS_ROUTES=on
  • Prefix tickets.routes.prefix (api), názvy rout tickets.*, middleware tickets.routes.middleware (['api', 'auth', 'throttle:tickets-api']).
  • {ticket} je číslo tiketu ([A-Z][A-Z0-9]{0,9}-[0-9]+). Kontroléry hľadajú tikety cez miesto pre modely, takže API funguje bez SubstituteBindings aj s vymeneným modelom tiketu. Soft-deleted tiket je 404, okrem obnovenia.
  • Ak chcete zmeniť routu, publikujte routes/tickets.php (--tag=tickets-routes); publikovaná kópia natrvalo nahradí routy balíka.

So Sanctum

// config/tickets.php
'routes' => [
    'enabled' => env('TICKETS_ROUTES') ?: 'off',
    'middleware' => ['api', 'auth:sanctum', 'throttle:tickets-api'],
    // …
],

Rovnako funguje akýkoľvek guard (auth:api, …). Bez middleware auth by každý endpoint odpovedal anonymným volajúcim — tickets:doctor upozorní.

Limity požiadaviek

Definujú sa len pri zapnutých routách a platia, len kým je ich middleware throttle: v zozname: tickets-api povolí tickets.routes.rate_limit (120) požiadaviek za minútu na prihláseného používateľa (inak na IP); tickets-guest povolí tickets.routes.guest.rate_limit (30) za minútu na IP a tiket.

Routy

Metóda a cestaNázovOprávneniePoznámky
GET /ticketsindexviewAnyObmedzené cez visibleTo; filtre, radenie a stránkovanie nižšie.
POST /ticketsstorecreate201
POST /tickets/bulkbulkpre každý tiketHromadné akcie, nižšie.
GET /tickets/{ticket}showviewDetailné: plus hodnoty polí a prílohy.
PATCH /tickets/{ticket}updateview + update (s dotknutými atribútmi)—
DELETE /tickets/{ticket}destroyview + deleteSoft; 204.
POST /tickets/{ticket}/restorerestoreview + restore—
GET /tickets/{ticket}/transitionstransitions.indexviewPrechody, ktoré tento používateľ smie práve spustiť: name, label, to, to_label, requires_reason, payload.
POST /tickets/{ticket}/transitions/{transition}transitions.storeview + transition{reason?, payload?}
PUT /tickets/{ticket}/assigneeassignee.updateview + assign{assignee: {type, id}} | {claim: true}
DELETE /tickets/{ticket}/assigneeassignee.destroyview + unassign—
GET /tickets/{ticket}/commentscomments.indexview (+ viewInternal pri ?internal=1)—
POST /tickets/{ticket}/repliesreplies.storereply (+ transition){body, channel?, attachments[]?, transition?, reason?, payload?}; 201 — aj keď odpoveď zadrží moderácia hostiteľa; vtedy sa žiadny prechod neuplatní.
POST /tickets/{ticket}/notesnotes.storeview + note{body, channel?, attachments[]?}; 201
PATCH /tickets/{ticket}/comments/{comment}comments.updateview + updateComment{body}; komentár musí patriť tomuto tiketu; zaznamená sa ako comment_edited.
DELETE /tickets/{ticket}/comments/{comment}comments.destroyview + deleteComment204; zaznamená sa ako comment_deleted.
GET /tickets/{ticket}/fieldsfields.indexviewFiltrované podľa roly.
PUT /tickets/{ticket}/fieldsfields.updateview + update (fields){fields: {key: value}}; null hodnotu zruší.
GET /tickets/{ticket}/watcherswatchers.indexview + manageWatchersLen agenti.
POST /tickets/{ticket}/watcherswatchers.storeview + manageWatchers{watcher: {type, id}}; 201
DELETE /tickets/{ticket}/watchers/{alias:key}watchers.destroyview + manageWatchers204
POST / DELETE /tickets/{ticket}/watchwatch / unwatchview + manageWatchers (pre seba)204
PUT /tickets/{ticket}/tagstags.syncview + tag{tags: [names]}
GET /tickets/{ticket}/linkslinks.indexview + link—
POST /tickets/{ticket}/linkslinks.storeview + link na oboch tiketoch{ticket: reference, type}; 201
DELETE /tickets/{ticket}/links/{link}links.destroyview + link204; odstráni presne tento riadok, z ktoréhokoľvek konca.
POST /tickets/{ticket}/mergemergeview + merge na zdroji aj cieli{target: reference, reason?}
GET /tickets/{ticket}/attachmentsattachments.indexviewKrátkodobé podpísané URL.
POST /tickets/{ticket}/attachmentsattachments.storeview + update (attachments) — agentiMultipart file; 201.
GET /tickets/{ticket}/attachments/{media}attachments.showview—
DELETE /tickets/{ticket}/attachments/{media}attachments.destroyview + update (attachments)204
GET /tickets/{ticket}/activityactivityviewFiltrované podľa roly.
GET / POST /ticket-categoriesadmin.categories.index / .storeviewAny / manageCategoriesStrom; ?project=alias:key; neaktívne kategórie (aj s podstromami) len pre agentov.
GET / PATCH / DELETE /ticket-categories/{id}admin.categories.show / .update / .destroyviewAny / manageCategoriesNeaktívna kategória je pre neagentov 404 category_not_found; mazanie berie {reassign_to?}.
POST /ticket-categories/{id}/moveadmin.categories.movemanageCategories{parent_id, position?}
GET / POST /ticket-fieldsadmin.fields.index / .storeviewAny / manageFields?project=, ?category=; filtrované podľa roly.
GET / PATCH / DELETE /ticket-fields/{id}admin.fields.show / .update / .destroyviewAny / manageFields—
GET / POST /ticket-tagsadmin.tags.index / .storeviewTags (agenti) / manageTags—
PATCH / DELETE /ticket-tags/{id}admin.tags.update / .destroymanageTags—
GET /tickets/guest/{ticket}guest.showpodpísaný odkazPozrite Hostia a odkazy pre hostí.
POST /tickets/guest/{ticket}/repliesguest.replypodpísaný odkaz—
POST /tickets/guest/{ticket}/transitions/{transition}guest.transitionpodpísaný odkazLen prechody žiadateľa.
GET /tickets/guest/{ticket}/attachments/{media}guest.attachmentpodpísaný odkazSúbory tiketu a jeho verejného vlákna.

Endpoint na obnovenie kategórie neexistuje — použite Tickets::categories()->restore().

Založenie tiketu

POST /api/tickets
{
  "subject": "Printer on floor 3 is jammed",
  "description": "Paper jam error E-102.",
  "category_id": 4,
  "priority": "high",
  "channel": "web",
  "fields": {"asset_tag": "PRN-0042"},
  "project": {"type": "project", "id": 1}
}

Samoobsluha zakladá tiket pre prihláseného používateľa. subject (≤ 255), description, category_id, priority, channel, project a fields (pre neagenta len polia, ktoré smie upravovať) sú otvorené každému, kto smie zakladať. requester, guest (polia namiesto žiadateľa), assignee, due_at (po teraz), tags a watchers sú vstupy len pre agentov; riešiteľ iný ako vy vyžaduje oprávnenie assign. Polymorfné vstupy sú {type: alias, id} s aliasom z povolených zoznamov — názov triedy, neznámy alias alebo kľúč bez záznamu vráti 422. Prílohy sú multipart attachments[].

Hromadné akcie

POST /api/tickets/bulk
{"action": "transition", "tickets": ["TCK-1", "TCK-2"], "transition": "resolve", "payload": {"resolution": "Fixed"}}

action je assign (assignee alebo claim: true), unassign, transition (transition, reason?, payload?), priority (priority) alebo tags (tags.attach / tags.detach, každé najviac 50 názvov) — najviac tickets.routes.bulk.max (100) rôznych čísel. Každý tiket ide vlastnou kontrolou policy aj transakciou a zlyhanie jedného nikdy nezastaví ostatné:

{"data": [
  {"ticket": "TCK-1", "ok": true, "status": 200},
  {"ticket": "TCK-2", "ok": false, "status": 422, "code": "ticket_transition_denied", "message": "…"}
]}

Zlyhanie validácie pri jednom tikete hlási code validation_failed; každé iné zlyhanie nesie kód z tabuľky chýb (ticket_not_found pre neznáme číslo alebo tiket, ktorý používateľ vidieť nesmie). assign bez assignee a bez claim: true je 422 pre celú požiadavku.

Telá požiadaviek

EndpointTelo
PATCH /tickets/{ticket}Ktorékoľvek z subject (≤ 255), description (≤ 65535, nullable), priority, category_id (nullable), due_at (po teraz, nullable), requester ({type, id}) — zmení sa len to, čo pošlete; meta hostiteľa sa cez API zapisovať nedá.
POST …/transitions/{transition}{reason?: string ≤ 5000, payload?: object}; {transition} zodpovedá [a-z][a-z0-9_]{0,63}.
POST …/replies{body: string ≤ 65535, channel?, attachments[]?, transition?, reason?, payload?}
POST …/notes, POST /tickets/guest/{ticket}/replies{body, channel?, attachments[]?}
PATCH …/comments/{comment}{body}
GET …/comments?internal=1 pre interné vlákno (agenti).
PUT …/fields{fields: {key: value, …}} (present; null hodnotu zruší).
POST …/watchers{watcher: {type, id}}; DELETE …/watchers/{alias}:{key}.
PUT …/tags{tags: [name, …]} (present, ≤ 50, každý ≤ 64) — nahradí celú sadu.
POST …/links{ticket: reference, type: relates_to | duplicates | blocks | parent_of}
POST …/merge{target: reference, reason?: string ≤ 5000}
POST …/attachmentsMultipart file.
GET /ticketsper_page (1 – max_per_page), page, filter[…], sort

Uploady (attachments[], file) sa cez media-library riadia mime typmi a limitom veľkosti svojej skupiny — tickets.attachments.* pre súbory tiketu, comments.media.* z balíka comments pre súbory správ — najviac 10 súborov na požiadavku.

Admin endpointy

Preložiteľný text (name, description, label, help, popis možnosti) je reťazec v aktuálnom jazyku alebo mapa jazykov (objekt s kľúčmi en, sk, …). Polymorfné vstupy sú {type: alias, id}; filtre zoznamov berú alias:key.

EndpointTelo / query
GET /ticket-categories?project=alias:key — globálny strom plus kategórie projektu, najprv korene, potomkovia vnorení.
POST /ticket-categories{name, description?, parent_id?, project?: {type, id}, position?, is_active?, default_priority?, default_assignee?: {type, id}, default_due_in?: ISO-8601 (PT4H)}
PATCH /ticket-categories/{id}Ktorékoľvek z name, description, position, is_active, default_priority, default_assignee, default_due_in (null zruší popis alebo predvolenú hodnotu); rodič sa mení cez /move, projekt sa nemení nikdy.
POST /ticket-categories/{id}/move{parent_id: id | null, position?} (parent_id musí byť prítomné; null = do koreňa).
DELETE /ticket-categories/{id}{reassign_to?: id} — potrebné, kým kategóriu používajú živé tikety.
GET /ticket-fields?project=alias:key, ?category=id (sada, ktorú by dostal tiket v tejto kategórii). Neagenti vidia len aktívne polia viditeľné pre žiadateľa.
POST /ticket-fields{key, type, label, help?, options?: {value: label}, rules?: [string], required?, encrypted?, default?, requester_visible?, requester_editable?, position?, is_active?, project?: {type, id}, category_id?} — najviac 200 možností a 20 pravidiel.
PATCH /ticket-fields/{id}Ktorékoľvek z type, label, help, options, rules, required, encrypted, default, requester_visible, requester_editable, position, is_active (key, projekt a kategória sú pevné).
GET /ticket-tags?project=alias:key — globálne štítky plus štítky projektu, podľa názvu (agenti).
POST /ticket-tags{name: string ≤ 64, color?: #rrggbb, project?: {type, id}} — opätovné vytvorenie zmazaného názvu ho obnoví, bez tiketov.
PATCH /ticket-tags/{id}Ktorékoľvek z name, color (null ju zruší).

Resources

Podľa roly: divák je agent (oprávnenie agenta a rozsah agentov mu tiket povoľujú), žiadateľ, účastník (sledujúci) alebo hosť (cez podpísaný odkaz).

ResourceKaždý, kto smie vidieťAgenti navyše
TicketResourcereference, number, subject, description, status, priority, category ({id, name, slug}), project ({type, id}), requester, assignee, channel, due_at, resolved_at, closed_at, merged_into (číslo tiketu), messages_count (verejné správy), created_at, updated_at; detailné odpovede pridajú fields (viditeľné pre žiadateľa) a attachments.assigned_at, first_responded_at, last_activity_at, tags ({name, slug, color}), úplný žiadateľ, všetky správy v počte, všetky polia.
TicketCommentResourceid, body, role, author, channel, attachments, created_at, updated_atinternal, status (stav moderácie z comments).
TicketActivityResourceid, type, label, system, actor, properties (typy morph ako aliasy), occurred_at — len verejné záznamy.visibility, interné záznamy, surové properties.
TicketLinkResourceid, type, label, direction (outgoing / incoming), ticket {reference, subject, status} — len ticket {reference}, ak divák druhý tiket vidieť nesmie.—
TicketAttachmentResourceid (uuid), name, mime_type, size, url, created_at—
TicketGuestResource{type: 'guest', name} — meno len pre samotného hosťa.{type: 'guest', id, name, fields} — všetky polia hosťa; osobné údaje zostávajú agentom.
TicketCategoryResourceid, name, description, slug, parent_id, project, position, is_active, childrentranslations {name, description} (mapy jazykov), defaults {priority, assignee, due_in}.
TicketFieldResourceid, key, type, label, help, options ([{value, label}], aktuálny jazyk), required, category_id, project, position.translations {label, help}, rules, default, encrypted, requester_visible, requester_editable, is_active
TicketTagResourceid, name, slug, color, project (dostanú sa k nemu len agenti).—

Osoby (žiadateľ, riešiteľ, autor, aktér, sledujúci) sa zobrazujú svojím aliasom z povolených zoznamov, nikdy názvom triedy: agenti dostanú {type, id, name}; ostatní {type, name}, pričom name je null pre hosťa, ktorý nie je samotným divákom. Žiadatelia nikdy neuvidia interné poznámky, zadržané odpovede, interné aktivity, last_activity_at, štítky ani zoznamy sledujúcich a meta hostiteľa sa neobjaví v žiadnom resource. Hodnoty enumov prichádzajú s preloženým popisom a farbou, takže klient vykreslí odznak bez vlastných tabuliek:

"status":   {"value": "pending", "label": "Waiting on requester", "category": "waiting", "color": "amber"},
"priority": {"value": "high", "label": "High", "weight": 30, "color": "orange"},
"channel":  {"value": "web", "label": "Web", "color": "blue"}

Telá odpovedí

EndpointTelo
GET /ticketsStránkovaná kolekcia TicketResource (data, links, meta).
POST /tickets (201), GET / PATCH /tickets/{ticket}, POST …/restore, POST …/transitions/{transition}, PUT / DELETE …/assigneeDetailný TicketResource.
GET …/transitions{data: [{name, label, to, to_label, requires_reason, payload}]}
GET …/commentsKolekcia TicketCommentResource, od najstaršej.
POST …/replies, POST …/notes (201), PATCH …/comments/{comment}TicketCommentResource.
GET / PUT …/fields{data: [{key, label, type, value}]} — platné polia v poradí (pre neagentov len viditeľné pre žiadateľa); value je null, ak nie je nastavená.
GET / POST …/watchers (201){data: [person, …]} — všetci sledujúci od najstaršieho; neagent, ktorý pridá seba, dostane len vlastný záznam.
PUT …/tagsŠtítky tiketu, kolekcia TicketTagResource.
GET / POST …/links (201){data: [TicketLinkResource, …]} — všetky prepojenia z pohľadu tohto tiketu.
POST …/merge{source: TicketResource, target: TicketResource, moved: {comments, attachments}}
GET …/attachments, GET …/attachments/{media}, POST …/attachments (201)TicketAttachmentResource (kolekcia).
GET …/activityKolekcia TicketActivityResource od najstaršieho ([] pri vypnutom tickets.activity.enabled).
POST /tickets/bulk{data: [{ticket, ok, status, code?, message?}]}
admin endpointyTicketCategoryResource (strom pre index), TicketFieldResource, TicketTagResource; store vráti 201.
každé iné DELETE, POST / DELETE …/watch204, bez tela.

Zoznam tiketov — filtre, radenie, stránkovanie

GET /tickets je query-builder-for-laravel nad visibleTo($user); každý filter je jeden zo scopes modelu Ticket:

FilterHodnotaScope
filter[status]zoznam hodnôt stavovwithStatus
filter[status_category]zoznam: open, active, waiting, resolved, closedwithStatusCategory
filter[priority]zoznamwithPriority
filter[category]zoznam id; filter[category_descendants]=1 pridá podkategórieinCategory
filter[assignee]zoznam alias:key, me, noneassignedTo / unassigned
filter[requester]zoznam alias:key (agenti; ktokoľvek iný nájde len seba), merequestedBy
filter[project]alias:key, noneinProject
filter[tag]zoznam slugov — len agenti (pre ostatných 400)taggedWith
filter[watching]mewatchedBy
filter[overdue]1 (0 nepridá nič)overdue
filter[due_from], filter[due_to], filter[created_from], filter[created_to]dátum alebo dátum a čas (inkluzívne; *_to len s dátumom, napríklad 2026-10-17, platí do konca dňa)dueFrom / dueTo / createdFrom / createdTo
filter[channel]zoznamviaChannel
filter[merged]booleanmerged
filter[search]text — presne číslo tiketu, alebo predmet / popis (% a _ doslova)search
filter[fields.<key>]zoznamwhereField
filter[fields.<key>.from], filter[fields.<key>.to]polia number / decimal / date / datetime (inkluzívne)whereFieldBetween
filter[trashed]with, only — len držitelia oprávnenia manage—

Filtre len pre agentov — tag, requester=alias:key a pole, ktoré nie je viditeľné pre žiadateľa — nájdu len tikety, na ktorých diváka nechá pracovať rozsah agentov. Neznámy filter alebo radenie vráti 400 pri query-builder.mode.unknown_filter = reject (predvolené); ignore ich zahodí.

Radenie: created_at, updated_at, due_at, number, priority (váhy enumu) a pre agentov last_activity_at. Predvolene -last_activity_at pre agentov a -created_at pre ostatných. Stránkovanie: per_page (predvolene 20, najviac tickets.routes.max_per_page) a page; zoznam načíta kategóriu, riešiteľa, žiadateľa, štítky a počty správ vopred, v pevnom počte dopytov.

Chyby

Mapujú sa v kontroléroch balíka — nikdy vo vašom exception handleri — na {message, code}. Chyby konfigurácie zostávajú 500: opraviť ich musí hostiteľ, nie klient.

StavcodeKedy
400(query-builder)Neznámy filter alebo radenie.
403forbiddenZamietnutie policy; UnauthorizedCommentActionException z comments (pri zapnutom comments.authorization).
403access_deniedNie je účastníkom tiketu.
403guest_link_invalidAkýkoľvek nefunkčný odkaz pre hosťa — jedno telo pre všetky.
404ticket_not_foundNeznáme číslo, alebo tiket, ktorý používateľ vidieť nesmie (skrytý = neexistujúci); na každom endpointe tiketu sa najprv overí view.
404comment_not_found, attachment_not_found, link_not_found, category_not_found, field_not_found, tag_not_foundZáznam, ktorý nepatrí tomuto tiketu, alebo neznámy.
409ticket_closedSpráva na uzavretom tikete (alebo zámok z comments).
409ticket_trashedZmena zmazaného tiketu.
409field_type_locked, duplicate_referenceUzamknutý typ poľa; duplicitný prefix projektu.
422 / 429ticket_transition_deniedLifecycle prechod odmietol (s denials); 429 s Retry-After, keď zamietnutie uvádza, kedy to skúsiť znova.
422unknown_transition, unknown_field, field_not_editable, unknown_*_type—
422invalid_assignee, invalid_link, invalid_merge, invalid_category, invalid_field, invalid_tag, invalid_watcher, invalid_project—
422comment_rejected, invalid_comment_bodycomments text odmietol.
422validačné telo LaraveluValidácia form requestu alebo hodnôt polí — vstupy pomenované v jazyku požiadavky; polia hosťa pod guest.<name>.

Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

Ďalšie spôsoby podpory vrátane kryptomien

Odoslaním daru súhlasíte s našimi podmienkami prijímania darov.

Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.