Skip to content

Commit ecd5e54

Browse files
buuhuuherzog31
andauthored
CIF-2461: refactored specific page logic to use new parameter objects (#776)
* refactored specific page logic to not mess with selectors anymore * fix formatting * improve coverage of SpecificPageStrategy * make SpecificPageFilter work with CatalogPageNotFoundFilter * fix issues caused by differences of parsable parameters from format parameters * prevent params copy when deep linking is disabled * add coverage for the specific page filter when resource is not a page * revert changes to NavigationImplTest * CIF-2081: Implement specific PDP for product categories (#784) * extract category url_key from product urls if possible * use a CategoryUrlFormat.Params object to carry category infromation encoded in product urls * implement specific product page selection by category * add product url formats that encode category url_key * CIF-2503: category context aware product urls (#785) * add selection of url_rewrite from category context to ProductPageWithSkuAndUrlPath * implement prefix and partial matching of category context * retain the category information form the url if not given otherwise * score either the contextUrlPath and contextUrlKey * set the category context to product urls for product list / search component * fix examples/bundle tests * format code * make breadcrumb context aware * make the context aware product urls configureable * fix tests * set category context before selecting specific product page * do not copy ref to immutable objects * update TODO comments with the jira issue * fix category aware urls in search result * fix tests Co-authored-by: Mark J. Becker <herzog31@users.noreply.github.com>
1 parent 5f50d5a commit ecd5e54

57 files changed

Lines changed: 2252 additions & 777 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/breadcrumb/BreadcrumbImpl.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Comparator;
2222
import java.util.List;
2323
import java.util.function.Function;
24+
import java.util.stream.Collectors;
2425

2526
import javax.annotation.PostConstruct;
2627

@@ -39,9 +40,11 @@
3940

4041
import com.adobe.cq.commerce.core.components.client.MagentoGraphqlClient;
4142
import com.adobe.cq.commerce.core.components.internal.datalayer.DataLayerComponent;
43+
import com.adobe.cq.commerce.core.components.internal.services.urlformats.UrlFormatBase;
4244
import com.adobe.cq.commerce.core.components.models.breadcrumb.Breadcrumb;
4345
import com.adobe.cq.commerce.core.components.models.navigation.Navigation;
4446
import com.adobe.cq.commerce.core.components.services.urls.CategoryUrlFormat;
47+
import com.adobe.cq.commerce.core.components.services.urls.ProductUrlFormat;
4548
import com.adobe.cq.commerce.core.components.services.urls.UrlProvider;
4649
import com.adobe.cq.commerce.core.components.utils.SiteNavigation;
4750
import com.adobe.cq.commerce.magento.graphql.CategoryInterface;
@@ -132,7 +135,8 @@ private void populateItems(NavigationItem item) {
132135
if (StringUtils.isEmpty(productSku)) {
133136
return;
134137
}
135-
categoriesBreadcrumbs = fetchProductBreadcrumbs(productSku, magentoGraphqlClient);
138+
ProductUrlFormat.Params urlParmas = urlProvider.parseProductUrlFormatParameters(request);
139+
categoriesBreadcrumbs = fetchProductBreadcrumbs(productSku, urlParmas, magentoGraphqlClient);
136140
} else if (isCategoryPage) {
137141
String categoryUid = urlProvider.getCategoryIdentifier(request);
138142
if (StringUtils.isEmpty(categoryUid)) {
@@ -221,11 +225,20 @@ public Comparator<CategoryInterface> getCategoryInterfaceComparator() {
221225
.reversed();
222226
}
223227

224-
private List<? extends CategoryInterface> fetchProductBreadcrumbs(String productSku, MagentoGraphqlClient magentoGraphqlClient) {
228+
private List<? extends CategoryInterface> fetchProductBreadcrumbs(String productSku, ProductUrlFormat.Params urlParams,
229+
MagentoGraphqlClient magentoGraphqlClient) {
225230
retriever = new BreadcrumbRetriever(magentoGraphqlClient);
226231
retriever.setProductIdentifier(productSku);
227232

228-
return retriever.fetchCategoriesBreadcrumbs();
233+
List<? extends CategoryInterface> categories = retriever.fetchCategoriesBreadcrumbs();
234+
List<String> alternatives = categories.stream().map(CategoryInterface::getUrlPath).collect(Collectors.toList());
235+
String contextUrlPath = UrlFormatBase.selectUrlPath(null, alternatives, null, urlParams.getCategoryUrlParams().getUrlKey(),
236+
urlParams.getCategoryUrlParams().getUrlPath());
237+
238+
// include only categories that are ancestors or descendants of the contextUrlPath
239+
return categories.stream()
240+
.filter(category -> contextUrlPath.startsWith(category.getUrlPath() + "/") || contextUrlPath.equals(category.getUrlPath()))
241+
.collect(Collectors.toList());
229242
}
230243

231244
private List<? extends CategoryInterface> fetchCategoryBreadcrumbs(String categoryUid, MagentoGraphqlClient magentoGraphqlClient) {

bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/common/ProductListItemImpl.java

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.List;
1919
import java.util.Objects;
20+
import java.util.function.Function;
2021

2122
import javax.annotation.Nullable;
2223

@@ -30,6 +31,7 @@
3031
import com.adobe.cq.commerce.core.components.models.common.ProductListItem;
3132
import com.adobe.cq.commerce.core.components.services.urls.ProductUrlFormat;
3233
import com.adobe.cq.commerce.core.components.services.urls.UrlProvider;
34+
import com.adobe.cq.commerce.magento.graphql.CategoryInterface;
3335
import com.adobe.cq.commerce.magento.graphql.GroupedProduct;
3436
import com.adobe.cq.commerce.magento.graphql.ProductImage;
3537
import com.adobe.cq.commerce.magento.graphql.ProductInterface;
@@ -58,10 +60,12 @@ public class ProductListItemImpl extends DataLayerListItem implements ProductLis
5860
private CommerceIdentifier identifier;
5961
private Boolean isStaged;
6062
private ProductInterface product;
63+
private CategoryInterface categoryContext;
6164

6265
private ProductListItemImpl(ProductInterface product, String sku, String urlKey, String urlPath, List<UrlRewrite> urlRewrites,
6366
String name, Price price, String imageURL, String imageAlt, Page productPage, String activeVariantSku,
64-
SlingHttpServletRequest request, UrlProvider urlProvider, String parentId, Boolean isStaged) {
67+
SlingHttpServletRequest request, UrlProvider urlProvider, String parentId, Boolean isStaged,
68+
CategoryInterface categoryContext) {
6569
super(parentId, productPage.getContentResource());
6670
this.product = product;
6771
this.sku = sku;
@@ -80,6 +84,7 @@ private ProductListItemImpl(ProductInterface product, String sku, String urlKey,
8084
this.identifier = activeVariantSku != null
8185
? new CommerceIdentifierImpl(activeVariantSku, CommerceIdentifier.IdentifierType.SKU, CommerceIdentifier.EntityType.PRODUCT)
8286
: new CommerceIdentifierImpl(sku, CommerceIdentifier.IdentifierType.SKU, CommerceIdentifier.EntityType.PRODUCT);
87+
this.categoryContext = categoryContext;
8388
}
8489

8590
@Deprecated
@@ -137,6 +142,11 @@ public String getURL() {
137142
params.setUrlPath(urlPath);
138143
params.setUrlRewrites(urlRewrites);
139144

145+
if (categoryContext != null) {
146+
params.getCategoryUrlParams().setUrlKey(categoryContext.getUrlKey());
147+
params.getCategoryUrlParams().setUrlPath(categoryContext.getUrlPath());
148+
}
149+
140150
return urlProvider.toProductUrl(request, productPage, params);
141151
}
142152

@@ -225,6 +235,7 @@ public static class Builder {
225235
private String name;
226236
private String urlPath;
227237
private List<UrlRewrite> urlRewrites;
238+
private CategoryInterface categoryContext;
228239

229240
public Builder(String parentId, @NotNull Page productPage, SlingHttpServletRequest request, UrlProvider urlProvider) {
230241
this.parentId = parentId;
@@ -278,22 +289,42 @@ public Builder price(Price price) {
278289
return this;
279290
}
280291

292+
/**
293+
* Sets the category as context for the product item. This will case the {@link UrlProvider} to generate product urls in the context
294+
* of this category if applicable.
295+
*
296+
* @param category
297+
* @return
298+
*/
299+
public Builder categoryContext(CategoryInterface category) {
300+
this.categoryContext = category;
301+
return this;
302+
}
303+
281304
public ProductListItem build() {
282-
String sku = this.sku == null && product != null ? product.getSku() : this.sku;
283-
String urlKey = this.urlKey == null && product != null ? product.getUrlKey() : this.urlKey;
284-
String urlPath = this.urlPath == null && product != null ? product.getUrlPath() : this.urlPath;
285-
List<UrlRewrite> urlRewrites = this.urlRewrites == null && product != null ? product.getUrlRewrites() : this.urlRewrites;
286-
String name = this.name == null && product != null ? product.getName() : this.name;
287-
Price price = this.price == null && product != null
288-
? new PriceImpl(product.getPriceRange(), productPage.getLanguage(false), product instanceof GroupedProduct)
289-
: this.price;
290-
ProductImage image = this.image == null && product != null ? product.getSmallImage() : this.image;
305+
String sku = getFromProductIfNull(this.sku, product, ProductInterface::getSku);
306+
String urlKey = getFromProductIfNull(this.urlKey, product, ProductInterface::getUrlKey);
307+
String urlPath = getFromProductIfNull(this.urlPath, product, ProductInterface::getUrlPath);
308+
List<UrlRewrite> urlRewrites = getFromProductIfNull(this.urlRewrites, product, ProductInterface::getUrlRewrites);
309+
String name = getFromProductIfNull(this.name, product, ProductInterface::getName);
310+
// TODO: target to be refactored with 3.0 (CIF-2634)
311+
// The price is required, either set by the builder or as a price range of the product. Some code expects the exception being
312+
// thrown by PriceImpl when the price range is null, for example the ProductCarouselImpl (validated by unit tests). However
313+
// Exceptions must not be used for control flow, meaning this implementation should return null if the price is really required
314+
// or, if not the impl should deal with null prices.
315+
Price price = getFromProductIfNull(this.price, product,
316+
p -> new PriceImpl(p.getPriceRange(), productPage.getLanguage(false), p instanceof GroupedProduct));
317+
ProductImage image = getFromProductIfNull(this.image, product, ProductInterface::getSmallImage);
291318
String imageUrl = image != null ? image.getUrl() : null;
292319
String imageLabel = image != null ? StringUtils.defaultIfBlank(image.getLabel(), name) : null;
293320
boolean isStaged = product != null && Boolean.TRUE.equals(product.getStaged());
294321

295322
return new ProductListItemImpl(product, sku, urlKey, urlPath, urlRewrites, name, price, imageUrl, imageLabel, productPage,
296-
variantSku, request, urlProvider, parentId, isStaged);
323+
variantSku, request, urlProvider, parentId, isStaged, categoryContext);
297324
}
298325
}
326+
327+
private static <T> T getFromProductIfNull(T value, ProductInterface product, Function<ProductInterface, T> getter) {
328+
return value == null && product != null ? getter.apply(product) : value;
329+
}
299330
}

bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/productlist/ProductListImpl.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,6 @@ public boolean showTitle() {
159159
return showTitle;
160160
}
161161

162-
// Not OSGi exported but public so UrlProviderImpl can use it
163-
public String getUrlPath() {
164-
return getCategory() != null ? getCategory().getUrlPath() : null;
165-
}
166-
167162
@Override
168163
public String getImage() {
169164
if (getCategory() != null) {
@@ -185,8 +180,10 @@ public boolean showImage() {
185180
@Override
186181
public Collection<ProductListItem> getProducts() {
187182
if (usePlaceholderData) {
188-
CategoryProducts categoryProducts = getCategory().getProducts();
189-
ProductToProductListItemConverter converter = new ProductToProductListItemConverter(productPage, request, urlProvider, getId());
183+
CategoryInterface category = getCategory();
184+
CategoryProducts categoryProducts = category.getProducts();
185+
ProductToProductListItemConverter converter = new ProductToProductListItemConverter(productPage, request, urlProvider, getId(),
186+
category);
190187
return categoryProducts.getItems().stream()
191188
.map(converter)
192189
.filter(Objects::nonNull) // the converter returns null if the conversion fails

bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/services/CategoryUrlParameterRetriever.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@
2020
import com.adobe.cq.commerce.magento.graphql.CategoryTreeQuery;
2121
import com.adobe.cq.commerce.magento.graphql.CategoryTreeQueryDefinition;
2222

23-
class CategoryUrlParameterRetriever extends AbstractCategoryRetriever {
23+
public class CategoryUrlParameterRetriever extends AbstractCategoryRetriever {
2424

25-
CategoryUrlParameterRetriever(MagentoGraphqlClient client) {
25+
public CategoryUrlParameterRetriever(MagentoGraphqlClient client) {
2626
super(client);
2727
}
2828

2929
@Override
3030
protected CategoryTreeQueryDefinition generateCategoryQuery() {
3131
return (CategoryTreeQuery q) -> {
32-
q.urlPath().urlKey();
32+
q.uid().urlPath().urlKey();
3333

3434
if (categoryQueryHook != null) {
3535
categoryQueryHook.accept(q);

0 commit comments

Comments
 (0)