Динамичные input в форме (Laravel + jQuery)
В этой заметке разберемся как добавлять динамически генерируемые поля input в форму. В качестве примера выполним следующую задачу, добавим список ссылок на «полезные ресурсы по теме» к новостям из примера, который мы сделали в статье про работу с изображениями в Laravel.
Заодно вспомним как использовать поля JSON в Laravel.
Напомню, наш шаблон формы использует Bootstrap 4 и jQuery.
Добавим к нашей таблице в базе данных со списком новостей поле links типа JSON, для этого создадим миграцию:
php artisan make:migration add_links_to_news_table --table=news
В файле миграции database/migrations/XXXX_XX_XX_XXXX_add_links_to_news_table.php
в методе up() добавим поле links:
Schema::table('news', function (Blueprint $table) {
$table->json('links')->default('[]');
});
«->default('[]')
» — этим мы указали, что по умолчанию в поле будет пустой массив.
Выполняем миграцию php artisan migrate
Далее в шаблон формы resources/views/news/create.blade.php
, после поля для добавления картинки, добавим таблицу с полем для ввода полезных ссылок и кнопкой «Еще ссылка».
Так же внизу подключим jQuery и напишем небольшой код JavaScript, в итоге шаблон должен выглядеть так:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<title>Добавление новости</title>
</head>
<body>
<div class="container">
<div class="col-8">
<h1>Добавить новость</h1>
<form method="post" action="{{ route('news.store') }}" enctype="multipart/form-data">
@csrf
<div class="form-group">
<label for="name">Название новости</label>
<input type="text" class="form-control" id="name" name="name">
</div>
<div class="form-group">
<label for="body">Содержание</label>
<textarea class="form-control" id="body" rows="3" name="body"></textarea>
</div>
<div class="form-group">
<label for="image">Картинка для новости</label>
<input type="file" class="form-control-file" id="image" name="image">
</div>
<div class="form-group">
<label>Ссылки на дополнительную информацию</label>
<table class="table table-bordered" id="linksTable">
<tr>
<td><input type="text" name="links[link0]" placeholder="Введите ссылку" class="form-control name_list" /></td>
</tr>
</table>
<button type="button" name="add" id="add" class="btn btn-success">Еще ссылка</button>
</div>
<button type="submit" class="btn btn-primary">Добавить</button>
</form>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script type="text/javascript">
var i = 0;
$("#add").click(function(){
++i;
$("#linksTable").append('<tr><td><input type="text" name="links[link'+i+']" placeholder="Введите ссылку" class="form-control" /></td><td><button type="button" class="btn btn-danger remove-tr">Удалить</button></td></tr>');
});
$(document).on('click', '.remove-tr', function(){
$(this).parents('tr').remove();
});
</script>
</body>
</html>
Перейдем на http://нашсайт/news/create и удостоверимся что все работает:
Следующем этапом сделаем сохранение ссылок в базу данных.
Первым делом в файле модели app/News.php добавим links в переменную $fillable.
Далее в модели мы добавили свойство $casts.
С помощью свойства $casts автоматически будет преобразовываться поле links из JSON формата в массив. Подробнее можно почитать в документации https://laravel.com/docs/6.x/eloquent-mutators#array-and-json-casting
В итоге наша модель выглядит так:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class News extends Model
{
protected $fillable = ['name', 'body', 'image', 'links'];
protected $casts = [
'links' => 'array',
];
}
Теперь можно протестировать добавление ссылок, если вы повторили все в точности за мной, должно заработать.
Добавьте для теста новость и пару ссылок ней.
Осталось добавить вывод дополнительных ссылок в списке новостей под картинкой, чтобы убедиться в работоспособности всего того, чего мы только что сделали.
Для этого переходим в шаблон списка новостей resources/views/news/index.blade.php
Немного его изменим, добавим в цикл новостей, цикл вывода ссылок:
@foreach($news as $item)
<div class="col-8">
<h2>{{ $item->name }}</h2>
<p><img src="{{ Storage::url('image/news/thumbnail/'.$item->image) }}" alt=""></p>
@foreach($item->links as $link)
<p>{{ $link }}</p>
@endforeach
</div>
@endforeach
Идем на отображение всех новостей http://нашсайт/news/ и проверяем:
Работает, появилась новость с дополнительными ссылками.