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

Konfigurácia

Balík funguje bez akejkoľvek konfigurácie. config/tickets.php publikujte, len ak chcete niečo prepísať:

php artisan vendor:publish --tag=tickets-config

Každý kľúč sa číta cez validujúcu čítačku (Support\TicketsConfig, nad Config z toolkitu): nezmyselná hodnota vyhodí InvalidTicketsConfigurationException (alebo InvalidConfigurationException z toolkitu) s názvom kľúča — nikdy sa potichu nepoužije náhradná hodnota.

  • Booleany sa čítajú ako on/off (aj true/false, 1/0, yes/no); čokoľvek iné vyhodí výnimku. Prázdna hodnota v env znamená dodanú predvolenú hodnotu.
  • Dĺžky trvania sú ISO-8601 (P7D, PT4H, P1DT12H); off prijímajú len kľúče, pri ktorých je to uvedené.
  • Test kontraktu konfigurácie stráži oba smery: každý kľúč, ktorý kód číta, je dodaný, a každý dodaný kľúč sa číta.

Konfiguračný súbor

// config/tickets.php (imports omitted)
return [
    'primary_key_type' => env('TICKETS_PRIMARY_KEY_TYPE', 'bigint'),
    'key_type' => env('TICKETS_KEY_TYPE', 'bigint'),

    'models' => [
        'ticket' => Ticket::class,
        'category' => TicketCategory::class,
        'field' => TicketField::class,
        'tag' => TicketTag::class,
        'guest' => TicketGuest::class,
    ],

    'lifecycle' => TicketLifecycle::class,
    'auto_close' => [
        'after' => env('TICKETS_AUTO_CLOSE_AFTER', 'P7D'),
    ],
    'priority' => [
        'enum' => TicketPriority::class,
        'default' => env('TICKETS_DEFAULT_PRIORITY', 'normal'),
    ],
    'reference' => [
        'prefix' => env('TICKETS_REFERENCE_PREFIX', 'TCK'),
    ],
    'channels' => ['web', 'email', 'api', 'phone', 'chat'],
    'default_channel' => 'api',

    'assignment' => [
        'strategy' => CategoryDefaultStrategy::class,
    ],
    'due' => [
        'resolver' => CategoryDueDateResolver::class,
        'soon_window' => env('TICKETS_DUE_SOON_WINDOW', 'PT4H'),
    ],

    'requesters' => ['user' => 'App\\Models\\User'],
    'assignables' => ['user' => 'App\\Models\\User'],
    'watchables' => null,
    'projects' => [],

    'guests' => [
        'enabled' => env('TICKETS_GUESTS', 'on'),
        'fields' => [
            'name' => ['type' => 'string', 'rules' => ['required', 'string', 'max:120'], 'display' => true],
            'email' => ['type' => 'string', 'rules' => ['nullable', 'email:rfc', 'max:191'], 'identifying' => true, 'route' => 'mail', 'normalize' => 'email'],
            'phone' => ['type' => 'string', 'rules' => ['nullable', 'string', 'max:32'], 'identifying' => true, 'route' => 'vonage', 'normalize' => 'phone', 'encrypted' => false],
        ],
        'require_one_identifying' => true,
        'update_on_resolve' => false,
        'link_ttl' => env('TICKETS_GUEST_LINK_TTL', 'P30D'),
        'revoke_on_close' => env('TICKETS_GUESTS_REVOKE_ON_CLOSE', 'off'),
        'link_generator' => SignedRouteLinkGenerator::class,
    ],

    'categories' => [
        'max_depth' => 5,
    ],
    'fields' => [
        'allowed_rules' => ['min', 'max', 'between', 'regex', 'digits', 'digits_between', 'alpha', 'alpha_num', 'alpha_dash', 'starts_with', 'ends_with'],
    ],

    'comments' => [
        'auto_approve' => env('TICKETS_COMMENTS_AUTO_APPROVE', 'on'),
        'reply_reopens' => env('TICKETS_REPLY_REOPENS', 'on'),
    ],
    'watchers' => [
        'can_view' => env('TICKETS_WATCHERS_CAN_VIEW', 'on'),
        'auto' => [
            'requester' => env('TICKETS_AUTO_WATCH_REQUESTER', 'on'),
            'assignee' => env('TICKETS_AUTO_WATCH_ASSIGNEE', 'on'),
            'agent_repliers' => env('TICKETS_AUTO_WATCH_AGENT_REPLIERS', 'on'),
            'mentioned' => env('TICKETS_AUTO_WATCH_MENTIONED', 'off'),
        ],
    ],

    'authorization' => [
        'register_policy' => env('TICKETS_REGISTER_POLICY', 'on'),
        'policy' => TicketPolicy::class,
        'abilities' => [
            'agent' => 'tickets.agent',
            'assign' => 'tickets.assign',
            'merge' => 'tickets.agent',
            'manage' => 'tickets.manage',
        ],
        'agent_scope' => null,
        'requester_transitions' => ['close', 'reopen'],
        'participants_can_transition' => env('TICKETS_PARTICIPANTS_CAN_TRANSITION', 'off'),
        'self_service' => env('TICKETS_SELF_SERVICE', 'on'),
        'requester_can_edit' => env('TICKETS_REQUESTER_CAN_EDIT', 'on'),
    ],

    'tags' => [
        'free_form' => env('TICKETS_TAGS_FREE_FORM', 'on'),
    ],
    'links' => [
        'cross_project' => env('TICKETS_LINKS_CROSS_PROJECT', 'on'),
        'max_depth' => 50,
    ],
    'merge' => [
        'cross_project' => env('TICKETS_MERGE_CROSS_PROJECT', 'off'),
        'move_attachments' => env('TICKETS_MERGE_MOVE_ATTACHMENTS', 'on'),
    ],

    'attachments' => [
        'bucket' => 'attachments',
        'disk' => env('TICKETS_ATTACHMENTS_DISK', 'local'),
        'mimes' => [
            'image/jpeg', 'image/png', 'image/gif', 'image/webp',
            'application/pdf', 'text/plain', 'text/csv',
            'application/zip',
            'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        ],
        'max_kb' => 10240,
    ],

    'activity' => [
        'enabled' => env('TICKETS_ACTIVITY', 'on'),
    ],
    'actor' => [
        'from_auth' => env('TICKETS_ACTOR_FROM_AUTH', 'on'),
        'guard' => env('TICKETS_ACTOR_GUARD'),
    ],

    'routes' => [
        // Blank means off: the route switch reads a blank value as on.
        'enabled' => env('TICKETS_ROUTES') ?: 'off',
        'prefix' => env('TICKETS_ROUTES_PREFIX', 'api'),
        'middleware' => ['api', 'auth', 'throttle:tickets-api'],
        'max_per_page' => 100,
        'rate_limit' => 120,
        'guest' => [
            'middleware' => ['api', 'throttle:tickets-guest'],
            'rate_limit' => 30,
        ],
        'bulk' => [
            'max' => 100,
        ],
    ],

    'notifications' => [
        'enabled' => env('TICKETS_NOTIFICATIONS', 'on'),
        'channels' => ['mail'],
        'queue' => env('TICKETS_NOTIFICATIONS_QUEUE'),
        'notify_actor' => env('TICKETS_NOTIFY_ACTOR', 'off'),
        'guests' => env('TICKETS_NOTIFY_GUESTS', 'on'),
        'url' => env('TICKETS_NOTIFICATIONS_URL'),
        'types' => [
            'created' => TicketCreatedNotification::class,
            'assigned' => TicketAssignedNotification::class,
            'replied' => TicketRepliedNotification::class,
            'requester_replied' => TicketRequesterRepliedNotification::class,
            'note_added' => TicketNoteAddedNotification::class,
            'mentioned' => TicketMentionedNotification::class,
            'status_changed' => TicketStatusChangedNotification::class,
            'overdue' => TicketOverdueNotification::class,
            'due_soon' => TicketDueSoonNotification::class,
            'merged' => TicketMergedNotification::class,
        ],
    ],
];

Typy kľúčov

KľúčEnvPredvolenéÚčel
primary_key_typeTICKETS_PRIMARY_KEY_TYPEbigintPrimárny kľúč modelov Ticket a TicketGuest a každého stĺpca, ktorý ukazuje na tiket — bigint, uuid alebo ulid. Musí sa rovnať lifecycle.key_type, attributes.key_type a comments.key_type. Nastavte ho pred migráciou.
key_typeTICKETS_KEY_TYPEbigintId polymorfných vzťahov smerom von: žiadateľ, riešiteľ, projekt, sledujúci, aktér, autor prepojenia, predvolený riešiteľ kategórie. Musí sa rovnať kľúčom vašich používateľov, lifecycle.actor_key_type a comments.key_type. Nastavte ho pred migráciou.

Modely

KľúčEnvPredvolenéÚčel
models.ticket—Models\TicketModel tiketu. Náhrada musí dediť od triedy z balíka.
models.category—Models\TicketCategoryModel kategórie; náhrada dedí od triedy z balíka.
models.field—Models\TicketFieldModel definície vlastného poľa; náhrada dedí od triedy z balíka.
models.tag—Models\TicketTagModel štítka; náhrada dedí od triedy z balíka.
models.guest—Models\TicketGuestModel hosťa (žiadateľa bez účtu); náhrada dedí od triedy z balíka.

Životný cyklus, priorita, čísla, kanály

KľúčEnvPredvolenéÚčel
lifecycle—Lifecycle\TicketLifecycleDefinícia lifecycle-for-laravel, ktorá implementuje Contracts\TicketLifecycle a ktorej enum stavov implementuje Contracts\TicketState.
auto_close.afterTICKETS_AUTO_CLOSE_AFTERP7DAko dlho vyriešený tiket čaká, kým ho lifecycle:sweep uzavrie; off = nikdy. Číta sa vo chvíli, keď tiket vstúpi do stavu Resolved.
priority.enum—Enums\TicketPriorityEnum s reťazcovými hodnotami, ktorý implementuje Contracts\Priority.
priority.defaultTICKETS_DEFAULT_PRIORITYnormalHodnota tohto enumu; použije sa, keď prioritu nenastaví volajúci ani kategória.
reference.prefixTICKETS_REFERENCE_PREFIXTCKPrefix tiketov bez projektu; ^[A-Z][A-Z0-9]{0,9}$.
channels—web, email, api, phone, chatAkceptované vstupné kanály, každý ^[a-z][a-z0-9_]{0,31}$. Prázdny alebo nenastavený zoznam sa číta ako dodaný; kanál, ktorý pridáte, berie popis z tickets::enums.ticket_channel.<value>.
default_channel—apiJeden z channels (inak api, inak prvý v zozname).

Predvolené hodnoty pri založení, termíny

KľúčEnvPredvolenéÚčel
assignment.strategy—Assignment\CategoryDefaultStrategyContracts\AssignmentStrategy, ktorej sa balík opýta, keď tiket vznikne bez riešiteľa.
due.resolver—Due\CategoryDueDateResolverContracts\DueDateResolver, ktorého sa balík opýta, keď tiket vznikne bez termínu.
due.soon_windowTICKETS_DUE_SOON_WINDOWPT4Htickets:sweep raz označí tiket, ktorého termín spadá do tohto okna (TicketDueSoon); off = nikdy.

Povolené aliasy polymorfných vzťahov

KľúčEnvPredvolenéÚčel
requesters—['user' => App\Models\User]Kto smie byť žiadateľom vo vstupe API ({type: alias, id}) — nikdy nie názov triedy. Model hosťa má vždy alias guest.
assignables—['user' => App\Models\User]Komu možno tiket priradiť; každá trieda musí implementovať Contracts\Assignee.
watchables—nullnull = všetky aliasy žiadateľov a riešiteľov.
projects—[]Aliasy projektov; každá trieda musí implementovať Contracts\TicketProject.

Hostia

KľúčEnvPredvolenéÚčel
guests.enabledTICKETS_GUESTSonSprístupní endpointy pre hostí (len pri zapnutých routách). Publikovaná konfigurácia bez tohto kľúča sa číta ako on; tickets:doctor upozorní.
guests.fields—name, email, phonePolia hosťa, uložené cez attributes-for-laravel — pozrite Hostia a odkazy pre hostí.
guests.require_one_identifying—trueHosť potrebuje aspoň jednu identifikačnú hodnotu.
guests.update_on_resolve—falsePrepísať odlišné neidentifikačné hodnoty, keď sa vracajúci hosť znova ozve.
guests.link_ttlTICKETS_GUEST_LINK_TTLP30DPlatnosť odkazu pre hosťa.
guests.revoke_on_closeTICKETS_GUESTS_REVOKE_ON_CLOSEoffUzavretie tiketu zneplatní všetky odkazy pre hostí k nemu.
guests.link_generator—Guests\SignedRouteLinkGeneratorImplementácia Contracts\GuestLinkGenerator.

Kategórie, polia, štítky, prepojenia, zlučovanie

KľúčEnvPredvolenéÚčel
categories.max_depth—5Najväčšia hĺbka stromu kategórií, 1–50.
fields.allowed_rules—min, max, between, regex, digits, digits_between, alpha, alpha_num, alpha_dash, starts_with, ends_withJediné pravidlá, ktoré smie mať vlastné pole; exists a unique sa odmietnu vždy.
tags.free_formTICKETS_TAGS_FREE_FORMonPripojenie neznámeho názvu štítka ho vytvorí; off = len existujúce štítky.
links.cross_projectTICKETS_LINKS_CROSS_PROJECTonPrepájať tikety rôznych projektov.
links.max_depth—50Ako hlboko prechádza kontrola cyklov pre blocks / parent_of, 1–1000.
merge.cross_projectTICKETS_MERGE_CROSS_PROJECToffZlučovať tikety rôznych projektov.
merge.move_attachmentsTICKETS_MERGE_MOVE_ATTACHMENTSonZlúčenie presunie prílohy zdrojového tiketu na cieľový.

Konverzácia a sledujúci

KľúčEnvPredvolenéÚčel
comments.auto_approveTICKETS_COMMENTS_AUTO_APPROVEonSchváliť odpoveď na tiket, ktorú hostiteľské comments.require_approval nechalo čakať — nikdy nie takú, ktorú zadržal blocklist. Pri off a zapnutom require_approval čaká každá odpoveď na moderátora a ani po schválení nemá na tiket účinok (pozrite Odpovede a interné poznámky).
comments.reply_reopensTICKETS_REPLY_REOPENSonOdpoveď žiadateľa znovu otvorí vyriešený tiket.
watchers.can_viewTICKETS_WATCHERS_CAN_VIEWonSledujúci, ktorý nie je agent, vidí tiket aj jeho verejné vlákno a smie odpovedať ako účastník. Pri off dostane prihlásený sledujúci 404 a hosť ako sledujúci žiadny odkaz (jeho odkazy vrátia 403, e-maily nemajú tlačidlo).
watchers.auto.requesterTICKETS_AUTO_WATCH_REQUESTERonŽiadateľ začne tiket sledovať pri jeho založení.
watchers.auto.assigneeTICKETS_AUTO_WATCH_ASSIGNEEonRiešiteľ začne tiket sledovať pri priradení.
watchers.auto.agent_repliersTICKETS_AUTO_WATCH_AGENT_REPLIERSonAgent, ktorý na tiket napíše, ho začne sledovať.
watchers.auto.mentionedTICKETS_AUTO_WATCH_MENTIONEDoffSpomenutá osoba (ktorá smie komentár čítať) začne tiket sledovať.

Autorizácia

KľúčEnvPredvolenéÚčel
authorization.register_policyTICKETS_REGISTER_POLICYonZaregistrovať policy pre model tiketu.
authorization.policy—Policies\TicketPolicyMusí dediť od TicketPolicy; neplatná trieda nezaregistruje nič (doctor to nahlási).
authorization.abilities.agent—tickets.agentGate oprávnenie, ktoré z používateľa robí agenta.
authorization.abilities.assign—tickets.assignPriradiť tiket niekomu inému.
authorization.abilities.merge—tickets.agentZlučovať tikety.
authorization.abilities.manage—tickets.manageMazať a obnovovať tikety (držiteľ musí byť zároveň agentom pre daný tiket — tickets.agent plus AgentScope); spravovať kategórie, polia a štítky.
authorization.agent_scope—nullContracts\AgentScope; null naviaže Authorization\UnrestrictedAgentScope (agenti vidia každý tiket).
authorization.requester_transitions—['close', 'reopen']Prechody, ktoré smie žiadateľ spustiť na vlastnom tikete; [] (alebo prázdny reťazec) = žiadne, null = predvolené; funguje aj reťazec oddelený čiarkami. Každý ^[a-z][a-z0-9_]{0,63}$.
authorization.participants_can_transitionTICKETS_PARTICIPANTS_CAN_TRANSITIONoffSpúšťať ich smie aj sledujúci.
authorization.self_serviceTICKETS_SELF_SERVICEonTiket smie založiť ktokoľvek prihlásený; off = len agenti.
authorization.requester_can_editTICKETS_REQUESTER_CAN_EDITonŽiadateľ smie upraviť predmet a popis, kým tiket nie je vyriešený.

Prílohy

KľúčEnvPredvolenéÚčel
attachments.bucket—attachmentsSkupina (bucket) media-library pre prílohy tiketov (súkromná); ^[a-z][a-z0-9_-]{0,63}$.
attachments.diskTICKETS_ATTACHMENTS_DISKlocalNesmie ho servírovať webový server — predvolený disk media-library (public) je dostupný pod /storage.
attachments.mimes—obrázky, PDF, text, CSV, ZIP, Word, ExcelAkceptované mime typy (type/subtype); [] = akékoľvek. Publikovaná konfigurácia, ktorej pole attachments tento kľúč nemá, dostane tento zoznam, nikdy nie akýkoľvek typ (doctor upozorní).
attachments.max_kb—10240Limit veľkosti v kilobajtoch, aspoň 1. Prílohy odpovedí a poznámok sa namiesto toho riadia comments.media.* z balíka comments.

Aktivity a aktér

KľúčEnvPredvolenéÚčel
activity.enabledTICKETS_ACTIVITYonZapisovať históriu aktivít; off = nezapíše sa nič a endpoint vráti [].
actor.from_authTICKETS_ACTOR_FROM_AUTHonVolanie bez explicitného aktéra sa pripíše prihlásenému používateľovi.
actor.guardTICKETS_ACTOR_GUARDprázdneGuard, ktorý sa na to použije; prázdne = predvolený guard.

HTTP API

KľúčEnvPredvolenéÚčel
routes.enabledTICKETS_ROUTESoffSprístupní JSON API. Zapísané ako env('TICKETS_ROUTES') ?: 'off', takže prázdna hodnota zostane off. Publikovaná konfigurácia bez tohto kľúča sa číta ako on (prepínač rout z toolkitu); tickets:doctor upozorní.
routes.prefixTICKETS_ROUTES_PREFIXapiPrefix URI (/ = žiadny); písmená, číslice, -, _, . a /.
routes.middleware—['api', 'auth', 'throttle:tickets-api']Endpointy agentov a žiadateľov; so Sanctum použite auth:sanctum.
routes.max_per_page—100Najväčšia stránka tiketov, 1–1000 (predvolene 20).
routes.rate_limit—120Požiadavky za minútu na používateľa (na IP pri anonymnom volajúcom) na tickets-api, aspoň 1.
routes.guest.middleware—['api', 'throttle:tickets-guest']Endpointy pre hostí; EnsureGuestTicketAccess sa pripojí vždy.
routes.guest.rate_limit—30Požiadavky za minútu na IP a tiket na tickets-guest, aspoň 1.
routes.bulk.max—100Najviac tiketov v jednom hromadnom volaní, 1–1000.

Notifikácie

KľúčEnvPredvolenéÚčel
notifications.enabledTICKETS_NOTIFICATIONSonPosielať notifikácie vôbec.
notifications.channels—['mail']database potrebuje vašu tabuľku notifications; každý iný kanál potrebuje podtriedu (pozrite Notifikácie).
notifications.queueTICKETS_NOTIFICATIONS_QUEUEprázdneNázov fronty; prázdne = predvolená fronta spojenia.
notifications.notify_actorTICKETS_NOTIFY_ACTORoffUpovedomiť aj toho, kto akciu vykonal (potvrdenie created odíde vždy).
notifications.guestsTICKETS_NOTIFY_GUESTSonPosielať e-maily hosťom — žiadateľom aj sledujúcim — s novým podpísaným odkazom.
notifications.urlTICKETS_NOTIFICATIONS_URLprázdneTlačidlo v e-maile pre prihlásených príjemcov s doplneným {reference} (https://help.example.com/tickets/{reference}); prázdne = bez tlačidla; musí byť http(s).
notifications.types.<type>—trieda z balíkaJeden kľúč na typ — created, assigned, replied, requester_replied, note_added, mentioned, status_changed, overdue, due_soon, merged: trieda, ktorá dedí od triedy z balíka, alebo false (aj reťazce false, off, 0, no) na vypnutie; prázdne či nenastavené = trieda z balíka.

Pri vypnutom authorization.register_policy Laravel cez vlastné vyhľadávanie policy stále priradí Models\Ticket k Policies\TicketPolicy — off neznamená zamietnuť všetko; vlastnú policy zaregistrujte cez Gate::policy(). Limity požiadaviek sa definujú len pri zapnutých routách a platia, len kým je ich middleware throttle: v zozname.

Nastavenia z iných balíkov

KľúčPrečo
lifecycle.key_type, lifecycle.actor_key_type, comments.key_type, attributes.key_typeZladenie typov kľúčov (doctor, inštalácia).
comments.authorizationUpozornenie doctora (pozrite Odpovede a interné poznámky).
comments.media.private_disk, media.stream.enabledKontrola URL príloh v doctorovi.
query-builder.mode.unknown_filterreject (predvolené) odpovie na neznámy filter[...] kódom 400; ignore ho zahodí.

Prostredie (env)

Bežné prepínače sú riadené cez env, takže konfiguráciu publikujete len zriedka:

TICKETS_REFERENCE_PREFIX=TCK
TICKETS_DEFAULT_PRIORITY=normal
TICKETS_AUTO_CLOSE_AFTER=P7D
TICKETS_DUE_SOON_WINDOW=PT4H
TICKETS_ROUTES=on
TICKETS_ATTACHMENTS_DISK=local
TICKETS_NOTIFICATIONS_URL=https://help.example.com/tickets/{reference}
TICKETS_NOTIFY_GUESTS=on

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.