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

Kým ho nezapnete, je vypnuté:

EVENTS_ROUTES=on
  • Prefix events.routes.prefix (api), názvy rout events.*, middleware events.routes.middleware (['api', 'throttle:events-api']); zapisovacie routy pridajú events.routes.auth_middleware (['auth']).
  • Nenastavený prepínač zostáva vypnutý — aj v publikovanej konfigurácii, ktorej pole routes kľúč nemá, pretože zlučovanie je plytké.
  • Ak chcete upraviť súbor rout, publikujte --tag=events-routes: keď routes/events.php vo vašej aplikácii existuje, načíta sa namiesto súboru balíka a prepínač ho naďalej riadi.
  • Zápisy vždy vyžadujú prihláseného používateľa. Po vašom auth_middleware spustí každá zapisovacia routa vlastnú kontrolu balíka: chýbajúci prihlásený používateľ alebo používateľ guardu, ktorý nie je Eloquent model, je 401 — aj pri auth_middleware = []. Beží pred validáciou, takže hosť sa nikdy nedozvie tvar požiadavky. Čítacie routy a routy odkazu z pozvánky hostí naďalej pustia.
  • Čítania rozpoznajú váš guard. Každá routa spúšťa aj UseConfiguredGuard: keď auth_middleware uvádza guardy (auth:sanctum alebo Authenticate::class s guardmi), prvý z nich, ktorý požiadavku overí, sa stane jej guardom, ako to robí middleware auth z Laravelu — nikoho však neodmietne. Používateľ s tokenom potom číta ako on sám — jeho súkromné podujatia, viewer.rsvp, filter[mine], odkaz na pripojenie — a požiadavka bez tokenu je hosť. Obyčajné auth používa predvolený guard, ktorý čítania používajú už teraz.
// config/events.php — a token client reads as itself, writes need a token
'routes' => [
    'enabled' => env('EVENTS_ROUTES') ?: 'off',
    'prefix' => 'api',
    'middleware' => ['api', 'throttle:events-api'],
    'auth_middleware' => ['auth:sanctum'],
    'max_per_page' => 100,
    'rate_limits' => ['api' => 120, 'rsvp' => 20, 'check_in' => 300, 'feeds' => 60],
],

Konvencie

  • {event} je uuid — obmedzené tak, aby sa naň /events/feed.ics nikdy nedostal — a podujatie, ktoré volajúci vidieť nesmie, je 404 presne ako neexistujúce, na každom endpointe. {rsvp} a {tier} sú obmedzené na podujatie; {tier} je kľúč typu vstupenky — aj čisto číselný, ak ho niektorý typ podujatia má —, inak je číslo jeho id. {venue} je číselné.
  • Na vaše modely sa API odvoláva cez alias, nikdy cez názov triedy: {type: user, id: 42} v telách a user:42 v cestách a filtroch (events.organizers, events.attendees) — ten istý alias, ktorý vykresľujú odpovede, takže klient pošle späť to, čo prečítal. V tele je každé zlyhanie — neznámy alias, chybný kľúč, chýbajúci riadok — rovnaké 422 unknown_alias.
  • Časy v požiadavkách sú miestny čas v pásme podujatia alebo okamih ISO-8601 s posunom; odpovede používajú ISO-8601 s vlastným posunom podujatia.
  • Textové polia prijímajú reťazec (aktuálny jazyk) alebo mapu jazykov. Ceny sú desatinné reťazce s voliteľnou menou — viac desatinných miest, než má mena (15.999 EUR, 15.5 JPY), je 422 invalid_tier; odpovede vykresľujú MoneyResource z money-for-laravel — {minor, decimal, currency, exponent, formatted}, pričom minor je reťazec a formatted je len na zobrazenie. Bezplatný typ vstupenky má price: null.
  • Zoznamy sú length-aware paginátory z Laravelu (data, links, current_page, per_page, total, …) s per_page obmedzeným na events.routes.max_per_page.

Routy

