From 6e2ef6f62c913eb216d3c50eb307abd5db066860 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Tue, 6 Jan 2026 09:22:57 +0330 Subject: [PATCH] slugify --- .../restaurants/dto/create-restaurant.dto.ts | 5 +- .../providers/restaurants.service.ts | 52 ++++++++++++++++--- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/modules/restaurants/dto/create-restaurant.dto.ts b/src/modules/restaurants/dto/create-restaurant.dto.ts index aeace68..0a5e14a 100644 --- a/src/modules/restaurants/dto/create-restaurant.dto.ts +++ b/src/modules/restaurants/dto/create-restaurant.dto.ts @@ -8,9 +8,10 @@ export class CreateRestaurantDto { @IsString() name!: string; - @ApiProperty({ example: 'zhivan', description: 'شناسه (slug) رستوران، لاتین و بدون فاصله' }) + @ApiPropertyOptional({ example: 'zhivan', description: 'شناسه (slug) رستوران، لاتین و بدون فاصله (اختیاری - در صورت عدم ارسال، از نام رستوران تولید می‌شود)' }) + @IsOptional() @IsString() - slug!: string; + slug?: string; @ApiPropertyOptional({ example: 2020, description: 'سال تأسیس' }) @IsOptional() diff --git a/src/modules/restaurants/providers/restaurants.service.ts b/src/modules/restaurants/providers/restaurants.service.ts index 4c3c888..25a6c73 100644 --- a/src/modules/restaurants/providers/restaurants.service.ts +++ b/src/modules/restaurants/providers/restaurants.service.ts @@ -13,6 +13,7 @@ import { Admin } from '../../admin/entities/admin.entity'; import { AdminRole } from '../../admin/entities/adminRole.entity'; import { Role } from '../../roles/entities/role.entity'; import { normalizePhone } from 'src/modules/utils/phone.util'; +import slugify from 'slugify'; import { NotificationPreference } from '../../notifications/entities/notification-preference.entity'; import { notificationPreferencesData } from '../../../seeders/data/notification-preferences.data'; import { Order } from '../../orders/entities/order.entity'; @@ -31,11 +32,25 @@ export class RestaurantsService { async setupRestuarant(dto: CreateRestaurantDto): Promise { return await this.em.transactional(async (em) => { - // Validate unique constraints - const validateSlug = await em.findOne(Restaurant, { slug: dto.slug }) - if (validateSlug) { - throw new BadRequestException("Duplicate slug: " + dto.slug) + // Generate or validate slug + let slug = dto.slug; + if (!slug) { + // Generate slug from restaurant name + slug = slugify(dto.name, { + lower: true, + strict: true, + locale: 'fa' + }); } + + // Ensure slug uniqueness + let finalSlug = slug; + let counter = 1; + while (await em.findOne(Restaurant, { slug: finalSlug })) { + finalSlug = `${slug}-${counter}`; + counter++; + } + const validateSubscriptionId = await em.findOne(Restaurant, { subscriptionId: dto.subscriptionId }) if (validateSubscriptionId) { throw new BadRequestException("Duplicate subscriptionId: " + dto.subscriptionId) @@ -44,12 +59,12 @@ export class RestaurantsService { // Create restaurant const restaurant = em.create(Restaurant, { name: dto.name, - slug: dto.slug, + slug: finalSlug, establishedYear: dto.establishedYear, phone: dto.phone, isActive: true, plan: dto.plan, - domain: `https://dmenu-plus-front.dev.danakcorp.com/${dto.slug}`, + domain: `https://dmenu-plus-front.dev.danakcorp.com/${finalSlug}`, subscriptionId: dto.subscriptionId, subscriptionEndDate: dto.subscriptionEndDate, subscriptionStartDate: dto.subscriptionStartDate, @@ -149,6 +164,31 @@ export class RestaurantsService { throw new NotFoundException(RestMessage.NOT_FOUND); } + // Handle slug generation/update + if (dto.slug !== undefined || dto.name !== undefined) { + let slug = dto.slug; + + if (!slug && dto.name) { + // Generate slug from restaurant name if slug not provided but name is + slug = slugify(dto.name, { + lower: true, + strict: true, + locale: 'fa' + }); + } + + // Ensure slug uniqueness if slug is being updated + if (slug) { + let finalSlug = slug; + let counter = 1; + while (await this.em.findOne(Restaurant, { slug: finalSlug, id: { $ne: id } })) { + finalSlug = `${slug}-${counter}`; + counter++; + } + dto.slug = finalSlug; + } + } + this.restRepository.assign(restaurant, dto); await this.em.persistAndFlush(restaurant);