Не хватает памяти для обработки запроса

164
31 декабря 2018, 06:40

yii2-advanced, база MariaDB. Сделал скрипт, который подтягивает данные из 3 таблиц и сопоставляет данные так:

public function matchImage()
{
    $this->image = UploadedFile::getInstances($this, 'image');
    if ($this->image && $this->validate()) {
        $products = Products::find()->all();
        foreach ($products as $product) {
            $barcodes = ProductsBarcodes::find()->where(['product_id' => $product->id])->all();
            foreach ($barcodes as $barcode) {
                foreach ($this->image as $image) {
                    $origName = "$image->baseName";
                    $exploded = explode("_", $origName);
                    if (isset($exploded[1])) {
                        $origName = $exploded[0];
                    }
                    if ($origName == $barcode->barcode) {
                        static::newImages($product->id, $image);
                    }
                }
            }
        }
    }
}

public function newImages($productId, $image)
{
    $array = Images::find()->where(['product_id' => $productId])->all();
    $dir = "uploads/products";
    $num = count($array);
    $name = $productId . "_" . "$num.$image->extension";
    $model = new Images();
    $main = 0;
    foreach ($array as $mainimage) {
        if ($mainimage->main) {
            $main = 1;
        }
    }
    if (!$main) {
        $model->main = 1;
    }
    $model->url = $name;
    $model->product_id = $productId;
    if($model->validate() && $model->save()) {
        $image->saveAs("$dir/$name");
        return true;
    }
    else {
        return false;
    }
}

На тестовой базе создал эти 3 таблицы, все работает нормально.

На второй тестовой базе (дамп с продакшена) в таблице products_barcodes нет поля id, т.к. это таблица one-to-many.

И вот здесь выпадает ошибка :

Allowed memory size of 268435456 bytes exhausted (tried to allocate 20480 bytes) 

//мало памяти

Это из-за того, что нету id? Можно ли как-то переписать скрипт под таблицу без id?

P.S.

$products = Products::find()->joinWith('product_barcode')->where(['id' => 'product_barcode.product_id'])->all();
public function getProductsBarcodes()
{
    return $this->hasMany(ProductsBarcodes::className(), ['product_id' => 'id']);
}
Answer 1

Непонятно для чего тут нужен $products = Products::find()->all();, если у ProductsBarcodes есть поле product_id. Первый форич убираем?

Метод newImage() хорошо, но ведь он в цикле вызывается, а в нем обращение к БД. Что мешает получить сразу все Images в массив с ключами product_id одним запросом и выбрать не все поля, а только main (судя по коду)?

$this->images = Images::find()->select('main')->indexBy('product_id')->all();

Затем уже делаем форич $this->images[$productId] или обходимся без форича, если выбрать только те записи, у которых main больше 0 (или что там должно быть). Достаточно будет проверить наличие $this->images[$productId] и не надо будет каждый раз перебирать массив, и сам массив будет меньше.

Уверен, что это значительно снизит прожорливость.

READ ALSO
Не работают relations

Не работают relations

yii2-advanced, не работают relations

162
Библиотеки для работы с Nested Set

Библиотеки для работы с Nested Set

Подскажите пожалуйста библиотеку(и) для работы с Nested Set, чтобы была возможность перемещать узлыВерсия php 5

189
Знаки вопроса вместо кириллицы

Знаки вопроса вместо кириллицы

Вместо кириллицы отображаются вопросительные знаки (не символы)

261