Všetkých 49 rout, zoskupených, s cestami relatívne k prefixu. Riadky s poznámkou „Aj hostia“ odpovedajú aj neprihláseným; každá iná routa vyžaduje prihláseného používateľa:

Metóda · cestaNázovOprávneniePoznámky
GET /eventsevents.indexviewAnyAj hostia. Filtre objavovania nižšie → stránkovaný EventResource.
GET /events/feed.icsevents.feed—Aj hostia. ?category=&label=&organizer=alias:key&venue= → text/calendar (inline); throttle events-feeds.
GET /events/{event}events.showviewAj hostia. {data: EventResource} s hosts a viewer.can.
GET /events/{event}/calendar.icsevents.calendarviewAj hostia. text/calendar (príloha); odkaz na pripojenie len pre diváka, ktorý ho smie vidieť.
GET /events/{event}/tiersevents.tiers.indexviewAj hostia. Skryté len pre správcov.
GET /events/{event}/attendeesevents.attendees.indexviewAttendeesAj hostia. ?status=&checked_in=&tier=&per_page= (predvolene 25) — filtre len pre správcov.
GET /events/{event}/hostsevents.hosts.indexviewAj hostia. {data: [referencia spoluorganizátora]}.
GET /event-venuesevents.venues.index—Aj hostia. ?mine=1&near=lat,lng&radius=&per_page= (predvolene 25).
GET /event-venues/{venue}events.venues.show—Aj hostia.
POST /event-invitations/accept · declineevents.invitations.accept · declinetokenAj hostia. {token, response?} (predvolene going, interested) → RsvpResource (pohľad vlastníka) / {status: declined}; throttle events-rsvp.
POST /eventsevents.storecreate201 {data: EventResource}.
PATCH /events/{event} · DELETE /events/{event}events.update · destroyupdate · deleteZmenia sa len poslané polia; ?notify=0 zapíše potichu · 204.
POST /events/{event}/restoreevents.restorerestoreZmazané podujatia, len správcovia (ostatní 404).
POST /events/{event}/publish · unpublish · completeevents.publish · …publish · …{data: EventResource}
POST /events/{event}/postpone · cancelevents.postpone · cancelpostpone · cancel{reason?} (≤ 500).
POST /events/{event}/rescheduleevents.reschedulereschedule{starts_at, ends_at?, reason?}
POST /events/{event}/move-onlineevents.move-onlinemoveOnline{url, keep_venue?}
PUT · DELETE /events/{event}/posterevents.poster.update · destroyupdateMultipart poster alebo token draftu · odstráni ho.
POST /events/{event}/tiers · tiers/reorderevents.tiers.store · reordermanageTiers201 AdmissionTierResource · {order: [id typu, …]} → 204.
PATCH · DELETE /events/{event}/tiers/{tier}events.tiers.update · destroymanageTiersTyp vstupenky podľa kľúča (aj čisto číselného), inak je číslo jeho id; len poslané polia · 204.
GET /events/{event}/rsvp · rsvp/qr.svgevents.rsvp.show · qr—Vaše RSVP (pohľad vlastníka) alebo null · váš kód ako image/svg+xml, Cache-Control: no-store, private.
PUT · DELETE /events/{event}/rsvpevents.rsvp.update · destroyrsvp{response, guests?, tier?, note?} · odpovie not_going; throttle events-rsvp.
POST /events/{event}/attendeesevents.attendees.storemanageAttendees{attendee: {type, id}, tier?, guests?, over_capacity?} → 201
PATCH · DELETE /events/{event}/attendees/{rsvp}events.attendees.update · destroymanageAttendees{tier?, guests?}: aspoň jedno, inak 422 — overuje sa až po policy, takže kto nie je správca, dostane 403 pri akomkoľvek tele; uplatní sa oboje alebo nič · 204.
POST · DELETE /events/{event}/attendees/{rsvp}/check-inevents.attendees.check-in · undo-check-incheckInRučný check-in {guests?} (bez kódu) · zrušenie.
POST /events/{event}/check-insevents.check-ins.storecheckInSkenovanie pri vstupe {code, guests?} → CheckInResultResource, 201 prvý raz, 200 opakovane; throttle events-check-in.
GET /events/{event}/waitlist · POST …/waitlist/promoteevents.waitlist.index · promoteviewWaitlist · manageAttendeesV poradí posúvania · {limit?} (1–1000).
POST · DELETE /events/{event}/hosts…events.hosts.store · destroymanageHosts{person: {type, id}} (Organizer) → 201 · {host} = alias:key → 204.
GET · POST · DELETE /events/{event}/invitations…events.invitations.index · store · destroyinviteTokeny sa nikdy nevykreslia · {person: {type, id}, message?} (≤ 1000) → 201 · {invitee} = alias:key → 204.
POST · PATCH · DELETE /event-venues…events.venues.store · update · destroyVenuePolicyTelo miesta konania nižšie → 201 · len poslané polia · 204.
POST /event-venues/{venue}/geocodeevents.venues.geocodeVenuePolicy geocodeVždy geokóduje znova (force).

