As I was working on my Laravel app, I noticed that I was creating controllers' methods which were very similar. For example, I have models: Task, Block, Lesson, etc. And in every controller there are very similar methods:
public function index()
{
return new GeneralResource(/model/::all());
}
public function show(/model/ $model)
{
return new GeneralResource($model);
}
public function store(/model/Request $request)
{
try {
$model = /model/::create($request->validated());
return response()->json(['status' => 'success']);
} catch (\Exception $e) {
echo $e->getMessage();
}
}
public function update(/model/Request $request, /model/ $model)
{
try {
$model->update($request->validated());
return response()->json(['status' => 'success']);
} catch (\Exception $e) {
echo $e->getMessage();
}
}
public function destroy(/model/ $model)
{
try {
$model->delete();
return response()->json(['status' => 'success']);
} catch (\Exception $e) {
echo $e->getMessage();
}
}
Maybe I could extend Controller class with these methods and pass parameters as protected properties, but I am not sure how this can be done.
Please, help.
What you are looking for is called Template Method. You can do it for each method from the controllers :)
More info:
https://refactoring.guru/design-patterns/template-method/php/example
https://designpatternsphp.readthedocs.io/en/latest/Behavioral/TemplateMethod/README.html
Related
Particulars in Model
protected $allowedFields = ['page_title','page_status'];
public function store($data){
try {
if($this->insert($data,false)=== true){
return $this->getInsertID();
}else{
return $this->errors();
}
} catch (Exception $e) {
return $e->getMessage();
}
}
Particulars in Controller
$page_data_to_add=array('post_title'=>$page_title,'post_status'=>$page_status);
$data_added=$this->page->store($page_data_to_add);
Both these variables $page_title and $page_status has value
Trying to add data.Same function is working in other models and controller.
The problem in the controller data array
allowedfields are page_title,page_status
but in controller data array is different
$page_data_to_add=array('post_title'=>$page_title,'post_status'=>$page_status);
it will be
$page_data_to_add=array('page_title'=>$page_title,'page_status'=>$page_status);
I have a form when the user submits lostFound model. The validation request is as follows:
public function rules()
{
return [
'LFImage' => 'required|image|mimes:jpeg,jpg,png,gif,webp', // TODO: Read about 'Sometimes'.
'handoverStatement' => 'nullable|image|mimes:jpeg,jpg,png,gif,webp',
];
}
So, one attached is required (LFImage) and the other isn't (handoverStatement).
I created a dynamic image upload action:
class UploadImageAction implements UploadImageContract {
public function handle(Request $request, $image, $imageLocation)
{
$storedImage = ($request->hasFile($image))
? $request->file($image)->store($imageLocation)
: NULL;
if(!$storedImage)
{
throw new ImageUploadException('Something went wrong with the image upload.' . $storedImage);
}
return $storedImage;
}
}
Then calling it in the controller store() with try, catch:
// Add new Record.
public function store(LostFoundRequest $request, LostFoundService $lostFoundService, UploadImageContract $uploadImageAction)
{
try {
$LFImage = $uploadImageAction->handle($request, 'LFImage', 'lostFound/lostItems');
$handoverStatement = $uploadImageAction->handle($request, 'handoverStatement', 'lostFound/handoverStatements');
$lostFoundService->storeLostFound($request, $LFImage, $handoverStatement);
return redirect('data-entry/lost-and-found')->with('success', 'Item Added Successfully');
} catch (ImageUploadException $exception) {
// Handle upload image error
return back()->withErrors($exception->getMessage());
} catch (LostFoundException $exception) {
// Handle lostfound created error
return back()->withErrors('lostFound', $exception->getMessage());
} catch (\Throwable $exception) {
throw $exception;
}
}
Every time I submit the form without attaching handoverStatement I get the exception error (The data isn't saved, yet LFImage is uploaded to the dir).
What am I missing here?
Note: the ImageUploadException class doesn't have any methods:
<?php
namespace App\Exceptions;
use Exception;
class ImageUploadException extends Exception {}
As we know, we can define every RestApi in Laravel and we can implementing them into Route or Controllers, this below code is one of my simple RestApi which i want to convert that to GraphQl
public function loginAccount(RequestLoginAccount $request): JsonResponse
{
$user = User::where('mobile_number', $request->input('mobile_number'))->first();
if (!$user) {
return response()->json(['response' => false]);
}
try {
$user->notify(new LoginVerifyCode($user->mobile_number));
} catch (Exception $exception) {
return response()->json(['response' => -1]);
}
return response()->json(['response' => true]);
}
after creating simple resolver i have this query:
command:
php artisan lighthouse:query loginAccount
loginAccount query:
type Query {
///
loginAccount(mobile_number:String!, verify_code: String!): User
}
loginAccount resolver:
class LoginAccount
{
public function __invoke($_, array $args)
{
$user = User::where('mobile_number', $args['mobile_number'])->first();
if (!$user) {
return null;
}
/*try {
$user->notify(new LoginVerifyCode($user->mobile_number));
} catch (Exception $exception) {
return response()->json(['response' => -1]);
}
return response()->json(['response' => true]);*/
}
}
now could you help me to know whats equivalent of this RestApi in GraphQl? or is any custom response to define them?
I found how can i convert it from RestApi to GraphQl and it's resolved
i should be have custom type in schema:
type LoginResponce{
response:Int
}
custom GraphQl resolve should be has:
class LoginAccount
{
public function __invoke($_, array $args): array
{
$user = User::where('mobile_number', $args['mobile_number'])->first();
if (!$user) {
return ['response' => -1];
}
try {
$user->notify(new LoginVerifyCode($user->mobile_number));
} catch (Exception $exception) {
return ['response' => -2];
}
return ['response' => 1];
}
}
and query:
type Query {
//
loginAccount(mobile_number:String!): LoginResponce
}
GraphQl query:
query {
loginAccount(mobile_number: "00000") {
response
}
}
I can't find an explanation of why a return response() inside a catch is not stopping the execution, i guess im missing something.
In this example the request have an error so the try-catch on the service receive ValidationException. This goes to ValidationErrorResponder and here is where the execution should finish and return a json with the errors. But it continues and return the json error response through $this->updateUserResponder->respond()
I have a defined route which execute the __invoke() method on UpdateUserAction
class UpdateUserAction
{
protected $updateUserService;
protected $updateUserResponder;
public function __construct(UpdateUserService $updateUserService, UpdateUserResponder $updateUserResponder)
{
$this->updateUserService = $updateUserService;
$this->updateUserResponder = $updateUserResponder;
}
public function __invoke(Request $request, $userId)
{
$serviceData = [
'id' => $userId,
'commandPayload' => $request->only('name')
];
return $this->updateUserResponder->respond($this->updateUserService->execute($serviceData));
}
}
class UpdateUserService extends BaseService
{
public function execute(array $data = [])
{
try {
$this->bus->addHandler(UpdateUserCommand::class, UpdateUserHandler::class);
return $this->bus->dispatch(UpdateUserCommand::class, $data, [UpdateUserValidator::class]);
} catch (ValidationException $e) {
return $this->validationErrorResponder->respond($e);
}
}
}
class UpdateUserValidator implements Middleware
{
protected $rules = [
'id' => 'uuid',
'commandPayload.name' => 'max:256'
];
protected $messages = [];
public function execute($command, callable $next)
{
$validator = Validator::make((array) $command, $this->rules, $this->messages);
if ($validator->fails()) {
throw new ValidationException($validator);
}
return $next($command);
}
}
This shoudl return the final response wiht the errors in a JSON but
class ValidationErrorResponder
{
public function respond($validator)
{
$messages = $validator->getValidator()->getMessageBag()->messages();
return response()->json(['errors' => $messages], 422);
}
}
Maybe the error it's another and the catch does not working because only are catching ValidationException.
So try catching all exception to see what happens:
class UpdateUserService extends BaseService
{
public function execute(array $data = [])
{
try {
$this->bus->addHandler(UpdateUserCommand::class, UpdateUserHandler::class);
return $this->bus->dispatch(UpdateUserCommand::class, $data, [UpdateUserValidator::class]);
} catch (\Exception $e) {
return $this->validationErrorResponder->respond($e);
}
}
}
I am building an RESTful API in Laravel 5.2.
In my resource controllers I want to use implicit model binding to show resources. e.g.
public function show(User $users)
{
return $this->respond($this->userTransformer->transform($users));
}
When a request is made for a resource that doesn't exist Laravel automatically returns the NotFoundHttpException
NotFoundHttpException
I want to return my own custom response but how can I do that for a query that is done using route model binding?
Would something like this Dingo API response answer be able to be implemented?
Or will I stick with my old code which was something like this:
public function show($id)
{
$user = User::find($id);
if ( ! $user ) {
return $this->respondNotFound('User does not exist');
}
return $this->respond($this->userTransformer->transform($users));
}
So I could see if a resource (user) was not found and return an appropriate response.
See if you can catch ModelNotFound instead.
public function render($request, Exception $e)
{
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
dd('model not found');
}
return parent::render($request, $e);
}
I think a good place would be in the Handler.php file under /app/Exceptions
public function render($request, Exception $e)
{
if ($e instanceof NotFoundHttpException) {
// return your custom response
}
return parent::render($request, $e);
}
In Laravel 7 and 8 you can do something like this.
In app/Exception/Handler.php class, add the render() method like below(if it doesn't exist).
Note that instead of type hinting Exception class you should use Throwable .
use Throwable;
public function render($request, Throwable $e)
{
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
//For API (json)
if (request()->wantsJson()) {
return response()->json([
'message' => 'Record Not Found !!!'
], 404);
}
//Normal
return view('PATH TO YOUR ERROR PAGE');
}
return parent::render($request, $e);
}