Search code examples
phplaravellaravel-middleware

Adding custom data to middleware response in laravel 9 or 10 php


I want to implement a user tracking system, after searching I realized that I have to do this in the middleware. The problem I had was that I could only add values to the response header in the answers that were in the middleware. And after saving their information, delete them in the middleware response. Please guide me how to add custom data to the middleware response, thanks

For this, I found 2 examples that work in json mode, but in the case that I want to transfer the user to the route, I have to use headers.

Sample code for json response:

return $response->json([
                    'status' => $status,
                    'code' => $code,
                    'errors' => [$errors],
                ], $status);

Solution

  • I did exactly the same system in my project and without using headers, please see the example below

    for example: We plan to create a tracking system for each user's performance in the software so that its reports are stored in the system.

    Migration:

    Schema::create('user_trackings', function (Blueprint $table) {
                $table->id();
                $table->boolean('transaction')->default(1);
                $table->string('event');
                $table->string('class');
                $table->longText('request_params')->nullable();
                $table->string('request_method');
                $table->string('request_ip');
                $table->string('request_userAgent');
                $table->longText('params')->nullable();
                $table->foreignId('user_id')->constrained()->onUpdate('cascade')->onDelete('cascade');
                $table->timestamps();
                $table->softDeletes();
            });
    

    RoleController:

    use App\Traits\Response;
    
    class RoleController extends Controller
    {
        use Response;
    
        public function store(Request $request)
        {
            $Validator = Validator::make($request->all(), [
                'display_name' => ['required', 'unique:roles,display_name', 'max:255'],
                'name' => ['required', 'unique:roles,name', 'persian_not_accept', 'max:255'],
                'permissions' => ['nullable', "array","min:1"],
                'permissions.*' => ['nullable', 'exists:permissions,name'],
                'parent' => [new ParentRule('roles,name')]
            ]);
            if ($Validator->fails()) {
                $this->Validation($Validator);
                return back();
            }
    
            DB::beginTransaction();
            try {
                $Role = Role::create([
                    'name' => $request->name,
                    'display_name' => $request->display_name
                ]);
                $Role->syncPermissions($request->permissions);
                if($request->parent != 'no-parent'){
                    $RoleParent = Role::findByName($request->parent);
                    $Role->parent()->associate($RoleParent);
                    $Role->save();
                }
    
                DB::commit();
            } catch (\Exception $e) {
                DB::rollback();
                $this->Error([
                    'Permission' => $Role,
                    'Exception' => $e->getMessage()
                ]);
            }
            return $this->Success([
                'Permission' => $Role
            ], 'role.index');
        }
    
    }
    

    ResponseTrait:

    trait Response {
        use SweetAlert;
    
        public function Success($Params = [], $Route = null , $RouteParams = [], $title = null, $message = null){
            $title = is_null($title) ? __('message.response.title.Success') : $title;
            $message = is_null($message) ? __('message.response.message.Success') : $message;
    
            if($Route){
                return (new RedirectResponse(route($Route, $RouteParams)))->bag(true, $Params);
            }
    
            return (new HttpResponse)->json(['status' => true, 'message' => $message])->bag(true, $Params);
        }
    
        public function Error($Params = [], $Route = null , $RouteParams = [], $title = null, $message = null){
            $title = is_null($title) ? __('message.response.title.Error') : $title;
            $message = is_null($message) ? __('message.response.message.Error') : $message;
    
            if($Route){
                $this->AlertError( $title , $message );
                return (new RedirectResponse(route($Route, $RouteParams)))->bag(false, $Params);
            }
    
            return (new HttpResponse)->json(['status' => false, 'message' => $message])->bag(false, $Params);
        }
    
    }
    

    tip: In the middleware response, you will receive 2 types of responses, which are made of 2 classes (Illuminate\Http\Response, Illuminate\Http\RedirectResponse), so we must create 2 custom classes for this purpose and extend these 2 classes.

    Create Custom Response:

    1- Class One:

    namespace App\Library\Response;
    
    use Illuminate\Http\RedirectResponse as BaseRedirectResponse;
    
    class RedirectResponse extends BaseRedirectResponse {
    
        public $bag;
    
        public function bag($Status = null, $Data = null){
            $this->bag = (object) [
                'Status' => $Status,
                'Data' => $Data
            ];
            return $this;
        }
    }
    
    

    2- Class Two:

    namespace App\Library\Response;
    
    use Illuminate\Http\Response;
    
    class HttpResponse extends Response {
    
        public $bag;
    
        public function bag($Status = null, $Data = null){
            $this->bag = (object) [
                'Status' => $Status,
                'Data' => $Data
            ];
            return $this;
        }
    }
    

    We create the User Tracking storage trait

    namespace App\Traits;
    
    use Illuminate\Support\Facades\DB;
    use Illuminate\Support\Facades\Log;
    use Illuminate\Support\Facades\Auth;
    use Illuminate\Support\Facades\Route;
    use App\Models\UserTracking as ModelUserTracking;
    
    trait UserTracking{
    
        public function Track($Transaction = true, $Params = []){
            DB::beginTransaction();
            try {
                $UserTracking = new ModelUserTracking;
                $UserTracking->transaction = $Transaction;
                $UserTracking->event = Route::currentRouteName();
                $UserTracking->class = Route::currentRouteAction();
                $UserTracking->request_params = json_encode(request()->all());
                $UserTracking->request_method = Route::current()->methods()[0];
                $UserTracking->request_ip = request()->ip();
                $UserTracking->request_userAgent = request()->userAgent();
                if(!empty($Params)): $UserTracking->params = json_encode($Params); endif;
                $UserTracking->User()->associate(Auth::user());
                $UserTracking->save();
    
                DB::commit();
            } catch (\Exception $e) {
                DB::rollback();
    
                Log::error("Insert User Tracking Data Error", [
                    'UserTracking' => $UserTracking,
                    'Exception' => $e->getMessage()
                ]);
            }
        }
    
    }
    

    Now we go to build our custom middleware

    Custom middleware:

    namespace App\Http\Middleware;
    
    use Closure;
    use App\Traits\UserTracking as TraitUserTracking;
    use Illuminate\Http\Request;
    
    class UserTracking
    {
        use TraitUserTracking;
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure(\Illuminate\Http\Request): (\App\Library\Response\HttpResponse|\App\Library\Response\RedirectResponse)  $next
         * @return \App\Library\Response\HttpResponse|\App\Library\Response\RedirectResponse
         */
        public function handle(Request $request, Closure $next)
        {
            $response = $next($request);
    
            if(property_exists($response, 'bag'))
                $this->Track($response->bag->Status, $response->bag->Data);
    
            return $response;
        }
    }
    
    

    The point of this guide is that you should create a custom class of your response and extend it from (Illuminate\Http\Response, Illuminate\Http\RedirectResponse) classes. With this trick, you can send and process any value you like. Good luck