Routa na potvrdenie platby neexistuje: potvrďte ju z webhooku poskytovateľa v PHP cez Events::rsvps()->confirmPayment($reference).

Vytvorenie podujatia

{
  "title": { "en": "Jazz Night", "sk": "Jazzový večer" },
  "venue_id": 7,
  "starts_at": "2026-11-14 20:00",
  "duration": "PT3H",
  "capacity": 120,
  "max_guests": 1,
  "category": "jazz",
  "labels": ["Live", "Late night"],
  "tiers": [
    { "name": "Free entry", "key": "free" },
    { "name": "Supporter", "price": "15.00", "currency": "EUR", "capacity": 30 }
  ],
  "publish": true
}
PolePravidlá
titlePovinné; reťazec alebo mapa jazykov, každý ≤ 200 znakov.
description≤ 20 000.
organizer{type, id} z events.organizers — predvolene volajúci; musí volajúceho povoliť.
venue_id · venueExistujúce miesto konania (akýkoľvek organizátor môže usporiadať podujatie na akomkoľvek mieste) — alebo nové miesto priamo v tele, vlastnené volajúcim: name (povinné), street, city, region, postal_code, country, coordinates, timezone.
online_url · attendance_mode · visibility · guest_list · timezone≤ 2048 · hodnoty enumov · názov IANA.
starts_at · ends_at · duration · doors_open_at · all_day_from · all_day_throughstarts_at povinné, ak nie je all_day_from; celodenné dátumy Y-m-d.
capacity · max_guests0–10,000,000 · 0–1000
waitlist · waitlist_auto_promote · show_counts · publishBoolean hodnoty; publish: true ho ohlási v tej istej transakcii.
rsvp_opens_at · rsvp_closes_at · remindersReťazce; ≤ 5 pripomienok.
category · labelsKategória stromu · ≤ 10 reťazcov (v katalógu organizátora pri events.tags.scope = organizer).
tiers≤ 50 položiek {name, key?, price?, currency?, capacity?, hidden?}; price v tvare ^\d{1,12}(\.\d{1,4})?$.
poster_draftToken draftu z media-library.

PATCH /events/{event} zmení len poslané polia a null vymaže pole, ktoré môže byť prázdne; category (null vymaže) a labels (presne tieto) sa uplatnia v tej istej transakcii. Nový začiatok ohláseného podujatia sa odmietne — použite POST /reschedule. ?notify=0, alebo notify: false v tele, zapíše potichu.

Typy vstupeniek a miesta konania

