10

It had been many months since I'm using laravel but never faced such problem.

I have made a simple Request class to validate the the update user request which works fine if validation rules are followed. If validation rule fails we should come back to the previous page and display all errors in html.

According to me I have written everything correctly as I used to write in other applications but the $errors seems to be inaccessible in blade

Following are my required code snippets to debug:

routes.php

Route::group(['middleware' => ['web']], function () {
    Route::get('/users/{id}/edit', 'UserController@edit');
    Route::post('/users/{id}/edit', 'UserController@update');
});

UserController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Requests\UserUpdateRequest;
use App\Models\User;
use App\Models\Role;
use App\Models\Post;

class UserController extends Controller
{    
    public function edit($id)
    {
        try {
            $user = User::find($id);
            $roles = Role::all();
            return view('users.edit', compact(['user', 'roles']));
        }catch(Exception $e) {
            return view('errors.500', compact(['e']));
        }
    }

    public function update($id, UserUpdateRequest $request)
    {
        dd($request);
    }
}

UserUpdateRequest.php

<?php

namespace App\Http\Requests;

use App\Http\Requests\Request;

class UserUpdateRequest extends Request
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name'      =>  'required|string|min:4',
            'email'     =>  'required|email',
            'role'      =>  'required|numeric',
            'password'  =>  'required',
        ];
    }
}

edit.blade.php

@extends('master')

@section('title') Edit Users @stop

@section('content')
<div class="row">
    <div class="col-sm-12">
        <h2>Edit User</h2>
    </div>
</div>
<div class="alert alert-warning alert-dismissible" role="alert">
  @foreach($errors->all() as $error)
  {{ $error }}
  @endforeach
</div>
<form action="{{ url('/users/'.$user->id.'/edit') }}" method="post">
    <input type="hidden" name="_token" value="{{ csrf_token() }}">
    <div class="col-sm-6">
        <div class="form-group">
            <label>Name</label>
            <input type="text" name="name" value="{{ $user->name }}" class="form-control" placeholder="Name">
        </div>
    </div>
    <div class="col-sm-6">
        <div class="form-group">
            <label>Email Address</label>
            <input type="text" name="email" value="{{ $user->email }}" class="form-control" placeholder="Email Address">
        </div>
    </div>
    <div class="col-sm-6">
        <div class="form-group">
            <label>Role</label>
            <select name="role" class="form-control">
            @foreach($roles as $role)
            @if($role->id == $user->role)
            <option value="{{ $role->id }}" selected>{{ $role->name }}</option>
            @else
            <option value="{{ $role->id }}">{{ $role->name }}</option>
            @endif
            @endforeach
            </select>
        </div>
    </div>
    <div class="col-sm-6">
        <div class="form-group">
            <label>Password</label>
            <input type="password" name="password" class="form-control" placeholder="New Password">
        </div>
    </div>
    <div class="col-sm-12">
        <div class="form-group">
            <input type="submit" class="btn btn-info btn-block" value="Update">
        </div>
    </div>
</form>
@stop

The HTML response on browser is blank. I also tried <?php dd($errors); ?> which displayed the following

Edit User

ViewErrorBag {#168 ▼
  #bags: []
}

More info here

Cybersupernova
  • 1,833
  • 1
  • 20
  • 37

2 Answers2

6

@VipindasKS is right with his assumption. Since Laravel Version 5.2.28 the web middleware is included in all routes via the RouteServiceProviders's method:

protected function mapWebRoutes(Router $router)
{
    $router->group([
        'namespace' => $this->namespace, 'middleware' => 'web',
    ], function ($router) {
        require app_path('Http/routes.php');
    });
}

Since that version Laravel's default routes.php file only contains:

Route::get('/', function () {
    return view('welcome');
});

So if you upgrade from a previous version, that has a routes.php file like this:

Route::group(['middleware' => ['web']], function () {
   // web routes
});

Your application will just work fine, because with an composer update you won't touch your RouteServiceProvider (It does not add the mapWebRoutes() method). So the 'web' middleware is only added to the routes within the 'web' group'.

However if you are pulling a fresh installation of Laravel ( currently 5.2.29 ) and have a routes.php with

Route::group(['middleware' => ['web']], function () {
   // web routes
});

The web middleware stack will be added twice to the routes. You can check this via:

php artisan route:list

Which will show that the 'web' middleware is added twice:

| POST      | users/{id}/edit          |                  | App\Http\Controllers\UserController@update      | web,web    |

