feat: add update endpoint
This commit is contained in:
parent
f8467ada09
commit
c2e621859e
@ -22,6 +22,7 @@ $app->get('/', [HomeFrontController::class, 'home']);
|
||||
|
||||
$app->get('/tasks/{id}', [TaskController::class, 'getTask']);
|
||||
$app->delete('/tasks/{id}', [TaskController::class, 'delete']);
|
||||
$app->patch('/tasks/{id}', [TaskController::class, 'update']);
|
||||
|
||||
$app->get('/tasks', [TaskController::class, 'getTasks']);
|
||||
$app->post('/tasks', [TaskController::class, 'addTask']);
|
||||
|
@ -178,4 +178,87 @@ class TaskController
|
||||
return $errorResponse;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function update(ServerRequestInterface $request, ResponseInterface $response, $id): ResponseInterface
|
||||
{
|
||||
$payloadWarnings = [];
|
||||
|
||||
$toUpdate = $request->getParsedBody()['payload']['update'];
|
||||
$toUpdate['updated_at'] = Carbon::now()->toDateTimeString();
|
||||
|
||||
$acceptableFields = [
|
||||
'task_owner',
|
||||
'name',
|
||||
'description',
|
||||
'start',
|
||||
'end'
|
||||
];
|
||||
|
||||
foreach ($toUpdate as $updateField => $value)
|
||||
{
|
||||
if($updateField !== 'updated_at' && !in_array($updateField, $acceptableFields))
|
||||
{
|
||||
$payloadWarnings[] = "W: An invalid, unknown, or locked field has been truncated: " . $updateField;
|
||||
unset($toUpdate[$updateField]);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$task = $this->repository->readById($id);
|
||||
// if we used the dirty implementation, we could skip all this code
|
||||
|
||||
foreach($toUpdate as $property => $value)
|
||||
{
|
||||
// ignore updated_at
|
||||
if($property !== 'updated_at')
|
||||
{
|
||||
$task->{'set' . ucfirst(snakeToCamel($property))}($value);
|
||||
}
|
||||
}
|
||||
|
||||
$finalFields = array_keys($toUpdate);
|
||||
$result = $this->repository->updateSelective($task, $finalFields);
|
||||
|
||||
if ($result && empty($payloadWarnings))
|
||||
{
|
||||
return $response->withStatus(204);
|
||||
}
|
||||
elseif($result)
|
||||
{
|
||||
$this->builder->setOptionalMessage('Task updated successfully with warnings.')
|
||||
->setPayload([
|
||||
'updated' => $toUpdate,
|
||||
'warnings' => $payloadWarnings,
|
||||
]);
|
||||
$response->getBody()->write($this->builder->build());
|
||||
|
||||
return $response;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->builder->setError()
|
||||
->setErrorMessage('An unexpected error occurred while updating the resource.')
|
||||
->setErrorCode(500);
|
||||
|
||||
$errorResponse = $response->withStatus(500)->withHeader('Content-Type', 'application/json');
|
||||
$errorResponse->getBody()->write($this->builder->build());
|
||||
|
||||
return $errorResponse;
|
||||
}
|
||||
|
||||
}
|
||||
catch (TaskNotFoundException $e)
|
||||
{
|
||||
$this->builder->setError()
|
||||
->setErrorMessage($e->getMessage())
|
||||
->setErrorCode(404);
|
||||
|
||||
$errorResponse = $response->withStatus(404)->withHeader('Content-Type', 'application/json');
|
||||
$errorResponse->getBody()->write($this->builder->build());
|
||||
|
||||
return $errorResponse;
|
||||
}
|
||||
}
|
||||
}
|
@ -83,6 +83,49 @@ class TaskRepository implements TaskDao
|
||||
]);
|
||||
}
|
||||
|
||||
// fields = ['name', 'description']
|
||||
|
||||
// TODO: Abstract this implementation
|
||||
// TODO: Check for injection vectors; fields might be used to pollute the query. Fields aren't bindParam'able.
|
||||
public function updateSelective(Task $task, array $fields): bool
|
||||
{
|
||||
if (empty($fields))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$setClause = [];
|
||||
$params = [];
|
||||
|
||||
|
||||
foreach ($fields as $field)
|
||||
{
|
||||
// TODO: use the Dirty implementation. We can chop off the $fields param and still not trust that data.
|
||||
// Consider using a Trait to control this behavior (dirty or dynamic get: e.g Uses DirtyFields or Uses ManualUpdates)
|
||||
// we're discarding invalid properties here; if they give us --name;, this check will fail.
|
||||
// essentially, the Model is serving as our array of allowed fields.
|
||||
if (property_exists($task, snakeToCamel($field)))
|
||||
{
|
||||
$setClause[] = camel_to_snake($field) . " = ?"; // SET name = ?, SET updatedAt = ? (fixed by camel_to_snake)
|
||||
$params[] = $task->{'get' . ucfirst(snakeToCamel($field))}();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($setClause))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// we might have to update this and set the id later directly from the model
|
||||
$params[] = $task->getTaskId();
|
||||
|
||||
$query = "UPDATE Tasks SET " . implode(', ', $setClause) . " WHERE id = ?";
|
||||
$stmt = $this->conn->prepare($query);
|
||||
|
||||
return $stmt->execute($params);
|
||||
}
|
||||
|
||||
public function delete(Task $task): bool
|
||||
{
|
||||
$stmt = $this->conn->prepare('DELETE FROM Tasks WHERE id = ?');
|
||||
|
Loading…
x
Reference in New Issue
Block a user