Laravel: защита от повторного добавления данных при обновлении страницы

Ромчик
0

laravelДоброго времени суток. В данной статье хочу поделиться одним методом, который позволяет нам защититься от повторного добавления данных при обновлении страницы. Данный способ защиты подходит не только для Laravel. Но мы будем рассматривать защиту от повторного добавления в контексте Laravel. Дальше мы рассмотрим основной принцип проверки, а затем напишем собственное правило валидации.

И так приступим. Первым делом подготовимся. Создадим таблицу users в базе данных со следующими полями:

  • id
  • first_name
  • last_name
  • created_at
  • updated_at

Вот дамп:


CREATE TABLE IF NOT EXISTS `users` (

`id` int(11) NOT NULL,

`first_name` varchar(255) NOT NULL,

`last_name` varchar(255) NOT NULL,

`created_at` datetime NOT NULL,

`updated_at` datetime NOT NULL

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `users`

ADD PRIMARY KEY (`id`);

ALTER TABLE `users`

MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

Теперь создадим модель User:


<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model

{

protected $table = 'users';

protected $fillable = [

'first_name', 'last_name',

];

}

И не забываем прописать настройки для подключения к базе данных.

Теперь создаем контроллер, который будет содержат три метода: первый метод будет вызывать вьюшку с формой, второй будет принимать пост запросы и передавать данные в модель для записи в базу, а третий – будет получать из модели записи и передавать их для отображения во вьюшку.

<?php
    namespace App\Http\Controllers;
    use Illuminate\Http\Request;
    use App\User;
    class MainController extends Controller
    {
        public function index()
        {
            $list = User::all();
            return view('index',['list' => $list]);
        }

        public function getForm()
        {
            return view('form');
        }

        public function postForm(Request $request)
        {
            User::create(['first_name' => $request->firstName, 'last_name' => $request->lastName]);
            echo "Добавлено";
        }
}

Отлично, осталось добавить две вьюшки. Вьюшка для отображения данных: index.blade.php

<!DOCTYPE html>
<html>
    <head>
        <title>Laravel</title>
        <link href="https://fonts.googleapis.com/css?family=Lato:100" rel="stylesheet" type="text/css">
        <style>
            html, body {
                height: 100%;
            }

            body {
                margin: 0;
                padding: 0;
                width: 100%;
                display: table;
                font-weight: 100;
                font-family: 'Lato';
            }

            .container {
                text-align: center;
                display: table-cell;
                vertical-align: middle;
            }

            .content {
                text-align: center;
                display: inline-block;
            }

            .title {
                font-size: 96px;
            }
        </style>
    </head>
<body>
<div class="container">
    <div class="content">
        @if($list)
            <table>
                <tr>
                    <th>Имя</th>
                    <th>Фамилия</th>
                </tr>
                @foreach($list as $l)
                    <tr>
                        <td>{{ $l->first_name }}</td>
                        <td>{{ $l->last_name }}</td>
                    </tr>
                @endforeach
            </table>
        @endif
    </div>
</div>
</body>
</html>

И вьюшка для формы: form.blade.php


<!DOCTYPE html>
<html>
    <head>
        <title>Laravel</title>
        <link href="https://fonts.googleapis.com/css?family=Lato:100" rel="stylesheet" type="text/css">
        <style>
            html, body {
                height: 100%;
            }

            body {
                margin: 0;
                padding: 0;
                width: 100%;
                display: table;
                font-weight: 100;
                font-family: 'Lato';
            }

            .container {
                text-align: center;
                display: table-cell;
                vertical-align: middle;
             }

             .content {
                 text-align: center;
                 display: inline-block;
             }

             .title {
                 font-size: 96px;
             }
         </style>
     </head&gt;
<body>
<div class="container">
    <div class="content">
        <form method="POST" action="{{ route('add') }}">
            <input name="_token" type="hidden" value="{{ csrf_token() }}">
             <label>Имя:</label>
             <input type="text" name="firstName"><br /><br />
             <label>Фамилия:</label>
             <input type="text" name="lastName"><br /><br />
             <input type="submit" value="Сохранить">
         </form>
     </div>
</div>
</body>
</html>

Теперь, чтобы все это заработало необходимо добавить роуты:

    Route::get('/', ['as' =>; 'home', 'uses' => 'MainController@index']);
    Route::get('add', ['as' => 'form', 'uses' => 'MainController@getForm']);
    Route::post('add', ['as' => 'add', 'uses' => 'MainController@postForm']);

Проверяем. Адрес моего хоста http://lesson.loc/
Данных нет.
01Переходим на форму для добавления http://lesson.loc/add
02
Добавляем данные.
И смотрим, что получилось http://lesson.loc/
03
Работает.
Давайте посмотрим в чем заключается проблема. Переходим на форму добавления и вводим, например, Имя: имя-2, Фамилия: фам-2 и жмем «Сохранить» появилась страница.
04Теперь обновим страницу (нажмем F5). Подтверждаем отправку. И переходим к списку:
05
И что видим. У нас данные добавились два раза.
Один из самых простых способов – это добавить в форму метку (time()).
Открываем вьюшку с формой.
И добавляем:

    <input name="_check" type="hidden" value="{{ time() }}">

А в методе, который обрабатывает пост запросы от формы, добавляем обработку. Вот как наш метод:

    public function postForm(Request $request)
    {
        if(!$request->session()->has('check')){
            $request->session()->set('check',$request->_check);
            $request->session()->save();
            $check = true;
        } else {
            if($request->_check != $request->session()->get('check')){
                $request->session()->set('check',$request->_check);
                $request->session()->save();
                $check = true;
            } else {
                $check = false;
            }
        }
        if(!$check){
            return redirect()->route('home');
        }
        User::create(['first_name' => $request->firstName, 'last_name' => $request->lastName]);
        echo "Добавлено";
    }

Решения проблем с сессиями в Laravel смотри у меня в статье «Фишки Laravel: не сохраняются сессии»
Проверяем и видим, что нас перекидывает на главную страницу, когда мы обновляем страницу (второй раз данные не добавляются)
06
Отлично. Все работает. Но мы же используем Laravel. И наша проверка выглядит как-то не «кашерно». Давайте создадим новое правило валидации.
Для этого создадим файл CustomValidator.php в app следующего содержания:

<?php
    namespace App;
    use Illuminate\Validation\Validator;
    use Session;

    class CustomValidator extends Validator {
        public function validateReinclusion($attribute, $value, $parameters)
        { 
            if(!session($attribute)){
                session([$attribute => $value]);
                return true;
            } else {
                if($value != session($attribute)){
                    session([$attribute => $value]);
                    return true;
                } else {
                    return false;
                }
            }
        }
    }

Где:

  • $attribute – название поля
  • $value – значение
  • $parameters – параметры, которые передаются

Теперь зарегистрируем новое правило. Для этого открываем AppServiceProvider.php, который находится в app/Providers и вносим в метод boot код

    Validator::resolver(function($translator, $data, $rules, $messages)
    { 
        return new CustomValidator($translator, $data, $rules, $messages); 
    });

Вот код файла AppServiceProvider.php:

<?php
    namespace App\Providers;
    use Illuminate\Support\ServiceProvider;
    use Validator;
    use App\CustomValidator;

    class AppServiceProvider extends ServiceProvider
    {
    /**
    * Bootstrap any application services.
    *
    * @return void
    */
    public function boot()
    {
        Validator::resolver(function($translator, $data, $rules, $messages)
        { 
            return new CustomValidator($translator, $data, $rules, $messages); 
        });
    }
    /**
    * Register any application services.
    *
    * @return void
    */
    public function register()
    {
        //
    }
}

Ну вот и все. Мы создали собственное правило валидации и зарегистрировали его. Проверим.

Переходим в наш контроллер MainController.php и изменим его (добавим новое правило)

<?php
    namespace App\Http\Controllers;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Validator;
    use App\User;
    class MainController extends Controller
    {
       public function index()
       {
           $list = User::all();
           return view('index',['list' => $list]);
       }
       public function getForm()
       {
           return view('form');
       }
       public function postForm(Request $request)
       {
           $valid = Validator::make($request->all(),[
               '_check' => 'reinclusion',
           ]);
           if($valid->fails()){
               return redirect()->route('home');
           } 
           User::create(['first_name' => $request->firstName, 'last_name' => $request->lastName]);
           echo "Добавлено";
       }
}

Проверяем. Работает.
P.S. Мы с вами сделали проверку на повторное добавление данных при обновлении страницы. Затем создали собственное правило валидации в Laravel, зарегистрировали его.

Понравилась статья? Поделись с друзьями.
  • Add to favorites
  • Добавить ВКонтакте заметку об этой странице
  • Twitter
  • Facebook
  • Мой Мир
  • LiveJournal
  • Одноклассники
  • Блог Я.ру
  • MySpace
  • FriendFeed
  • В закладки Google
  • Google Buzz
  • Яндекс.Закладки
  • Reddit
  • StumbleUpon
  • Technorati
  • del.icio.us
  • БобрДобр
  • LinkedIn
  • Memori.ru
  • Сто закладок
  • Blogger

©2012-2020 По всем вопросам обращайтесь через форму обратной связиПолитика конфиденциальности

Яндекс.Метрика