This breaks the Session's flash variables as they are normally only intended to last only during one session lifecycle.

So the solution is:

Don't use the 'web' middleware group in the routes.php file if you pulled a fresh instance of laravel.

shock_gone_wild
  • 6,700
  • 4
  • 28
  • 52
  • Thanks this worked. When I pulled `5.2.29` I thought I have to add `web` middleware manually and i created the problem myself – Cybersupernova Apr 18 '16 at 10:56
1

You may want to use withErrors redirect, in case validation fails

    if ($validator->fails()) {
        return redirect()
            ->route('route.here')
            ->withErrors($validator)
            ->withInput();
    }

Also, please check

  \Illuminate\View\Middleware\ShareErrorsFromSession::class,

is there in 'web' middleware in app/Http/Kernel.php

so your kernel.php should look something like:

/**
 * The application's route middleware groups.
 *
 * @var array
 */
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
    ],
    'api' => [
        'throttle:60,1',
    ],
];

If that too doesn't work, you can move the

  \Illuminate\View\Middleware\ShareErrorsFromSession::class,

to the global middleware. (just to try. I won't suggest)

** Make sure sessions work. To have errors returned or to flash messages to the browser, you need to have a session running.

From 5.2, sessions only start if you specify that the route should use the 'web' middleware (which is already done by you in the routes.php).

And, From 5.2.28, web middleware is automatically included in all routes, you can see this in the RouteServiceProvider. so, we don't want to specify a 'web' middleware to the routes.php or in controller unless we have a custom middleware. But, not sure this caused the problem.

VipindasKS
  • 516
  • 7
  • 15
  • As suggested It worked on moving `\Illuminate\Session\Middleware\StartSession::class` and `\Illuminate\View\Middleware\ShareErrorsFromSession::class` and everything started working but I need to know why this is happening. There must be something wrong in routes.php – Cybersupernova Apr 15 '16 at 15:42
  • @Cybersupernova as it is mentioned in my answer the error messages works only if there is session. Global middleware is something which every request is passed through. As you have mentioned 'web' as your middleware group in the routes.php, the web middleware should contain the lines related to session. Please learn more about group routing [here](https://laravel.com/docs/5.2/routing#route-groups) – VipindasKS Apr 16 '16 at 01:26
  • yes that's absolutely correct but me `$middlewareGroups` array is exactly as you have mentioned in your answer. We also can not consider that routes are not passing though that group as if I don't give the `csrf_token` in the form then `\App\Http\Middleware\VerifyCsrfToken::class` middleware throws its exception `TokenMismatchException in VerifyCsrfToken.php line 67`. So why the session middlewares are not working? – Cybersupernova Apr 16 '16 at 01:44
  • I also want to mention that `return view('some.view)->withErrors('My Custom Error')` works fine and `$errors` is accessible in blade as I mentioned here https://github.com/laravel/framework/issues/13145 – Cybersupernova Apr 16 '16 at 01:45
  • @Cybersupernova Can you please post the out put after commenting use App\Http\Requests; in your UserController.php – VipindasKS Apr 18 '16 at 03:10
  • Nothing changes. It gives errors on `login.blade.php` if session middlewares are in `$middleware` and gives just the `login.blade.php` view with no errors when session middlewares are in `$middlewareGroups`. I have not commented ant of my request classes like 'LoginRequest.php`. If I do so only class not found exception is thrown in both cases of session middlewares – Cybersupernova Apr 18 '16 at 07:55
  • weird! can you please try with composer update. is it a version bug? – VipindasKS Apr 18 '16 at 08:02
  • Indeed, this seems to be a version bug... If you upgrade a previously installed laravel version to 5.2.29 it is working well. But if you directly do a composer create-project laravel/laravel --prefer-dist The behavior is exactly as the OP stated... – shock_gone_wild Apr 18 '16 at 08:42
  • From 5.2.28, web middleware is automatically included in all routes, you can see this in the [RouteServiceProvider](https://github.com/laravel/laravel/blob/master/app/Providers/RouteServiceProvider.php). so, we don't want to specify a 'web' middleware to the routes.php or in controller unless we have a custom middleware. But, not sure this caused the problem. I have updated the answer with this point added. – VipindasKS Apr 18 '16 at 09:47
  • @VipindasKS seems like you are right! Maybe you should submit a bug report? Or at least a request to update the 'upgrade' guide? – shock_gone_wild Apr 18 '16 at 10:48