Telá typov vstupeniek prijímajú name (pri vytvorení povinné, ≤ 200), description (≤ 2000), key (≤ 64; bez neho sa odvodí z názvu), price + currency, capacity, sale_starts_at, sale_ends_at, min_party (1–1000), max_party, hidden a position. Telá miest konania prijímajú name (pri vytvorení povinné), street, city, region, postal_code, country (2 písmená), formatted_address, coordinates (lat,lng, [lat, lng] alebo {latitude, longitude}), timezone, capacity a geocode a pri vytvorení owner ({type, id}, ktorý musí volajúceho povoliť) alebo shared (len administrátori a abilities.shared_venues). Bez owner a shared patrí nové miesto volajúcemu. Pri PATCH sa každé pole adresy mení samostatne a vlastníctvo sa nikdy nemení.

Vaše RSVP

PUT /api/events/{event}/rsvp s telom:

{ "response": "going", "tier": "free", "guests": 1, "note": "Wheelchair access" }

vráti vaše vlastné RSVP v pohľade vlastníka, s kódom pre check-in a URL QR kódu:

{
  "data": {
    "id": 42,
    "status": "going",
    "guests": 1,
    "party_size": 2,
    "tier": { "id": 1, "key": "free", "name": "Free entry" },
    "note": "Wheelchair access",
    "waitlist_position": null,
    "held_until": null,
    "amount": null,
    "checked_in_at": null,
    "checked_in_guests": null,
    "responded_at": "2026-11-02T18:12:40+01:00",
    "payment_url": null,
    "check_in_code": "EV1-…",
    "qr_url": "https://example.com/api/events/0195f2c4-7a1e-7c3b-9d2e-5b8a1f4c6e21/rsvp/qr.svg"
  }
}

Zoznam hostí

GET /events/{event}/attendees vypíše správcom akýkoľvek stav (?status=, predvolene going) so záznamom o check-ine — nikdy s kódmi — a filtrom podľa checked_in a tier. Ktokoľvek iný, kto zoznam smie vidieť, uvidí len účastníkov so stavom going ako {attendee, guests}: status=interested je preňho 403 a status, checked_in a tier sa ignorujú — vždy celý zoznam, nikdy zúžený na to, kto je na mieste alebo kto má skrytý typ vstupenky.

Objavovanie: GET /events

Postavené na scopes modelu, takže PHP aj HTTP filtrujú rovnako; query-builder rozdelí každú hodnotu podľa čiarok. Základom je browse($viewer) — viditeľné, verejne uvedené, ohlásené:

ParameterScope
filter[when]=upcoming|ongoing|pastupcoming() …
filter[from], filter[to]Prekrývajúce sa s rozsahom, miestny čas v ?tz= (predvolene app.timezone).
filter[date]=2026-11-14onLocalDate() — skutočný dátum (2027-02-30 je 422).
filter[near]=48.14,17.11,25near() — polomer voliteľný, ≤ geo.max_radius_km.
filter[category]=jazzinCategory() vrátane podkategórií.
filter[labels]=live,outdoorVšetky štítky.
filter[organizer]=user:42organizedBy() — riadok sa nikdy nevyhľadáva; neexistujúci nezodpovedá ničomu.
filter[venue]=7,8 · filter[mode]=online,mixedatVenue() · withAttendanceMode()
filter[free]=true · filter[available]=truefree() · withSeatsAvailable()
filter[search]=…search() — vyhľadávanie podreťazca v názve a popise so zneškodnenými znakmi (čiarky zostanú).
filter[status]=postponed,rescheduledinPhase()
filter[mine]=organizing|attending|interestedVaše podujatia, každá viditeľnosť (organizing vrátane konceptov); hostia nedostanú nič.
sort=starts_at (default), -starts_at, created_at, -created_at, popularity, distancedistance vyžaduje filter[near] (inak 400); popularity počíta podujatie s vypnutým show_counts ako nula účastníkov — okrem filter[mine]=organizing.
per_pagePredvolene 15, ≤ events.routes.max_per_page.

Neplatná hodnota filtra je 422 invalid_query. Stránka načíta miesta konania a ich vlastníkov, organizátorov, štítky, plagáty, typy vstupeniek, vlastné RSVP diváka, cesty kategórií a to, ktoré podujatia divák spravuje, v pevnom počte dotazov — počet nerastie s veľkosťou stránky.

