Поля типа JSON в MySQL, как использовать в Laravel

На простом примере разберем как использовать в Laravel поля типа JSON в MySQL. Для простоты представим, что у нас есть раздел статей на сайте и для СЕО потребовалось добавить отдельные поля для meta title и meta description страницы. Пока у нас получается 2 дополнительных поля, но в будущем возможно добавится еще одно keywords, поэтому лучше мы будем записывать все эти мета данные страницы в одно поле типа JSON в нашей базе данных.
Когда я уже закончил заметку, я подумал что более удачный пример был бы с характеристиками товара для интернет магазина, поскольку там всегда не знаешь сколько будет характеристик у каждого товара. Но разницы в реализации особой нет.
Весь начальный этап создания миграции, контроллеров, шаблонов, я не буду. В данной статье речь не об этом.
Предположим у нас уже есть функционал по добавлению статей на сайт и нам просто необходимо добавить к нему нужные поля.
Поле типа JSON в Mysql
Создаем миграцию чтобы добавить дополнительное поле типа JSON к нашей таблице с постами, поле назовем meta.
Для этого выполним команду:
php artisan make:migration add_meta_to_posts_table --table=posts
В созданном файле миграции добавляем поле meta типа JSON:
Schema::table('posts', function (Blueprint $table) {
$table->json('meta');
});
Выполняем миграцию php artisan migrate
Работа с JSON в Laravel
После этого идем в модель постов app/Post.php
и добавляем к следующий код:
class Post extends Model
{
……
protected $casts = [
'meta' => 'array',
];
……
}
Этим мы указали, что нужно автоматически преобразовывать поле meta из JSON формата в массив. Т.е. мы получаем $post->meta
в виде массива.
Выше мы использовали свойство $casts модели которое предоставляет удобный метод преобразования атрибутов к общим типам данных. Подробнее можно посмотреть в документации: https://laravel.com/docs/6.x/eloquent-mutators#array-and-json-casting
Далее идем шаблон Blade, а именно в форму добавления/изменения постов. Нам нужно добавить 2 дополнительных поля с мета заголовком и описанием страницы:
<form action="{{ route('admin.posts.store') }}" method="post" enctype="multipart/form-data" class="mb-4">
@csrf
…..
<div class="form-group">
<label for="meta[title]">Мета заголовок</label>
<input type="text" class="form-control" id="m-title" name="meta[title]" value="{{ old('meta.title', isset($post->meta['title']) ? $post->meta['title'] : '') }}">
</div>
<div class="form-group">
<label for="meta[description]">Мета описание</label>
<input type="text" class="form-control" id="m-description" name="meta[description]" value="{{ old('meta.description', isset($post->meta['description']) ? $post->meta['description'] : '') }}">
</div>
<button type="submit" class="btn btn-primary">Сохранить</button>
</form>
Обратите внимание, на директиву old() шаблонизатора Blade, поскольку в поле meta мы передаем массив, чтобы вывести значение, например мета заголовка, нам нужно указать old(meta.title)
.
Метод store() в контроллере постов app/Http/Controllers/PostController.php
совсем простой:
public function store(PostRequest $request)
{
$data = $request->all();
…..
Post::create($data);
return redirect('admin/posts')->with('status', 'Пост добавлен!');
}
Так же как и метод для обновления поста update():
public function update(PostRequest $request, Post $post)
{
$data = $request->all();
…..
$post->update($data);
return redirect('admin/posts');
}
Теперь осталось отобразить наши данные в шаблоне Blade. Для этого в нужных местах шаблона достаточно указать {{ $post->meta['title'] ?? '' }}
и {{ $post->meta['description'] ?? '' }}
UPD: В примере выше, при добавлении статьи, если не указаны значения title и description, в наше поле JSON попадут значения null
и будут там храниться:
[
{
"title":null,
"description":null
}
]
Чтобы этого не было, нужно исключить null значения из массива. Можно например добавить мутатор Eloquent в нашу модель app/Post.php
.
Что-то типа этого:
public function setMetaAttribute($value)
{
if(is_null($value['title'])){
unset($value['title']);
}
if(is_null($value['description'])){
unset($value['description']);
}
$this->attributes['meta'] = json_encode($value);
}
Но как это более красиво реализовать подумайте сами )