Первое использование массива медленное

Я столкнулся со странной "ошибкой" в PHP, и, поскольку я новичок, я нахожусь в конце своих знаний.

Я разрабатываю расширение TYPO3, которое имеет некоторые серьезные проблемы с производительностью данных, или так я думал. Оказывается, первое использование массива, в котором хранятся все объекты, полученные из запроса к базе данных, заняло много времени. Каждое использование или цикл после этого снова выполняется быстро.

Код выглядит так:

        $productsArr = $this->productRepository->findByDetail($category, $properties);

        $newSortArr = array();
        $familyProductList = array();

        $counter = count($productsArr);
        /** @var Product $product */
        for($i = 0; $i < $counter; $i++) {

            //it takes to long to do this
            $product = $productsArr[$i];

            if(!empty($productsArr[$i])) {
                $newSortArr[$product->getInFamily()->getUid()][] = $product;
            }
        }

Неважно, где я впервые использую массив объектов. Первое использование массива всегда занимает около 30 секунд.

Кто-нибудь сталкивался с чем-то подобным? Если вам нужна дополнительная информация, я с удовольствием предоставлю это.

Заранее спасибо!

3 ответа

Ваш $productsArr это не массив, а объект класса Exbase QueryResult который вы можете перебрать с foreach или сделать индексный доступ. Этот объект выполняет запрос и создает его объекты только при необходимости, поэтому в данный момент вы делаете $product = $productsArr[$i];, все Product-объекты $productsArrпостроены. Основная проблема заключается в том, что создание объектов в PHP имеет плохую производительность и потребляет много памяти.

Итак, чтобы избежать проблемы с производительностью, рассмотрите возможность использования пользовательского запроса с

$this->productRepository->createQuery()->statement('select * from ...')->execute();

чтобы получить именно то, что вы хотите, вместо того, чтобы загружать огромное количество объектов и уточнить их позже в PHP.

Как уже упоминал Джей, ваш результат не массив, а QueryResult, Просто к сведению, можно преобразовать его в массив, добавив ->toArray() в конце вашего запроса:

$productsArr = $this->productRepository->findByDetail($category, $properties)->toArray();

Но это не улучшит ситуацию. Есть две возможные проблемы:

Итерация всех объектов

Преимущество QueryResult является то, что он отражает только результат запроса, но не разрешает все объекты уже. QueryResult может быть передан, например, в виджет Pagination, и тогда он будет загружать только запрошенные результаты (например, 1-10, 11-20 и т. Д.).

Поскольку вы применяете сортировку вручную, все ваши объекты (в зависимости от вашего проекта это может быть много...) загружаются.

Видимо, вы хотели бы отсортировать продукты по их UID семьи? Почему бы не сделать это с функциональностью Extbase в вашем ProductRepository:

protected $defaultOrderings = array(
    'inFamily.uid' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
);

Стремительная загрузка подчиненных объектов

Ваша модель Product может иметь отношение к другим моделям (например, "Товар к категории", "Товар к опциям" и т. д.). По умолчанию Extbase разрешает все эти отношения при доступе к объектам.

Чтобы предотвратить это, вы можете использовать Lazy Loading для отношений. Это имеет смысл для подобъектов, которые используются не во всех представлениях. Например, при просмотре списка вам нужны только название, изображение и цена вашего продукта, но вам не нужны все параметры продукта.

Чтобы настроить отложенную загрузку для этих подчиненных объектов, вам просто нужно @lazy аннотация в модели:

/**
 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\My\Extension\Domain\Model\ObjectStorageModel>
 * @lazy
 */
protected $categories;

/**
 * @var \My\Extension\Domain\Model\OtherModel
 * @lazy
 */
protected $author;

Ленивая загрузка может иметь некоторые недостатки, например, в определенных ситуациях при проверке объекта, являющегося экземпляром OtherModel, вы получаете объект типа LazyLoadingProxy вместо. Вы можете обойти большинство из этих проблем или, возможно, даже не наткнуться на них в обычных сценариях. Обычный обходной путь, если вы действительно зависите от объекта, не являющегося LazyLoadingProxy, - это проверка вроде этой:

if ($product->getAuthor() instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
    $product->getAuthor()->_loadRealInstance();
}

Это гарантирует, что в любом случае у вас есть "реальный" экземпляр объекта.

Пожалуйста, не забывайте очищать системные кеши, когда вы вносите изменения в одну из проблем.

Я предполагаю, что массив заполняется в первой строке. Рассматривали ли вы заселение $product с помощью foreach($productArr as $product) вместо того, чтобы использовать for?

Другие вопросы по тегам