Resources

Bezplatné podujatie tak, ako ho vidí hosť v zozname:

{
  "id": "0195f2c4-7a1e-7c3b-9d2e-5b8a1f4c6e21",
  "slug": "jazz-night",
  "title": "Jazz Night",
  "description": "A live quartet, then an open jam session.",
  "phase": "scheduled",
  "schema_status": "EventScheduled",
  "attendance_mode": "offline",
  "starts_at": "2026-11-14T20:00:00+01:00",
  "ends_at": "2026-11-14T23:00:00+01:00",
  "doors_open_at": null,
  "previous_starts_at": null,
  "all_day": false,
  "timezone": "Europe/Bratislava",
  "starts_on": "2026-11-14",
  "ends_on": "2026-11-14",
  "venue": {
    "id": 7,
    "name": "Blue Note",
    "slug": "blue-note",
    "address": {
      "street": "Hlavná 1", "city": "Bratislava", "region": null, "postal_code": "811 01",
      "country": "SK", "formatted": "Hlavná 1, 811 01 Bratislava, SK"
    },
    "coordinates": { "latitude": 48.1439, "longitude": 17.1097 },
    "timezone": "Europe/Bratislava",
    "capacity": null,
    "owner": null,
    "distance_km": null,
    "deleted": false
  },
  "online_url": null,
  "organizer": { "id": 1, "type": "user" },
  "category": { "id": 3, "slug": "jazz", "name": "Jazz", "color": null, "icon": null, "path": ["Music"] },
  "labels": [{ "id": 4, "slug": "live", "name": "Live", "color": null, "icon": null }],
  "poster": null,
  "tiers": [
    {
      "id": 1, "key": "free", "name": "Free entry", "description": null, "price": null, "free": true,
      "capacity": null, "seats_left": null, "on_sale": true, "sale_starts_at": null, "sale_ends_at": null,
      "min_party": 1, "max_party": null, "position": 1
    }
  ],
  "capacity": 120,
  "seats_left": 85,
  "counts": { "going": 35, "waitlisted": 0 },
  "viewer": { "rsvp": null },
  "links": {
    "self": "https://example.com/api/events/0195f2c4-7a1e-7c3b-9d2e-5b8a1f4c6e21",
    "ics": "https://example.com/api/events/0195f2c4-7a1e-7c3b-9d2e-5b8a1f4c6e21/calendar.ics",
    "url": "https://example.com/en/events/jazz-night"
  }
}
ResourceTvar
EventResourcePozrite príklad vyššie. status a visibility len pre správcov; online_url pre správcov a účastníkov so stavom going; seats_left a counts pre správcov, alebo pre všetkých pri show_counts (counts.interested len pre správcov); hosts a viewer.can len v detaile.
VenueResourceid, name, slug, address {street, city, region, postal_code, country, formatted}, coordinates {latitude, longitude} | null, timezone, capacity, owner, distance_km (3 decimals, when selected), deleted — never meta
AdmissionTierResourceid, key, name, description, price (MoneyResource alebo null), free, capacity, seats_left, on_sale, sale_starts_at, sale_ends_at, min_party, max_party, position a hidden pre správcov.
RsvpResourceid, status, guests, party_size, tier {id, key, name}, note, waitlist_position, held_until, amount, checked_in_at, checked_in_guests, responded_at; pohľad vlastníka pridá payment_url (kým je rezervované), check_in_code a qr_url; pohľad správcov pridá attendee, check_in_method a checked_in_by — nikdy kód.
InvitationResourceid, status, invitee, invited_by, message, expires_at (null, kým je odložené), responded_at, sent_at — nikdy token.
CheckInResultResourcefirst_check_in, guests, checked_in_at, method, attendee, rsvp (RsvpResource)

Referenčné resources

