Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement common CRUD controllers, Poster Type and Group web UIs, and other minor fixes. #51

Merged
merged 11 commits into from
Jul 23, 2024
Merged
Empty file modified backend/bin/console
100644 → 100755
Empty file.
58 changes: 53 additions & 5 deletions backend/config/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,59 @@
->setName('dashboard');

$group->group('/admin', function (RouteCollectorProxy $group) {
$group->group('/groups', function (RouteCollectorProxy $group) {
$group->get(
'',
App\Controller\Dashboard\Admin\GroupsController::class . ':listAction'
)->setName('dashboard:admin:groups');

$group->map(
['GET', 'POST'],
'/create',
App\Controller\Dashboard\Admin\GroupsController::class . ':createAction'
)->setName('dashboard:admin:groups:create');

$group->map(
['GET', 'POST'],
'/edit[/{id}]',
App\Controller\Dashboard\Admin\GroupsController::class . ':editAction'
)->setName('dashboard:admin:groups:edit');

$group->get(
'/delete[/{id}]',
App\Controller\Dashboard\Admin\GroupsController::class . ':deleteAction'
)->setName('dashboard:admin:groups:delete');
});

$group->group('/poster_types', function (RouteCollectorProxy $group) {
$group->get(
'',
App\Controller\Dashboard\Admin\PosterTypesController::class . ':listAction'
)->setName('dashboard:admin:poster_types');

$group->map(
['GET', 'POST'],
'/create',
App\Controller\Dashboard\Admin\PosterTypesController::class . ':createAction'
)->setName('dashboard:admin:poster_types:create');

$group->map(
['GET', 'POST'],
'/edit[/{id}]',
App\Controller\Dashboard\Admin\PosterTypesController::class . ':editAction'
)->setName('dashboard:admin:poster_types:edit');

$group->get(
'/delete[/{id}]',
App\Controller\Dashboard\Admin\PosterTypesController::class . ':deleteAction'
)->setName('dashboard:admin:poster_types:delete');
});

$group->get(
'/users',
App\Controller\Dashboard\Admin\UsersAction::class
)->setName('dashboard:admin:users');

$group->group('/worlds', function (RouteCollectorProxy $group) {
$group->get(
'',
Expand All @@ -54,11 +107,6 @@
App\Controller\Dashboard\Admin\WorldsController::class . ':deleteAction'
)->setName('dashboard:admin:worlds:delete');
});

$group->get(
'/users',
App\Controller\Dashboard\Admin\UsersAction::class
)->setName('dashboard:admin:users');
})->add(new App\Middleware\Auth\RequireAdmin());

