Как сохранить в кэш добавление в базу данных в yii2?

Проблема такая, у меня после сохранения висит много запросов к БД. Сохранил 4 категории и 10 связных продуктов, и висит 35 запросов. Прочитал много статей, почти везде говорят про кэширование.

Как его реализовать правильно, примеров нигде не нашел нормальных. Понимаю, возможно не правильно что то делаю, надеюсь вы подтолкнете меня в правильное русло)) За ранее спасибо вам!

Модель Category

<?php

namespace app\models;

use Yii;

/**
 * This is the model class for table "{{%category}}".
 *
 * @property int $id
 * @property string $name
 *
 * @property Product[] $products
 */
class Category extends \yii\db\ActiveRecord
{
    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return '{{%category}}';
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['id'], 'primaryKey'],
            [['name'], 'required'],
            [['name'], 'string', 'max' => 255],
            [['name'], 'unique'],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'name' => 'Name',
        ];
    }

    /**
     * Gets query for [[Products]].
     *
     * @return \yii\db\ActiveQuery
     */
    public function getProducts()
    {
        return $this->hasMany(Product::className(), ['category_id' => 'id']);
    }

}

модель Product

<?php

namespace app\models;

use Yii;

/**
 * This is the model class for table "{{%product}}".
 *
 * @property int $id
 * @property string|null $name
 * @property int $category_id
 *
 * @property Category $category
 */
class Product extends \yii\db\ActiveRecord
{
    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return '{{%product}}';
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['category_id'], 'required'],
            [['category_id'], 'integer'],
            [['name'], 'string', 'max' => 255],
            [['name'], 'unique'],
            [['category_id'], 'exist', 'skipOnError' => true, 'targetClass' => Category::className(), 'targetAttribute' => ['category_id' => 'id']],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'name' => 'Name',
            'category_id' => 'Category ID',
        ];
    }

    /**
     * Gets query for [[Category]].
     *
     * @return \yii\db\ActiveQuery
     */
    public function getCategory()
    {
        return $this->hasOne(Category::className(), ['id' => 'category_id']);
    }
}

controller

<?php
namespace app\controllers;


use yii\db\Exception;
use yii\web\Controller;
use app\components\parseDigiseller;
use app\models\Category;
use app\models\Product;

class ProductController extends Controller{

   public function actionIndex(){
        $host="../components/files/products.csv";
        $file='сайт получения информации';

        $parse = new parseDigiseller();
        $resParse = $parse->parseDigi($file, $host);

       foreach ($resParse as $saveData){
           $categoryModel = new Category();
           $categoryModel->name = $saveData['category'];
           $categoryModel->save();
       }

       $category = Category::find()->all();
       foreach ($resParse as $saveData){
           foreach ($category as $cat) {
               if($cat['name'] === $saveData['category']) {
                   $id = $cat['id'];

                   $productModel = new Product();
                   $productModel->name = $saveData['name'];
                   $productModel->category_id = $id;
                   $productModel->save();
               }
           }
       }

        return $this->render('index');
    }
}

Ответы (1 шт):

Автор решения: fedornabilkin

Не совсем понятна суть контроллера, но постараюсь донести мысль насколько я понял ситуацию. Судя по коду, данные категорий и продуктов лежат в файле и при запросе экшена добавляются в БД. Скорее всего это единичная операция. Если данные файла будут изменяться, тогда надо делать проверку, чтобы не добавить дублей в БД (это по категориям, для продуктов может и допустимо).

Если не нравится большое количество запросов при сохранении, то можно не сохранять каждую запись отдельно, а подготовить массив данных для категорий и продуктов и использовать метод batchInsert($columns, $items), так будет всего два запроса. При этом не будет валидации данных. Если данные в из файла надо валидировать, то перед добавлением в массив $items можно создавать объект модели (категории или продукта), грузить туда данные, делать валидацию и по результату уже добавлять или нет в $items. Соответственно логировать, если что-то не добавили и по какой причине. Также необходимо учитывать, что при использовании batchInsert() не будут вызваны события, которые вызываются при выполнении метода save(). Вероятно это можно как-то обработать дополнительно, но надо больше информации.

Запрос $category = Category::find()->all(); избыточен, потому что чуть выше уже есть $categoryModel. Достаточно их сложить в массив $category[] = $categoryModel и получится все тоже самое, только без запроса.

Обычно кэш применяется при получении данных. Если процесс трудоемкий, то данные пишут в кэш и получают оттуда, чтобы не нагружать БД. В случае записи данных в БД непонятно как и где применять возможности кэша.

  1. Получаем данные
  2. Формируем массив для добавления в БД (проводим валидацию)
  3. Пишем данные в БД

Если не устраивает пакетное добавление данных через batchInsert(), тогда тут может подойти метод link(), при наличии сохраненной категории и соответствующих реляций в моделях.

Также хочу заметить, что все это необходимо делать не в экшене контроллера, а создать какой-то сервис, в котором все это и реализовать.

→ Ссылка