I'm currently going through a small refactor with my roles, permissions and routes and asked myself the same question.
At the surface level, it appears true middleware and policies perform the same general idea. Check if a user can do what they are doing.
For reference here's the laravel docs...
Middleware
"May I see this? May I go here?"
HTTP middleware provide a convenient mechanism for filtering HTTP
requests entering your application. For example, Laravel includes a
middleware that verifies the user of your application is
authenticated. If the user is not authenticated, the middleware will
redirect the user to the login screen. However, if the user is
authenticated, the middleware will allow the request to proceed
further into the application.
Of course, additional middleware can be written to perform a variety
of tasks besides authentication. A CORS middleware might be
responsible for adding the proper headers to all responses leaving
your application. A logging middleware might log all incoming requests
to your application.
https://laravel.com/docs/master/middleware#introduction
In my reading, Middleware is about operating at the request level. In the terms of "Can this user see a page?", or "Can this user do something here?"
If so, it goes to the controller method associated with that page. Interestingly enough, Middleware may say, "Yes you may go there, but I'll write down that you are going." Etc.
Once it's done. It has no more control or say in what the user is doing. Another way I think of it as the middleperson.
Policies
"Can I do this? Can I change this?"
In addition to providing authentication services out of the box,
Laravel also provides a simple way to organize authorization logic and
control access to resources. There are a variety of methods and
helpers to assist you in organizing your authorization logic, and
we'll cover each of them in this document.
https://laravel.com/docs/master/authorization#introduction
Policies however, appear to be more concerned with doing. Can the user update any entry, or only theirs?
These questions seem fit for a controller method where all the calls to action on a resource are organized. Retrieve this object, store or update the article.
As tjbb mentioned, middleware can make routes very messy and hard to manage. This is an example from my routes file:
The problem
Route::group(['middleware' =>'role:person_type,person_type2',], function () {
Route::get('download-thing/{thing}', [
'as' => 'download-thing',
'uses' => 'ThingController@download'
]);
});
This gets very hard to read in my route file!
Another approach with policies
//ThingController
public function download(Thing $thing)
{
//Policy method and controller method match, no need to name it
$this->authorize($thing);
//download logic here....
}