How to keep DRY while creating common controllers in Laravel? - php

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

There is no data to insert. in SYSTEMPATH\BaseModel.php on line 729

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);

Laravel Exceptions: throwing error when not attaching nullable input file

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 {}

Laravel converting RestApi to GraphQl query

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
}
}

Laravel return response() not stop the execution

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);
}
}
}

How to return custom API response for "No query results for model", Laravel

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);
}

Categories