feat(poc): seoUrls yes or no env

This commit is contained in:
Björn Meyer 2023-07-17 15:56:32 +02:00
parent 744243c3d8
commit 46e003196f
6 changed files with 115 additions and 82 deletions

View File

@ -4,3 +4,4 @@ SITE_NAME="Next.js Commerce with Shopware Composable Frontends"
SHOPWARE_STORE_DOMAIN="" SHOPWARE_STORE_DOMAIN=""
SHOPWARE_API_TYPE="store-api" SHOPWARE_API_TYPE="store-api"
SHOPWARE_ACCESS_TOKEN="" SHOPWARE_ACCESS_TOKEN=""
SHOPWARE_USE_SEO_URLS="false"

View File

@ -3,8 +3,12 @@ import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
export async function Carousel() { export async function Carousel() {
const collectionName =
`${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'
? 'Summer-BBQ/Hidden-Carousel-Category'
: 'ff7bf3c59f1342a685844fbf8fdf9dc8';
const { products } = await getCollectionProducts({ const { products } = await getCollectionProducts({
collection: 'Summer-BBQ/Hidden-Carousel-Category' collection: collectionName
}); });
if (!products?.length) return null; if (!products?.length) return null;

View File

@ -37,8 +37,12 @@ function ThreeItemGridItem({
export async function ThreeItemGrid() { export async function ThreeItemGrid() {
// Collections that start with `hidden-*` are hidden from the search page. // Collections that start with `hidden-*` are hidden from the search page.
const collectionName =
`${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'
? 'Summer-BBQ/Hidden-Category'
: '4ab73c06d90d4a5cb312209a64480d87';
const { products: homepageItems } = await getCollectionProducts({ const { products: homepageItems } = await getCollectionProducts({
collection: 'Summer-BBQ/Hidden-Category' collection: collectionName
}); });
if (!homepageItems[0] || !homepageItems[1] || !homepageItems[2]) return null; if (!homepageItems[0] || !homepageItems[1] || !homepageItems[2]) return null;

View File

@ -168,15 +168,13 @@ export async function requestSeoUrl(
page: number = 1, page: number = 1,
limit: number = 1 limit: number = 1
): Promise<SeoURLResultSW | undefined> { ): Promise<SeoURLResultSW | undefined> {
console.log(handle);
try { try {
const FirstCriteria = { const criteriaSeoUrls = {
page: page, page: page,
limit: limit, limit: limit,
filter: [ filter: [
{ {
type: 'multi', type: 'multi',
// @ts-ignore
operator: 'or', operator: 'or',
queries: [ queries: [
{ {
@ -194,27 +192,7 @@ export async function requestSeoUrl(
] ]
}; };
// @ts-ignore // @ts-ignore
const firstResult = await apiInstance.invoke('readSeoUrl post /seo-url', FirstCriteria); return await apiInstance.invoke('readSeoUrl post /seo-url', criteriaSeoUrls);
if (firstResult.total && firstResult.total > 0) {
return firstResult;
}
const lastPart = handle.split('/').pop() + '';
console.log(lastPart);
const SecondCriteria = {
page: page,
limit: limit,
filter: [
{
type: 'contains',
field: 'seoPathInfo',
value: lastPart
}
]
};
return await apiInstance.invoke('readSeoUrl post /seo-url', SecondCriteria);
} catch (error) { } catch (error) {
if (error instanceof ApiClientError) { if (error instanceof ApiClientError) {
console.error(error); console.error(error);

View File

@ -40,6 +40,7 @@ import {
ProductListingCriteria, ProductListingCriteria,
StoreNavigationTypeSW StoreNavigationTypeSW
} from './types'; } from './types';
const useSeoUrls = `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true';
export async function getMenu(params?: { export async function getMenu(params?: {
type?: StoreNavigationTypeSW; type?: StoreNavigationTypeSW;
@ -53,21 +54,26 @@ export async function getMenu(params?: {
} }
export async function getPage(handle: string | []): Promise<Page | undefined> { export async function getPage(handle: string | []): Promise<Page | undefined> {
const pageHandle = decodeURIComponent(transformHandle(handle)); let seoUrlElement;
const seoUrlElement = await getFirstSeoUrlElement(pageHandle); let pageIdOrHandle = decodeURIComponent(transformHandle(handle)).replace('cms/', '');
if (seoUrlElement) {
const category = await getCategory(seoUrlElement);
if (!category) { if (useSeoUrls) {
console.log('[getPage] Did not found any category with page handle:', pageHandle); seoUrlElement = await getFirstSeoUrlElement(pageIdOrHandle);
if (seoUrlElement) {
pageIdOrHandle = seoUrlElement.foreignKey;
} }
return category ? transformPage(seoUrlElement, category) : undefined; if (!seoUrlElement) {
console.log('[getPage] Did not found any seoUrl element with page handle:', pageIdOrHandle);
}
} }
if (!seoUrlElement) { const category = await getCategory(pageIdOrHandle);
console.log('[getPage] Did not found any seoUrl element with page handle:', pageHandle); if (!category) {
console.log('[getPage] Did not found any category with handle:', pageIdOrHandle);
} }
return category ? transformPage(category, seoUrlElement) : undefined;
} }
export async function getFirstSeoUrlElement( export async function getFirstSeoUrlElement(
@ -91,17 +97,22 @@ export async function getFirstProduct(productId: string): Promise<ExtendedProduc
// ToDo: should be more dynamic (depending on handle), should work with server and not client see generateStaticParams from next.js // ToDo: should be more dynamic (depending on handle), should work with server and not client see generateStaticParams from next.js
export async function getSubCollections(collection: string) { export async function getSubCollections(collection: string) {
const collectionName = decodeURIComponent(transformHandle(collection ?? ''));
let criteria = getDefaultSubCategoriesCriteria(collectionName);
let res: CategoryListingResultSW | undefined = undefined; let res: CategoryListingResultSW | undefined = undefined;
const parentCollectionName = const parentCollectionName =
Array.isArray(collection) && collection[0] ? collection[0] : undefined; Array.isArray(collection) && collection[0] ? collection[0] : undefined;
const collectionName = transformHandle(collection ?? '');
const seoUrlElement = await getFirstSeoUrlElement(collectionName); if (useSeoUrls) {
if (seoUrlElement) { const seoUrlElement = await getFirstSeoUrlElement(collectionName);
const criteria = getDefaultSubCategoriesCriteria(seoUrlElement.foreignKey); if (seoUrlElement) {
// @ts-ignore criteria = getDefaultSubCategoriesCriteria(seoUrlElement.foreignKey);
res = await requestCategoryList(criteria); }
} }
// @ts-ignore
res = await requestCategoryList(criteria);
return res ? transformSubCollection(res, parentCollectionName) : []; return res ? transformSubCollection(res, parentCollectionName) : [];
} }
@ -160,7 +171,7 @@ export async function getCollectionProducts(params?: {
const collectionName = decodeURIComponent(transformHandle(params?.collection ?? '')); const collectionName = decodeURIComponent(transformHandle(params?.collection ?? ''));
const sorting = getSortingCriteria(params?.sortKey, params?.reverse); const sorting = getSortingCriteria(params?.sortKey, params?.reverse);
if (!category && collectionName !== '') { if (useSeoUrls && !category && collectionName !== '') {
const seoUrlElement = await getFirstSeoUrlElement(collectionName); const seoUrlElement = await getFirstSeoUrlElement(collectionName);
if (seoUrlElement) { if (seoUrlElement) {
category = seoUrlElement.foreignKey; category = seoUrlElement.foreignKey;
@ -173,6 +184,10 @@ export async function getCollectionProducts(params?: {
} }
} }
if (!useSeoUrls) {
category = params?.collection ?? undefined;
}
if (category) { if (category) {
const criteria = !params?.defaultSearchCriteria const criteria = !params?.defaultSearchCriteria
? getDefaultProductsCriteria(params?.page) ? getDefaultProductsCriteria(params?.page)
@ -194,29 +209,37 @@ export async function getCollectionProducts(params?: {
} }
export async function getCategory( export async function getCategory(
seoUrl: ApiSchemas['SeoUrl'], categoryId: string,
cms: boolean = false cms: boolean = false
): Promise<ExtendedCategory | undefined> { ): Promise<ExtendedCategory | undefined> {
const criteria = cms ? getDefaultCategoryWithCmsCriteria() : getDefaultCategoryCriteria(); const criteria = cms ? getDefaultCategoryWithCmsCriteria() : getDefaultCategoryCriteria();
return await requestCategory(seoUrl.foreignKey, criteria); return await requestCategory(categoryId, criteria);
} }
// This function is only used for generateMetadata at app/search/(collection)/[...collection]/page.tsx // This function is only used for generateMetadata at app/search/(collection)/[...collection]/page.tsx
export async function getCollection(handle: string | []) { export async function getCollection(handle: string | []) {
const collectionName = decodeURIComponent(transformHandle(handle)); let path;
const seoUrlElement = await getFirstSeoUrlElement(collectionName); let seoUrlElement;
if (seoUrlElement) { let categoryIdOrHandle = decodeURIComponent(transformHandle(handle));
const category = await getCategory(seoUrlElement);
const path = seoUrlElement.seoPathInfo ?? '';
if (category) {
const collection = transformCollection(seoUrlElement, category);
return { if (useSeoUrls) {
...collection, seoUrlElement = await getFirstSeoUrlElement(categoryIdOrHandle);
path: `/search/${path}` if (seoUrlElement) {
}; categoryIdOrHandle = seoUrlElement.foreignKey;
path = seoUrlElement.seoPathInfo ?? '';
} }
} }
const category = await getCategory(categoryIdOrHandle);
if (category) {
const collection = transformCollection(category, seoUrlElement);
path = path ?? category.id ?? '';
return {
...collection,
path: `/search/${path}`
};
}
} }
export async function getProductSeoUrls() { export async function getProductSeoUrls() {
@ -236,15 +259,19 @@ export async function getProduct(handle: string | []): Promise<Product | undefin
let productSW: ExtendedProduct | undefined; let productSW: ExtendedProduct | undefined;
let productId: string | undefined; let productId: string | undefined;
const productHandle = decodeURIComponent(transformHandle(handle)); const productHandle = decodeURIComponent(transformHandle(handle));
productId = productHandle; // if we do not use seoUrls the handle should be the product id
const seoUrlElement = await getFirstSeoUrlElement(productHandle); if (useSeoUrls) {
if (seoUrlElement) { const seoUrlElement = await getFirstSeoUrlElement(productHandle);
productId = seoUrlElement.foreignKey; if (seoUrlElement) {
productId = seoUrlElement.foreignKey;
}
} }
if (!productId) { if (!productId) {
console.log('[getProduct][search] Did not found any product with handle:', productHandle); console.log('[getProduct][search] Did not found any product with handle:', handle);
} }
if (productId) { if (productId) {
const firstProduct = await getFirstProduct(productId); const firstProduct = await getFirstProduct(productId);
if (firstProduct) { if (firstProduct) {

View File

@ -25,24 +25,30 @@ export function transformMenu(res: ExtendedCategory[], type: string) {
} }
function transformMenuItem(item: ExtendedCategory, type: string): Menu { function transformMenuItem(item: ExtendedCategory, type: string): Menu {
const path =
`${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'
? item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo
? type === 'footer-navigation'
? '/cms/' + item.seoUrls[0].seoPathInfo
: '/search/' + item.seoUrls[0].seoPathInfo
: ''
: type === 'footer-navigation'
? '/cms/' + item.id ?? ''
: '/search/' + item.id ?? '';
// @ToDo: currently only footer-navigation is used for cms pages, this need to be more dynamic (shoud depending on the item) // @ToDo: currently only footer-navigation is used for cms pages, this need to be more dynamic (shoud depending on the item)
return { return {
id: item.id ?? '', id: item.id ?? '',
title: item.name, title: item.name,
children: item.children?.map((item) => transformMenuItem(item, type)) ?? [], children: item.children?.map((item) => transformMenuItem(item, type)) ?? [],
path: path: path,
item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo
? type === 'footer-navigation'
? '/cms/' + item.seoUrls[0].seoPathInfo
: '/search/' + item.seoUrls[0].seoPathInfo
: '',
type: item.children && item.children.length > 0 ? 'headline' : 'link' type: item.children && item.children.length > 0 ? 'headline' : 'link'
}; };
} }
export function transformPage( export function transformPage(
seoUrlElement: ApiSchemas['SeoUrl'], category: ExtendedCategory,
category: ExtendedCategory seoUrlElement?: ApiSchemas['SeoUrl']
): Page { ): Page {
let plainHtmlContent; let plainHtmlContent;
if (category.cmsPage) { if (category.cmsPage) {
@ -51,20 +57,20 @@ export function transformPage(
} }
return { return {
id: seoUrlElement.id ?? '', id: seoUrlElement?.id ?? category.id ?? '',
title: category.translated?.metaTitle ?? category.name ?? '', title: category.translated?.metaTitle ?? category.name ?? '',
handle: seoUrlElement.seoPathInfo, handle: seoUrlElement?.seoPathInfo ?? category.id ?? '',
body: plainHtmlContent ?? category.description ?? '', body: plainHtmlContent ?? category.description ?? '',
bodySummary: category.translated?.metaDescription ?? category.description ?? '', bodySummary: category.translated?.metaDescription ?? category.description ?? '',
seo: { seo: {
title: category.translated?.metaTitle ?? category.name ?? '', title: category.translated?.metaTitle ?? category.name ?? '',
description: category.translated?.metaDescription ?? category.description ?? '' description: category.translated?.metaDescription ?? category.description ?? ''
}, },
createdAt: seoUrlElement.createdAt ?? '', createdAt: seoUrlElement?.createdAt ?? category.createdAt ?? '',
updatedAt: seoUrlElement.updatedAt ?? '', updatedAt: seoUrlElement?.updatedAt ?? category.updatedAt ?? '',
routeName: seoUrlElement.routeName, routeName: seoUrlElement?.routeName,
originalCmsPage: category.cmsPage, originalCmsPage: category.cmsPage,
foreignKey: seoUrlElement.foreignKey foreignKey: seoUrlElement?.foreignKey ?? category.id
}; };
} }
@ -89,18 +95,22 @@ export function transformToPlainHtmlContent(cmsPage: ExtendedCmsPage): string {
} }
export function transformCollection( export function transformCollection(
seoUrlElement: ApiSchemas['SeoUrl'], resCategory: ExtendedCategory,
resCategory: ExtendedCategory seoUrlElement?: ApiSchemas['SeoUrl']
) { ) {
return { return {
handle: seoUrlElement.seoPathInfo, handle: seoUrlElement?.seoPathInfo ?? resCategory.id ?? '',
title: resCategory.translated?.metaTitle ?? resCategory.name ?? '', title: resCategory.translated?.metaTitle ?? resCategory.name ?? '',
description: resCategory.description ?? '', description: resCategory.description ?? '',
seo: { seo: {
title: resCategory.translated?.metaTitle ?? resCategory.name ?? '', title: resCategory.translated?.metaTitle ?? resCategory.name ?? '',
description: resCategory.translated?.metaDescription ?? resCategory.description ?? '' description: resCategory.translated?.metaDescription ?? resCategory.description ?? ''
}, },
updatedAt: seoUrlElement.updatedAt ?? seoUrlElement.createdAt ?? '' updatedAt:
seoUrlElement?.updatedAt ??
seoUrlElement?.createdAt ??
resCategory.updatedAt ??
resCategory.createdAt
}; };
} }
@ -116,7 +126,10 @@ export function transformSubCollection(
.filter((item) => item.visible) .filter((item) => item.visible)
.filter((item) => item.type !== 'link') .filter((item) => item.type !== 'link')
.map((item) => { .map((item) => {
const handle = item.seoUrls ? findHandle(item.seoUrls, parentCollectionName) : undefined; const handle =
item.seoUrls && `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'
? findHandle(item.seoUrls, parentCollectionName)
: item.id;
if (handle) { if (handle) {
collection.push({ collection.push({
handle: handle, handle: handle,
@ -183,15 +196,21 @@ export function transformProducts(res: ExtendedProductListingResult): Product[]
} }
export function transformProduct(item: ExtendedProduct): Product { export function transformProduct(item: ExtendedProduct): Product {
const useSeoUrls = `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true';
const productOptions = transformOptions(item); const productOptions = transformOptions(item);
const productVariants = transformVariants(item); const productVariants = transformVariants(item);
return { let path = item.id ? item.id : '';
id: item.id ?? '', if (useSeoUrls) {
path: path =
item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo
? item.seoUrls[0].seoPathInfo ? item.seoUrls[0].seoPathInfo
: '', : '';
}
return {
id: item.id ?? '',
path: path,
availableForSale: item.available ?? false, availableForSale: item.available ?? false,
title: item.translated ? item.translated.name ?? '' : item.name, title: item.translated ? item.translated.name ?? '' : item.name,
description: item.translated?.metaDescription description: item.translated?.metaDescription