Každá osoba či tím, ktorý odpoveď spomína, sa vykreslí cez vlastný resource — predvolene {id, type}, kde type je alias modelu z events.organizers (prednostne) alebo events.attendees (user), ten istý, ktorý prijímajú telá a cesty alias:key; trieda, ktorá nie je v žiadnej mape, sa vykreslí krátkym názvom triedy. Nikdy názov PHP triedy ani morph typ, aj bez morph mapy. Chýbajúci model sa vykreslí ako null. Verzia 1.0 vykresľovala pri každej triede krátky názov (User):

Konfiguračný kľúčPredvolenéVykresľuje
events.resources.organizerEventOrganizerResourceorganizer
events.resources.hostEventHostResourcespoluorganizátori
events.resources.attendeeEventAttendeeResourceúčastníci
events.resources.inviteeEventInviteeResourcepozvaní
events.resources.actorEventActorResourceinvited_by, checked_in_by
events.resources.venue_ownerVenueOwnerResourcevlastník miesta konania

Ak chcete pridať meno či avatar, nastavte kľúč na vlastný JsonResource — dostane model. Prázdna hodnota znamená predvolený; čokoľvek, čo nie je inštanciovateľná podtrieda JsonResource, vyhodí výnimku s názvom kľúča. Zoznamy súvisiace modely načítajú vopred, takže resource, ktorý číta len ich atribúty, nepridá žiadne dotazy:

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

final class PersonResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return ['id' => $this->resource->getKey(), 'name' => $this->resource->name, 'avatar' => $this->resource->avatar_url];
    }
}

// config/events.php
'resources' => [
    'organizer' => App\Http\Resources\PersonResource::class,
    'host' => EventHostResource::class,
    'attendee' => App\Http\Resources\PersonResource::class,
    'invitee' => EventInviteeResource::class,
    'actor' => EventActorResource::class,
    'venue_owner' => VenueOwnerResource::class,
],

Chyby

{message, code} so stavom balíka — plus reason pri odmietnutí check-inu, denials pri zamietnutom prechode a errors pri validačnom 422. Správy sú preložené a nikdy neuvádzajú triedu a váš exception handler sa nikdy nemení:

{ "message": "This code is for another event.", "code": "check_in_refused", "reason": "wrong_event" }
StavcodeKedy
401(váš handler)Zápis bez prihláseného Eloquent používateľa.
403forbidden · invitation_requiredPolicy zamietla · súkromné podujatie bez pozvánky.
404event_not_found, venue_not_found, tier_not_found, rsvp_not_found, invalid_code, invalid_invitationNeexistuje alebo je skryté.
409event_trashed, rsvp_closed, event_full, tier_sold_out, tier_not_on_sale, hold_expired, invalid_rsvp_transition, check_in_refusedKonflikt stavu (check_in_refused pridá reason).
422validation_failed, invalid_event, invalid_schedule, invalid_venue, venue_in_use, invalid_tier, tier_key_taken, tier_in_use, tier_required, invalid_guest_count, invalid_organizer, unknown_alias, unknown_transition, transition_denied, invalid_query, tag_not_found, tag_limit_exceeded, invalid_tag, invalid_posterChybný vstup (validation_failed pridá errors, transition_denied pridá denials).
400invalid_querysort=distance bez filter[near].
429transition_denied (+ Retry-After) alebo throttlePrekročený limit požiadaviek.

Štítok iného rozsahu odpovie tag_not_found, rovnako ako chýbajúci. Plagát, ktorý media-library odmietne, alebo neznámy či vypršaný draft je invalid_poster.

Limity požiadaviek

Definujú sa, len keď sú routy zapnuté, za minútu:

LimiterPredvolenéKľúčRouty
events-apiroutes.rate_limits.api = 120používateľ, inak IPKaždá routa (v routes.middleware).
events-rsvprate_limits.rsvp = 20používateľ (alebo IP) a podujatiePUT / DELETE /rsvp, prijatie / odmietnutie pozvánky.
events-check-inrate_limits.check_in = 300používateľPOST /check-ins — skenery pri vstupe pracujú v nárazoch.
events-feedsrate_limits.feeds = 60IPfeed.ics, calendar.ics

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.