$group->map(
Expand Down
175 changes: 175 additions & 0 deletions backend/src/Controller/Dashboard/AbstractCrudController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
<?php

namespace App\Controller\Dashboard;

use App\Http\Response;
use App\Http\ServerRequest;
use Doctrine\DBAL\Connection;
use Psr\Http\Message\ResponseInterface;

abstract class AbstractCrudController
{
public function __construct(
protected Connection $db,
protected string $itemName,
protected string $listRouteName,
protected string $listTemplateName,
protected string $editTemplateName,
protected string $tableName,
protected string $idField = 'id'
) {
}

public function listAction(
ServerRequest $request,
Response $response,
array $params
): ResponseInterface {
$records = $this->db->createQueryBuilder()
->select('*')
->from($this->tableName)
->fetchAllAssociative();

return $request->getView()->renderToResponse(
$response,
$this->listTemplateName,
[
'records' => $records,
]
);
}

public function createAction(
ServerRequest $request,
Response $response,
array $params
): ResponseInterface {
$row = [];
$error = null;

if ($request->isPost()) {
try {
$row = $this->modifyFromRequest($request);

$this->db->insert($this->tableName, $row);

$request->getFlash()->success(sprintf('%s created.', $this->itemName));

return $response->withRedirect(
$request->getRouter()->urlFor($this->listRouteName)
);
} catch (\Throwable $e) {
$error = $e->getMessage();
}
}

return $request->getView()->renderToResponse(
$response,
$this->editTemplateName,
[
'isEditMode' => false,
'row' => $row,
'error' => $error,
]
);
}

public function editAction(
ServerRequest $request,
Response $response,
array $params
): ResponseInterface {
$id = $this->requireId($request, $params);

$row = $this->getRecord($id);
unset($row[$this->idField]);

$error = null;

if ($request->isPost()) {
try {
$row = $this->modifyFromRequest(
$request,
$row,
true
);

$this->db->update($this->tableName, $row, [$this->idField => $id]);

$request->getFlash()->success(sprintf('%s updated.', $this->itemName));

return $response->withRedirect(
$request->getRouter()->urlFor($this->listRouteName)
);
} catch (\Throwable $e) {
$error = $e->getMessage();
}
}

return $request->getView()->renderToResponse(
$response,
$this->editTemplateName,
[
'isEditMode' => true,
'row' => $row,
'error' => $error,
]
);
}

protected function requireId(
ServerRequest $request,
array $params
): int|string {
$id = $params[$this->idField] ?? $request->getParam($this->idField);
if (empty($id)) {
throw new \InvalidArgumentException('ID is required.');
}

return $id;
}

protected function getRecord(
int|string $id
): array {
$row = $this->db->createQueryBuilder()
->select('*')
->from($this->tableName)
->where($this->db->createExpressionBuilder()->eq($this->idField, ':id'))
->setParameter('id', $id)
->fetchAssociative();

if ($row === false) {
throw new \InvalidArgumentException(sprintf('%s not found!', $this->itemName));
}

return $row;
}

abstract protected function modifyFromRequest(
ServerRequest $request,
array $row = [],
bool $isEditMode = false
): array;

public function deleteAction(
ServerRequest $request,
Response $response,
array $params
): ResponseInterface {
$id = $this->requireId($request, $params);

$this->db->delete(
$this->tableName,
[
$this->idField => $id,
]
);

$request->getFlash()->success(sprintf('%s deleted.', $this->itemName));

return $response->withRedirect(
$request->getRouter()->urlFor($this->listRouteName)
);
}
}
43 changes: 43 additions & 0 deletions backend/src/Controller/Dashboard/Admin/GroupsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace App\Controller\Dashboard\Admin;

use App\Controller\Dashboard\AbstractCrudController;
use App\Http\ServerRequest;
use Doctrine\DBAL\Connection;

final class GroupsController extends AbstractCrudController
{
public function __construct(
Connection $db
) {
parent::__construct(
$db,
'Group',
'dashboard:admin:groups',
'dashboard/admin/groups/list',
'dashboard/admin/groups/edit',
'web_groups'
);
}

protected function modifyFromRequest(ServerRequest $request, array $row = [], bool $isEditMode = false): array
{
$postData = $request->getParsedBody();

if (!$isEditMode) {
$row['id'] = $postData['id'] ?? null;
if (empty($row['id'])) {
throw new \InvalidArgumentException('ID is required.');
}
}

$row['name'] = $postData['name'] ?? null;

if (empty($row['name'])) {
throw new \InvalidArgumentException('Name is required.');
}

return $row;
}
}
43 changes: 43 additions & 0 deletions backend/src/Controller/Dashboard/Admin/PosterTypesController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace App\Controller\Dashboard\Admin;

use App\Controller\Dashboard\AbstractCrudController;
use App\Http\ServerRequest;
use Doctrine\DBAL\Connection;

final class PosterTypesController extends AbstractCrudController
{
public function __construct(
Connection $db
) {
parent::__construct(
$db,
'Poster Type',
'dashboard:admin:poster_types',
'dashboard/admin/poster_types/list',
'dashboard/admin/poster_types/edit',
'web_poster_types'
);
}

protected function modifyFromRequest(ServerRequest $request, array $row = [], bool $isEditMode = false): array
{
$postData = $request->getParsedBody();

if (!$isEditMode) {
$row['id'] = $postData['id'] ?? null;
if (empty($row['id'])) {
throw new \InvalidArgumentException('ID is required.');
}
}

$row['name'] = $postData['name'] ?? null;

if (empty($row['name'])) {
throw new \InvalidArgumentException('Name is required.');
}

return $row;
}
}
2 changes: 1 addition & 1 deletion backend/src/Controller/Dashboard/Admin/UsersAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function __invoke(
$response,
'dashboard/admin/users',
[
'users' => $users,
'records' => $users,
]
);
}
Expand Down
Loading