В этой статье мы узнаем как хранить JSON в бд MySQL и как легко выбирать и обновлять эти данные с использованием Eloquent.
Прежде всего, вам необходимо проверить, что версия вашей бд MySQL 5.7.8 или выше. Более старые версии не поддерживают хранение json в таблицах.
Примеры для данной статьи будут использовать данные от известной на западе игры Habbo.
Структура данных
Как вы можете видеть, данные в формате json сгруппированы по двум категориям: типы элементов комнаты и типы элементов стены. Мы получим эту информацию и сохраним ее в таблице нашей базы данных.
Создание миграции
Прежде всего нам нужно сделать миграцию чтобы создать таблицу items и иметь возможность хранить наши данные
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateItemsTable extends Migration
{
public function up()
{
Schema::create('items', function (Blueprint $table) {
$table->id();
$table->json('furni');
$table->string('type');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('items');
}
}
Как вы уже заметили, мы отметили столбец furni как json.
А вы знали?
При создании миграции с помощью команды artisan make:migration вам не нужно указывать имя миграции с каким либо форматированием, достаточно только сделать это следующим образом:
php artisan make:migration "create items table"
Получение данных
Чтобы получить данные с сайта Habbo, мы создадим крошечную команду, которая будет импортировать эти данные в таблицу items.
Для начала создадим модель
php artisan make:model Item
Затем мы напишем команду, которая выглядит следующим образом:
<?php
namespace App\Console\Commands;
use App\Models\Item;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
use Symfony\Component\Console\Helper\ProgressBar;
class AddItemsCommand extends Command
{
private const HASHES_URL = 'https://www.habbo.com/gamedata/hashes2';
protected $signature = 'add:items';
protected $description = 'Add items from gamedata';
public function handle()
{
$this->storeFurniData();
$this->newLine();
return $this->info('Furni info fetched!');
}
private function getFurniDataUrl(): string
{
$hashUrl = Http::get(self::HASHES_URL);
if (! $hashUrl->successful()) {
$this->error('Furni data could not be fetched.');
}
$hashes = collect($hashUrl->json()['hashes']);
$hashes = $hashes->mapWithKeys(fn($hash) => [$hash['name'] => $hash]);
$furnidata = $hashes->get('furnidata');
return "{$furnidata['url']}/{$furnidata['hash']}";
}
private function storeFurniData(): void
{
$data = Http::get($this->getFurniDataUrl());
$data = collect($data->json());
$data = $data->mapWithKeys(fn($items, $key) => [$key => $items]);
$furniCount = $data->map(fn($items) => count($items['furnitype']))->flatten()->sum();
$this->info("Trying to fetch {$furniCount} furnis.");
$progressBar = $this->output->createProgressBar($furniCount);
$progressBar->start();
$data->each(fn($types, $type) => collect($types)->each(fn($items) => collect($items)->each(fn($item) => $this->addItem($item, $type, $progressBar))));
$progressBar->finish();
}
private function addItem(array $furni, string $type, ProgressBar $bar): void
{
$item = new Item();
$item->furni = json_encode($furni);
$item->type = $type;
$item->save();
$bar->advance();
}
}
В коде выше мы получаем URL-адрес данных furni, а затем перебираем элементы для хранения в нашей базе данных. Не забудьте закодировать информацию JSON, как мы делаем в этом примере при добавлении новых строк в таблицу!
Выполнив эту команду, мы получим нашу таблицу элементов, полную данных в течение нескольких секунд.
Обработка данных
Давайте посмотрим на JSON-информацию первой мебели с помощью простого выбора:
\App\Models\Item::first()->furni
{
"bc": true,
"id": 13,
"name": "Beige Bookcase",
"rare": false,
"xdim": 1,
"ydim": 1,
"adurl": null,
"buyout": true,
"offerid": 5,
"canlayon": false,
"cansiton": false,
"category": "shelf",
"revision": 61856,
"classname": "shelves_norja",
"furniline": "iced",
"canstandon": false,
"defaultdir": 0,
"partcolors": {
"color": [
"#ffffff",
"#F7EBBC"
]
},
"rentbuyout": false,
"description": "For nic naks and books.",
"environment": null,
"rentofferid": -1,
"specialtype": 1,
"customparams": null,
"excludeddynamic": false
}
Это то, что включено в первую строку нашей таблицы товаров в столбце furni. Как вы можете видеть, существует множество свойств, которые хранятся в одном столбце!
Приведение JSON к массиву
Можно преобразовать JSON в массив без необходимости использования json_decode. Все, что нам нужно сделать, это добавить это в нашу модель Item.
protected $casts = [
'furni' => 'array'
];
Теперь каждый раз, когда мы получаем данные из столбца furni, мы получим в ответе массив.
Использование условий JSON Where
Представьте себе, что вы хотите только запросить замороженные предметы мебели, как это можно сделать?
Laravel eloquent предоставляет JSON where, которые помогут нам быстро это исправить:
\App\Models\Item::where('furni->furniline', 'iced')->get()
Как вы можете видеть, нам нужно только запросить столбец JSON с помощью оператора ->.
Запрос массивов JSON по их длине
Что делать, если мы хотим запросить мебель, которая имеет три цвета? Это можно сделать с помощью метода whereJsonLength, который проверит, сколько элементов имеет массив.
\App\Models\Item::whereJsonLength('furni->partcolors->color', 3)->get()
И это все, мы получили 534 предмета простым способом!
Обновление данных столбца JSON
Обновление данных JSON также очень легко сделать с помощью синтаксиса ->.
Мы изменим свойство rare на true:
\App\Models\Item::where('furni->furniline', 'rare')->update(['furni->rare' => true])
И дело сделано! Давайте проверим, что получилось:
\App\Models\Item::where('furni->furniline', 'rare')->get()
Выводы
На мой взгляд, хранить и обрабатывать данные JSON очень легко с помощью Laravel. От выбора простого значения JSON до массового обновления некоторых данных JSON. Я надеюсь, что эти трюки были вам полезны.
https://davidtorralbo.dev/post/using-json-column-types-in-laravel