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

Поля типа 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);
    }

Но как это более красиво реализовать подумайте сами )

Хостинг для ваших проектов