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->get('/tasks/{id}', [TaskController::class, 'getTask']);
|
||||||
$app->delete('/tasks/{id}', [TaskController::class, 'delete']);
|
$app->delete('/tasks/{id}', [TaskController::class, 'delete']);
|
||||||
|
$app->patch('/tasks/{id}', [TaskController::class, 'update']);
|
||||||
|
|
||||||
$app->get('/tasks', [TaskController::class, 'getTasks']);
|
$app->get('/tasks', [TaskController::class, 'getTasks']);
|
||||||
$app->post('/tasks', [TaskController::class, 'addTask']);
|
$app->post('/tasks', [TaskController::class, 'addTask']);
|
||||||
|
@ -178,4 +178,87 @@ class TaskController
|
|||||||
return $errorResponse;
|
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
|
public function delete(Task $task): bool
|
||||||
{
|
{
|
||||||
$stmt = $this->conn->prepare('DELETE FROM Tasks WHERE id = ?');
|
$stmt = $this->conn->prepare('DELETE FROM Tasks WHERE id = ?');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user