Laravel передача данных из посредника в контроллер (IoC)

212
12 февраля 2019, 22:50

Появилась нужда из посредника (middleware) при НЕ прохождении проверки редиректить с определёнными параметрами в контроллер. Никогда с IoC ещё не сталкивался и решил его использовать:

middleware:

public function handle($request, Closure $next)
{
    $nowUserIp = $request->ip();
    $findIps = UserIp::where('ip', $nowUserIp)->get();
    if ($findIps->isEmpty()) {
        $newUserIp = new UserIp();
        $newUserIp->ip = $nowUserIp;
        auth()->user()->UserIp()->save($newUserIp);
    }
    foreach ($findIps as $findIp) {
        $hoursDiff = Carbon::now()->diffInHours($findIp->created_at, true);
        if ($findIp->user_id != auth()->user()->id && $hoursDiff < 24) {
            return redirect()->route('ip-ban');
        }
    }
    return $next($request);
}

controller (куда в случае пойдёт /redirect. Сейчас с повторением кода, IoC не получается. Нужно передать из middleware $findIps = UserIp::where...):

public function index(Request $request)
{
    $nowUserIp = $request->ip();
    $findIps = UserIp::where('ip', $nowUserIp)->get();
    foreach ($findIps as $findIp) {
        $hoursDiff = Carbon::now()->diffInHours($findIp->created_at, true);
        if ($findIp->user_id != auth()->user()->id && $hoursDiff < 24) {
            return 'Access Denied! Your ip is banned: ' . $findIp->ip;
        }
    }
     return redirect('/');
}

Ничего не происходит, что я делаю не так? Пробовал способ из этого вопроса https://stackoverflow.com/questions/30212390/laravel-middleware-return-variable-to-controller/33297725#33297725 (Ответ crishoj) Ничего не выходит...

Answer 1

Варианты:

1) Сохранить данные в сессию

RedirectResponse

Если передавать данные для роута который в url, то там нельзя передавать сложные данные, например, экземпляр модели или коллекцию.

К счастью, вместе с редиректом можно передать данные в сессию Flash Data(данные хранятся только для следующего запроса):

return redirect()->route('ip-ban')->with(['data' => $data] );

Получить можно так:

session('data');

Конечно, это работает для всех видов редиректов (через экземпляр RedirectResponse ), это тоже будет работать:

return redirect()->action(
    'TestController@testAction', 
    ['data' => 123] // данные роута
)->with(['data' => $users]); // данные в сессию

Документация.

2) Вызвать действие контроллера самому

В Middleware можно вызвать action нужного контроллера передав ему нужные данные.

Код тестировался с передачей коллекции Eloquent, значит скорее всего можно передавать что угодно.

Работал следующий код:

routes/web.php

Route::middleware('redirect')->group(function () {
    Route::get('/test', 'TestController@testAction');
});
Route::get('/test2', 'TestController@testAction2');

Middleware "redirect" (упустил код добавления в Kernel.php, не знаете как добавлять - смотрите документацию)

public function handle($request, Closure $next)
{
    $users = \App\User::all();
    $controller = app()->make(\App\Http\Controllers\TestController::class);
    // передается массив параметров, `$users` попадет в первый параметр `$param`
    $controller->callAction('testAction2', [$users]);
}

Контроллер TestController:

namespace App\Http\Controllers;
class TestController extends Controller
{
    public function testAction()
    {
        return 'этот action не должен сработать';
    }
    public function testAction2($param)
    {
        // вывелась коллекция пользователей
        dd($param);
    }
}

На самом работает и без контейнера зависимостей, можно просто:

$con = new \App\Http\Controllers\TestController;
$con->callAction('testAction2', [$users]);

Пишите какие еще способы знаете.

Answer 2

В документации и в исходниках instance примаент первый параметр как строку: https://github.com/laravel/framework/blob/5.7/src/Illuminate/Container/Container.php#L391

Попробуйте этот вариант:

// свзязывает экземпляр с контейнером
app()->instance('userIp', $findIp);

Дальше в контейнере просто получите этот экземпляр. Вместо того чтобы писать

$this->findIp = app()->make('userIp');

Можно

$this->findIp = resolve('userIp');

Почему там в ответе используется "route model binding" если маршрут тут не причем я не знаю, по идее оно не должно работать.

READ ALSO
Ошибка прайс-листа в валидаторе Yandex XML

Ошибка прайс-листа в валидаторе Yandex XML

*Возникшие ошибки: Error parsing XML feed: Not allowed value &quot;THB&quot; for the attribute &quot;id&quot;: XML tag &quot;currency&quot;Столбец 37 Строка 9

192
Проверка данных на входе

Проверка данных на входе

Ребята подскажите пожалуйстаЕсть задача, сделать веб

176
Ближайшее большее число

Ближайшее большее число

Есть такой код:

167
Загрузка видео по требованию

Загрузка видео по требованию

Мне необходимо начинать загрузку видео с сервера на странице после того как пользователь доскролит до этого местаВидео на страницу вставляются...

197