Forked from
Stud.IP / Stud.IP
4172 commits behind the upstream repository.
-
Marcus Eibrink-Lunzenauer authoredMarcus Eibrink-Lunzenauer authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
RouteMap.php 24.26 KiB
<?php
namespace JsonApi;
use JsonApi\Contracts\JsonApiPlugin;
use JsonApi\Middlewares\Authentication;
use JsonApi\Middlewares\DangerousRouteHandler;
use JsonApi\Middlewares\JsonApi as JsonApiMiddleware;
use JsonApi\Middlewares\StudipMockNavigation;
use Slim\Routing\RouteCollectorProxy;
/**
* Diese Klasse ist die JSON-API-Routemap, in der alle Routen
* registriert werden und die Middleware hinzugefügt wird, die
* JSON-API spezifische Fehlerbehandlung usw. übernimmt.
*
* Routen der Kernklasen sind hier explizit vermerkt.
*
* Routen aus Plugins werden über die PluginEngine abgefragt. Plugins
* können genau dann eigene Routen registrieren, wenn sie das
* Interface \JsonApi\Contracts\JsonApiPlugin implementieren.
*
* Routen können entweder mit Autorisierung oder auch ohne eingetragen
* werden. Autorisierte Kernrouten werden in
* RouteMap::authenticatedRoutes vermerkt. Kernrouten ohne
* notwendige Autorisierung werden in
* RouteMap::unauthenticatedRoutes registriert. Routen aus Plugins
* werden jeweils in den Methoden
* \JsonApi\Contracts\JsonApiPlugin::registerAuthenticatedRoutes und
* \JsonApi\Contracts\JsonApiPlugin::registerUnauthenticatedRoutes
* eingetragen.
*
* Zu authentifizierende Routen werden in \JsonApi\Middlewares\Authentication
* authentifiziert.
*
* Wie Routen registriert werden, kann man im `User Guide` des
* Slim-Frameworks nachlesen
* (http://www.slimframework.com/docs/objects/router.html#how-to-create-routes)
*
* Route-Handler können als Funktionen, in der Slim-Syntax
* "Klassenname:Methodenname" oder auch mit dem Klassennamen einer
* Klasse, die __invoke implementiert, angegeben werden. Die
* __invoke-Variante wird hier sehr empfohlen.
*
* Beispiel:
*
* use Studip\MeineRoute;
*
* $this->app->post('/article/{id}/comments', MeineRoute::class);
*
* @see \JsonApi\Middlewares\JsonApi
* @see \JsonApi\Middlewares\Authentication
* @see \JsonApi\Contracts\JsonApiPlugin
* @see http://www.slimframework.com/docs/objects/router.html#how-to-create-routes
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class RouteMap
{
/** @var \Slim\App */
private $app;
/**
* Der Konstruktor.
*
* @param \Slim\App $app die Slim-Applikation, in der die Routen
* definiert werden sollen
*/
public function __construct(\Slim\App $app)
{
$this->app = $app;
}
/**
* Hier werden die Routen tatsächlich eingetragen.
* Autorisierte Routen werden mit der Middleware
* \JsonApi\Middlewares\Authentication ausgestattet und in
* RouteMap::authenticatedRoutes eingetragen. Routen ohne
* Autorisierung werden in RouteMap::unauthenticatedRoutes vermerkt.
*/
public function __invoke(RouteCollectorProxy $group): void
{
$group->group('', [$this, 'authenticatedRoutes'])->add(new Authentication($this->getAuthenticator()));
$group->group('', [$this, 'unauthenticatedRoutes']);
$group->get('/discovery', Routes\DiscoveryIndex::class);
}
/**
* Hier werden autorisierte (Kern-)Routen explizit vermerkt.
* Außerdem wird über die \PluginEngine allen JsonApiPlugins die
* Möglichkeit gegeben, sich hier einzutragen.
*/
public function authenticatedRoutes(RouteCollectorProxy $group): void
{
\PluginEngine::sendMessage(JsonApiPlugin::class, 'registerAuthenticatedRoutes', $this->app);
$group->get('/users', Routes\Users\UsersIndex::class);
$group->get('/users/me', Routes\Users\UsersShow::class)->setName('get-myself');
$group->get('/users/{id}', Routes\Users\UsersShow::class);
$group->delete('/users/{id}', Routes\Users\UsersDelete::class)->add(DangerousRouteHandler::class);
$group->get('/users/{id}/activitystream', Routes\ActivityStreamShow::class);
$group->get('/users/{id}/institute-memberships', Routes\InstituteMemberships\ByUserIndex::class);
$group->get('/users/{id}/course-memberships', Routes\CourseMemberships\ByUserIndex::class);
$group->get('/course-memberships/{id}', Routes\CourseMemberships\CourseMembershipsShow::class);
$group->patch('/course-memberships/{id}', Routes\CourseMemberships\CourseMembershipsUpdate::class);
$group->get('/users/{id}/schedule', Routes\Schedule\UserScheduleShow::class)->setName('get-schedule');
$group->get('/schedule-entries/{id}', Routes\Schedule\ScheduleEntriesShow::class);
$group->get('/seminar-cycle-dates/{id}', Routes\Schedule\SeminarCycleDatesShow::class);
$group->get('/users/{id}/config-values', Routes\ConfigValues\ByUserIndex::class);
$group->get('/config-values/{id}', Routes\ConfigValues\ConfigValuesShow::class);
$group->patch('/config-values/{id}', Routes\ConfigValues\ConfigValuesUpdate::class);
$this->addAuthenticatedBlubberRoutes($group);
// $this->addAuthenticatedConsultationRoutes($group);
$this->addAuthenticatedContactsRoutes($group);
$this->addAuthenticatedCoursesRoutes($group);
$this->addAuthenticatedCoursewareRoutes($group);
$this->addAuthenticatedEventsRoutes($group);
$this->addAuthenticatedFeedbackRoutes($group);
$this->addAuthenticatedFilesRoutes($group);
$this->addAuthenticatedForumRoutes($group);
$this->addAuthenticatedInstitutesRoutes($group);
$this->addAuthenticatedMessagesRoutes($group);
$this->addAuthenticatedNewsRoutes($group);
$this->addAuthenticatedStudyAreasRoutes($group);
$this->addAuthenticatedWikiRoutes($group);
}
/**
* Hier werden unautorisierte (Kern-)Routen explizit vermerkt.
* Außerdem wird über die \PluginEngine allen JsonApiPlugins die
* Möglichkeit gegeben, sich hier einzutragen.
*/
public function unauthenticatedRoutes(RouteCollectorProxy $group): void
{
\PluginEngine::sendMessage(JsonApiPlugin::class, 'registerUnauthenticatedRoutes', $group);
$group->get('/semesters', Routes\SemestersIndex::class);
$group->get('/semesters/{id}', Routes\SemestersShow::class)->setName('get-semester');
$group->get('/studip/properties', Routes\Studip\PropertiesIndex::class);
}
private function getAuthenticator(): callable
{
return $this->app->getContainer()->get('studip-authenticator');
}
private function addAuthenticatedBlubberRoutes(RouteCollectorProxy $group): void
{
// find BlubberThreads
$group->get('/courses/{id}/blubber-threads', Routes\Blubber\ThreadsIndex::class)->setArgument('type', 'course');
$group
->get('/institutes/{id}/blubber-threads', Routes\Blubber\ThreadsIndex::class)
->setArgument('type', 'institute');
$group->get('/studip/blubber-threads', Routes\Blubber\ThreadsIndex::class)->setArgument('type', 'public');
$group->get('/users/{id}/blubber-threads', Routes\Blubber\ThreadsIndex::class)->setArgument('type', 'private');
$group->get('/blubber-threads', Routes\Blubber\ThreadsIndex::class)->setArgument('type', 'all');
$group->get('/blubber-threads/{id}', Routes\Blubber\ThreadsShow::class);
// create, read, update and delete BlubberComments
$group->get('/blubber-threads/{id}/comments', Routes\Blubber\CommentsByThreadIndex::class);
$group->post('/blubber-threads/{id}/comments', Routes\Blubber\CommentsCreate::class);
$group->get('/blubber-comments', Routes\Blubber\CommentsIndex::class);
$group->get('/blubber-comments/{id}', Routes\Blubber\CommentsShow::class);
$group->patch('/blubber-comments/{id}', Routes\Blubber\CommentsUpdate::class);
$group->delete('/blubber-comments/{id}', Routes\Blubber\CommentsDelete::class);
// REL mentions
$this->addRelationship(
$group,
'/blubber-threads/{id}/relationships/mentions',
Routes\Blubber\Rel\Mentions::class
);
}
private function addAuthenticatedConsultationRoutes(RouteCollectorProxy $group): void
{
$group->get('/users/{id}/consultations', Routes\Consultations\BlocksByUserIndex::class);
$group->get('/consultation-blocks/{id}', Routes\Consultations\BlockShow::class);
$group->get('/consultation-blocks/{id}/slots', Routes\Consultations\SlotsByBlockIndex::class);
$group->get('/consultation-slots/{id}', Routes\Consultations\SlotShow::class);
$group->get('/consultation-slots/{id}/bookings', Routes\Consultations\BookingsBySlotIndex::class);
$group->post('/consultation-slots/{id}/bookings', Routes\Consultations\BookingsCreate::class);
$group->get('/consultation-bookings/{id}', Routes\Consultations\BookingsShow::class);
$group->delete('/consultation-bookings/{id}', Routes\Consultations\BookingsDelete::class);
}
private function addAuthenticatedContactsRoutes(RouteCollectorProxy $group): void
{
$group->get('/users/{id}/contacts', Routes\Users\ContactsIndex::class);
$this->addRelationship($group, '/users/{id}/relationships/contacts', Routes\Users\Rel\Contacts::class);
}
private function addAuthenticatedEventsRoutes(RouteCollectorProxy $group): void
{
$group->get('/courses/{id}/events', Routes\Events\CourseEventsIndex::class);
$group->get('/users/{id}/events', Routes\Events\UserEventsIndex::class);
// not a JSON:API route
$group->get('/users/{id}/events.ics', Routes\Events\UserEventsIcal::class);
}
private function addAuthenticatedFeedbackRoutes(RouteCollectorProxy $group): void
{
$group->get('/feedback-elements/{id}', Routes\Feedback\FeedbackElementsShow::class);
$group->get('/feedback-elements/{id}/entries', Routes\Feedback\FeedbackEntriesIndex::class);
$group->get('/courses/{id}/feedback-elements', Routes\Feedback\FeedbackElementsByCourseIndex::class);
$group->get('/file-refs/{id}/feedback-elements', Routes\Feedback\FeedbackElementsByFileRefIndex::class);
$group->get('/folders/{id}/feedback-elements', Routes\Feedback\FeedbackElementsByFolderIndex::class);
$group->get('/feedback-entries/{id}', Routes\Feedback\FeedbackEntriesShow::class);
}
private function addAuthenticatedInstitutesRoutes(RouteCollectorProxy $group): void
{
$group->get('/institute-memberships/{id}', Routes\InstituteMemberships\InstituteMembershipsShow::class);
$group->get('/institutes/{id}', Routes\Institutes\InstitutesShow::class);
$group->get('/institutes', Routes\Institutes\InstitutesIndex::class);
$group->get('/institutes/{id}/status-groups', Routes\Institutes\StatusGroupsOfInstitutes::class);
}
private function addAuthenticatedNewsRoutes(RouteCollectorProxy $group): void
{
$group->post('/courses/{id}/news', Routes\News\CourseNewsCreate::class);
$group->post('/users/{id}/news', Routes\News\UserNewsCreate::class);
$group->post('/news', Routes\News\StudipNewsCreate::class);
$group->post('/news/{id}/comments', Routes\News\CommentCreate::class);
$group->patch('/news/{id}', Routes\News\NewsUpdate::class);
$group->get('/news/{id}', Routes\News\NewsShow::class);
$group->get('/courses/{id}/news', Routes\News\ByCourseIndex::class);
$group->get('/users/{id}/news', Routes\News\ByUserIndex::class);
$group->get('/news/{id}/comments', Routes\News\CommentsIndex::class);
$group->get('/news', Routes\News\ByCurrentUser::class);
$group->get('/studip/news', Routes\News\GlobalNewsShow::class);
$group->delete('/news/{id}', Routes\News\NewsDelete::class);
$group->delete('/comments/{id}', Routes\News\CommentsDelete::class);
// RELATIONSHIP: 'ranges'
$this->addRelationship($group, '/news/{id}/relationships/ranges', Routes\News\Rel\Ranges::class);
}
private function addAuthenticatedStudyAreasRoutes(RouteCollectorProxy $group): void
{
$group->get('/study-areas', Routes\StudyAreas\StudyAreasIndex::class);
$group->get('/study-areas/{id}', Routes\StudyAreas\StudyAreasShow::class);
$group->get('/study-areas/{id}/children', Routes\StudyAreas\ChildrenOfStudyAreas::class);
$group->get('/study-areas/{id}/courses', Routes\StudyAreas\CoursesOfStudyAreas::class);
$group->get('/study-areas/{id}/institute', Routes\StudyAreas\InstituteOfStudyAreas::class);
$group->get('/study-areas/{id}/parent', Routes\StudyAreas\ParentOfStudyAreas::class);
}
private function addAuthenticatedWikiRoutes(RouteCollectorProxy $group): void
{
$this->addRelationship($group, '/wiki-pages/{id:.+}/relationships/parent', Routes\Wiki\Rel\ParentPage::class);
$group->get('/wiki-pages/{id:.+}/children', Routes\Wiki\ChildrenIndex::class);
$group->get('/wiki-pages/{id:.+}/descendants', Routes\Wiki\DescendantsIndex::class);
$group->get('/courses/{id}/wiki-pages', Routes\Wiki\WikiIndex::class);
$group->get('/wiki-pages/{id:.+}', Routes\Wiki\WikiShow::class)->setName('get-wiki-page');
$group->post('/courses/{id}/wiki-pages', Routes\Wiki\WikiCreate::class);
$group->patch('/wiki-pages/{id:.+}', Routes\Wiki\WikiUpdate::class);
$group->delete('/wiki-pages/{id:.+}', Routes\Wiki\WikiDelete::class);
}
private function addAuthenticatedCoursesRoutes(RouteCollectorProxy $group): void
{
$group->get('/courses', Routes\Courses\CoursesIndex::class);
$group->get('/courses/{id}', Routes\Courses\CoursesShow::class);
$group->get('/users/{id}/courses', Routes\Courses\CoursesByUserIndex::class);
$group->get('/courses/{id}/memberships', Routes\Courses\CoursesMembershipsIndex::class);
$this->addRelationship(
$group,
'/courses/{id}/relationships/memberships',
Routes\Courses\Rel\Memberships::class
);
$group->get('/courses/{id}/status-groups', Routes\Courses\StatusGroupsOfCourses::class);
$group->get('/sem-classes', Routes\Courses\SemClassesIndex::class);
$group->get('/sem-classes/{id}', Routes\Courses\SemClassesShow::class);
$group->get('/sem-classes/{id}/sem-types', Routes\Courses\SemTypesBySemClassIndex::class);
$group->get('/sem-types', Routes\Courses\SemTypesIndex::class);
$group->get('/sem-types/{id}', Routes\Courses\SemTypesShow::class);
}
private function addAuthenticatedCoursewareRoutes(RouteCollectorProxy $group): void
{
$group->get('/{type:courses|users}/{id}/courseware', Routes\Courseware\CoursewareInstancesShow::class);
$group->patch('/courseware-instances/{id}', Routes\Courseware\CoursewareInstancesUpdate::class);
$this->addRelationship(
$group,
'/courseware-instances/{id}/relationships/bookmarks',
Routes\Courseware\Rel\BookmarkedStructuralElements::class
);
$group->get('/courseware-instances/{id}/bookmarks', Routes\Courseware\BookmarkedStructuralElementsIndex::class);
$group->get('/courseware-blocks/{id}', Routes\Courseware\BlocksShow::class);
$group->post('/courseware-blocks', Routes\Courseware\BlocksCreate::class);
$group->patch('/courseware-blocks/{id}', Routes\Courseware\BlocksUpdate::class);
$group->delete('/courseware-blocks/{id}', Routes\Courseware\BlocksDelete::class);
$this->addRelationship(
$group,
'/courseware-blocks/{id}/relationships/edit-blocker',
Routes\Courseware\Rel\BlocksEditBlocker::class
);
$this->addRelationship(
$group,
'/courseware-blocks/{id}/relationships/file-refs',
Routes\Courseware\Rel\BlocksFilerefs::class
);
$group->get('/courseware-blocks/{id}/file-refs', Routes\Courseware\BlocksListFiles::class);
// not a JSON route
$group->post('/courseware-blocks/{id}/copy', Routes\Courseware\BlocksCopy::class);
$group->get('/courseware-containers/{id}', Routes\Courseware\ContainersShow::class);
$group->post('/courseware-containers', Routes\Courseware\ContainersCreate::class);
$group->patch('/courseware-containers/{id}', Routes\Courseware\ContainersUpdate::class);
$group->delete('/courseware-containers/{id}', Routes\Courseware\ContainersDelete::class);
$group->get('/courseware-containers/{id}/blocks', Routes\Courseware\BlocksIndex::class);
$this->addRelationship(
$group,
'/courseware-containers/{id}/relationships/blocks',
Routes\Courseware\Rel\ContainersBlocks::class
);
$this->addRelationship(
$group,
'/courseware-containers/{id}/relationships/edit-blocker',
Routes\Courseware\Rel\ContainersEditBlocker::class
);
// not a JSON route
$group->post('/courseware-containers/{id}/copy', Routes\Courseware\ContainersCopy::class);
$group->get('/courseware-structural-elements/{id}', Routes\Courseware\StructuralElementsShow::class);
$group->get('/courseware-structural-elements', Routes\Courseware\StructuralElementsIndex::class);
$group->post('/courseware-structural-elements', Routes\Courseware\StructuralElementsCreate::class);
$group->patch('/courseware-structural-elements/{id}', Routes\Courseware\StructuralElementsUpdate::class);
$group->delete('/courseware-structural-elements/{id}', Routes\Courseware\StructuralElementsDelete::class);
$group->get(
'/courseware-structural-elements/{id}/children',
Routes\Courseware\ChildrenOfStructuralElementsIndex::class
);
$group->get('/courseware-structural-elements/{id}/containers', Routes\Courseware\ContainersIndex::class);
$this->addRelationship(
$group,
'/courseware-structural-elements/{id}/relationships/containers',
Routes\Courseware\Rel\StructuralElementsContainers::class
);
$this->addRelationship(
$group,
'/courseware-structural-elements/{id}/relationships/children',
Routes\Courseware\Rel\StructuralElementsChildren::class
);
$group->get(
'/courseware-structural-elements/{id}/descendants',
Routes\Courseware\DescendantsOfStructuralElementsIndex::class
);
$this->addRelationship(
$group,
'/courseware-structural-elements/{id}/relationships/edit-blocker',
Routes\Courseware\Rel\StructuralElementsEditBlocker::class
);
$group->post(
'/courseware-structural-elements/{id}/image',
Routes\Courseware\StructuralElementsImageUpload::class
);
$group->delete(
'/courseware-structural-elements/{id}/image',
Routes\Courseware\StructuralElementsImageDelete::class
);
// not a JSON route
$group->post('/courseware-structural-elements/{id}/copy', Routes\Courseware\StructuralElementsCopy::class);
$group->get('/courseware-blocks/{id}/user-data-field', Routes\Courseware\UserDataFieldOfBlocksShow::class);
$group->get('/courseware-user-data-fields/{id}', Routes\Courseware\UserDataFieldsShow::class);
$group->patch('/courseware-user-data-fields/{id}', Routes\Courseware\UserDataFieldsUpdate::class);
$group->get('/courseware-blocks/{id}/user-progress', Routes\Courseware\UserProgressOfBlocksShow::class);
$group->get('/courseware-user-progresses/{id}', Routes\Courseware\UserProgressesShow::class);
$group->patch('/courseware-user-progresses/{id}', Routes\Courseware\UserProgressesUpdate::class);
$group->get('/courseware-blocks/{id}/comments', Routes\Courseware\BlockCommentsOfBlocksIndex::class);
$group->post('/courseware-block-comments', Routes\Courseware\BlockCommentsCreate::class);
$group->get('/courseware-block-comments/{id}', Routes\Courseware\BlockCommentsShow::class);
$group->patch('/courseware-block-comments/{id}', Routes\Courseware\BlockCommentsUpdate::class);
$group->delete('/courseware-block-comments/{id}', Routes\Courseware\BlockCommentsDelete::class);
$group->get('/courseware-blocks/{id}/feedback', Routes\Courseware\BlockFeedbacksOfBlocksIndex::class);
$group->post('/courseware-block-feedback', Routes\Courseware\BlockFeedbacksCreate::class);
$group->get('/courseware-block-feedback/{id}', Routes\Courseware\BlockFeedbacksShow::class);
}
private function addAuthenticatedFilesRoutes(RouteCollectorProxy $group): void
{
$group->get('/terms-of-use', Routes\Files\TermsOfUseIndex::class);
$group->get('/terms-of-use/{id}', Routes\Files\TermsOfUseShow::class);
$group->get('/{type:courses|institutes|users}/{id}/file-refs', Routes\Files\RangeFileRefsIndex::class);
$group->get('/{type:courses|institutes|users}/{id}/folders', Routes\Files\RangeFoldersIndex::class);
$group->post('/{type:courses|institutes|users}/{id}/folders', Routes\Files\RangeFoldersCreate::class);
$group->get('/file-refs/{id}', Routes\Files\FileRefsShow::class);
$group->patch('/file-refs/{id}', Routes\Files\FileRefsUpdate::class);
$group->delete('/file-refs/{id}', Routes\Files\FileRefsDelete::class);
$this->addRelationship(
$group,
'/file-refs/{id}/relationships/terms-of-use',
Routes\Files\Rel\TermsOfFileRef::class
);
$group->map(['HEAD'], '/file-refs/{id}/content', Routes\Files\FileRefsContentHead::class);
$group->get('/file-refs/{id}/content', Routes\Files\FileRefsContentShow::class);
$group->post('/file-refs/{id}/content', Routes\Files\FileRefsContentUpdate::class);
$group->get('/folders/{id}', Routes\Files\FoldersShow::class);
$group->patch('/folders/{id}', Routes\Files\FoldersUpdate::class);
$group->delete('/folders/{id}', Routes\Files\FoldersDelete::class);
// not a JSON route
$group->post('/folders/{id}/copy', Routes\Files\FoldersCopy::class);
$group->get('/folders/{id}/file-refs', Routes\Files\SubfilerefsIndex::class);
$group->get('/folders/{id}/folders', Routes\Files\SubfoldersIndex::class);
$group->post('/folders/{id}/file-refs', Routes\Files\NegotiateFileRefsCreate::class);
$group->post('/folders/{id}/folders', Routes\Files\SubfoldersCreate::class);
$group->get('/files/{id}', Routes\Files\FilesShow::class);
$group->get('/files/{id}/file-refs', Routes\Files\FileRefsOfFilesShow::class);
$this->addRelationship($group, '/files/{id}/relationships/file-refs', Routes\Files\Rel\FileRefsOfFile::class);
}
private function addAuthenticatedMessagesRoutes(RouteCollectorProxy $group): void
{
$group->get('/users/{id}/inbox', Routes\Messages\InboxShow::class);
$group->get('/users/{id}/outbox', Routes\Messages\OutboxShow::class);
$group->post('/messages', Routes\Messages\MessageCreate::class);
$group->get('/messages/{id}', Routes\Messages\MessageShow::class);
$group->patch('/messages/{id}', Routes\Messages\MessageUpdate::class);
$group->delete('/messages/{id}', Routes\Messages\MessageDelete::class);
}
private function addAuthenticatedForumRoutes(RouteCollectorProxy $group): void
{
$group->get('/courses/{id}/forum-categories', Routes\Forum\ForumCategoriesIndex::class);
$group->get('/forum-entries/{id}', Routes\Forum\ForumEntriesShow::class);
$group->get('/forum-entries/{id}/entries', Routes\Forum\ForumEntryEntriesIndex::class);
$group->get('/forum-categories/{id}', Routes\Forum\ForumCategoriesShow::class);
$group->get('/forum-categories/{id}/entries', Routes\Forum\ForumCategoryEntriesIndex::class);
$group->post('/forum-entries/{id}/entries', Routes\Forum\ForumEntryEntriesCreate::class);
$group->post('/forum-categories/{id}/entries', Routes\Forum\ForumCategoryEntriesCreate::class);
$group->post('/courses/{id}/forum-categories', Routes\Forum\ForumCategoriesCreate::class);
$group->patch('/forum-categories/{id}', Routes\Forum\ForumCategoriesUpdate::class);
$group->patch('/forum-entries/{id}', Routes\Forum\ForumEntriesUpdate::class);
$group->delete('/forum-categories/{id}', Routes\Forum\ForumCategoriesDelete::class);
$group->delete('/forum-entries/{id}', Routes\Forum\ForumEntriesDelete::class);
}
private function addRelationship(RouteCollectorProxy $group, string $url, string $handler): void
{
$group->map(['GET', 'PATCH', 'POST', 'DELETE'], $url, $handler);
}
}