Search code examples
shopwareshopware6

sw-entity-listing: Cannot read properties of undefined (reading 'page')


In the administration I'm using the sw-entity-listing component where I load the entries of a custom entity. The listing has four columns (customer group, sales channel, products and maxcount). customer group and sales channel contains an ID and products contains an JSON with ID's.

What I'm trying to do is map the ID's to the actual entity names. For that part I've created this JS:

getList() {
            this.isLoading = true;
            const criteria = new Criteria();
            return this.cartCrossSellRepository.search(criteria, Shopware.Context.api).then(async (result) => {
                this.total = result.total;

                const cartCrossSell = result.map(async (item) => {
                    const customerGroup = await this.getCustomerGroupName(item.customerGroupId);
                    const salesChannel = await this.getSalesChannelName(item.salesChannelId);
                    const products = await this.getProductNames(item.products);

                    return {
                        ...item,
                        customerGroup,
                        salesChannel,
                        products
                    };
                });

                this.cartCrossSell = await Promise.all(cartCrossSell);
                this.isLoading = false;
            });
        },

        async getCustomerGroupName(customerGroupId) {
            const criteria = new Criteria();
            criteria.addFilter(Criteria.equals('id', customerGroupId));

            const customerGroup = await this.customerGroupRepository.search(criteria, Shopware.Context.api);
            return customerGroup[0].translated.name;
        },

        async getSalesChannelName(salesChannelId) {
            const criteria = new Criteria();
            criteria.addFilter(Criteria.equals('id', salesChannelId));

            const salesChannel = await this.salesChannelRepository.search(criteria, Shopware.Context.api);
            return salesChannel[0].translated.name;
        },

        async getProductNames(productIds) {
            const criteria = new Criteria();
            criteria.setIds(productIds);

            const products = await this.productRepository.search(criteria, Shopware.Context.api);
            const productNames = products.map((product) => product.productNumber);

            return productNames.join('<br>');
        }

All is working fine but I'm getting an error in my console log: An error was captured in current module: TypeError: Cannot read properties of undefined (reading 'page').

When I use something like this the error is gone but then I don't have the data mapping:

getList() {
            this.isLoading = true;
            const criteria = new Criteria();
            return this.cartCrossSellRepository.search(criteria, Shopware.Context.api).then((result) => {
                this.total = result.total;
                this.cartCrossSell = result;
                this.isLoading = false;
            })
        }

Because of the error, the delete function of the component isn't working as expected (page isn't reloading after delete and freezes). What is causing the problem and how can I fix this?


Solution

  • The listing is expecting a type of EntityCollection which the search call on the repository would normally return. You however return an array of generic objects by mapping the original result. There are a few things that don't seem optimal here. You obviously have a foreign key to customer_group, as well as sales_channel. So I assume your entity definition partially looks like this:

    new FkField('customer_group_id', 'customerGroupId', CustomerGroupDefinition::class),
    new FkField('sales_channel_id', 'salesChannelId', SalesChannelDefinition::class),
    new ManyToOneAssociationField('customerGroup', 'customer_group_id', CustomerGroupDefinition::class, 'id'),
    new ManyToOneAssociationField('salesChannel', 'sales_channel_id', SalesChannelDefinition::class, 'id'),
    

    Then you could simply add the corresponding association to the criteria when fetching the entities by the cartCrossSellRepository. That way, you wouldn't have to fetch individual instances of sales_channel and customer_group on each iteration, making the whole procedure much more performant.

    const criteria = new Criteria();
    criteria.addAssociation('customerGroup');
    criteria.addAssociation('salesChannel');
    
    this.cartCrossSellRepository.search(criteria, Shopware.Context.api).then(async (result) => {
        result.forEach((item) => {
            // should log the respective entity data fetched by association
            console.log(item.customerGroup);
            console.log(item.salesChannel);
        });
    });
    

    For the products you ideally should also use the corresponding association as well. However it seems like your products property is just a json field containing the ids? I would advise to make it a proper ManyToManyAssociationField if not already the case.

    If you want to stick with your current approach, you could simply iterate the items of the EntityCollection and set your additional data as an unmapped property to the entity.

    this.cartCrossSellRepository.search(criteria, Shopware.Context.api).then(async (result) => {
        result.forEach((item) => {
            item.productNames = await this.getProductNames(item.products);
        });
    
        return result;
    });