rondevz liked a comment+100 XP
3mos ago
@jwmcpeak I tested with all endpoints and then made a versioned ApiExceptions. Don't guess it really needs to be versioned.
Note apparently in Laravel 11 when using route model binding a ModelNotFoundException gets translated into a NotFoundHttpException so I have them both using the same handler.
app.php
->withExceptions(function (Exceptions $exceptions) {
$exceptions->render(function (Throwable $e, Request $request) {
$className = get_class($e);
$handlers = ApiExceptions::$handlers;
if (array_key_exists($className, $handlers)) {
$method = $handlers[$className];
return ApiExceptions::$method($e, $request);
}
return response()->json([
'error' => [
'type' => basename(get_class($e)),
'status' => intval($e->getCode()), // returns 0 if no code
'message' => $e->getMessage()
]
]);
});
})->create();
ApiExceptions.php
<?php
namespace App\Exceptions\Api\V1;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class ApiExceptions
{
public static array $handlers = [
AuthenticationException::class => 'handleAuthenticationException',
ValidationException::class => 'handleValidationException',
ModelNotFoundException::class => 'handleNotFoundException',
NotFoundHttpException::class => 'handleNotFoundException',
];
public static function handleAuthenticationException(AuthenticationException $e, Request $request): JsonResponse
{
// log that sensitive stuff
// should move this out to custom logger
$source = 'Line: ' . $e->getLine() . ', File: ' . $e->getFile();
Log::notice(basename(get_class($e)) . ' - ' . $e->getMessage() . ' - ' . $source);
return response()->json([
'error' => [
'type' => basename(get_class($e)),
'status' => 401,
'message' => $e->getMessage()
]
]);
}
public static function handleValidationException(ValidationException $e, Request $request): JsonResponse
{
foreach ($e->errors() as $key => $value)
foreach ($value as $message) {
$errors[] = [
'type' => basename(get_class($e)),
'status' => 422,
'message' => $message,
];
}
return response()->json([
'errors' => $errors
]);
}
public static function handleNotFoundException(ModelNotFoundException|NotFoundHttpException $e, Request $request): JsonResponse
{
return response()->json([
'error' => [
'type' => basename(get_class($e)),
'status' => 404,
'message' => 'Not Found ' . $request->getRequestUri()
]
]);
}
}
rondevz liked a comment+100 XP
3mos ago
To improve code readability, you can use resolveRouteBinding in the ticket model, where you can check if a path parameter named 'author' is being received to then query the ticket with it
public function resolveRouteBinding($value, $field = null) {
$ticket = $this::query()->where('id', $value);
if (request()->route()->hasParameter('author')) {
$ticket->where('user_id', request()->route('author'));
}
return $ticket->firstOrFail();
}
then, in your controllers you can just use route model binding as usual and resolveRouteBinding will make sure you get the correct ticket
public function replace(string $author_id, Ticket $ticket, ReplaceTicketRequest $request) {
$this->isAble('replace', $ticket);
$ticket->update($request->mappedAttributes());
return new TicketResource($ticket);
}
this will work for both TicketsController and AuthorTicketsController
rondevz liked a comment+100 XP
3mos ago
@sadhakbj I agree with you for the most part. I build applications on various platforms with various languages and frameworks, and my philosophy is to build using the tools provided by the environment/platform/framework. With saying that, I know a lot of developers that eschew the hidden "magic" that a framework provides and prefers to see it all done in the controller. One thing I've learned throughout my career is everyone has a different definition of clean.
rondevz wrote a comment+100 XP
4mos ago
In the seeder, I think you can also use:
Task::factory(10)
->for($user)
->create();
rondevz liked a comment+100 XP
5mos ago
It seems you made a typo that you correct before showing the result but after the last screen code:
$rows[$prompt->index] = collect($rows[$prompt->index]) // ...
This first $rows takes an s otherwise, it does not works!