> ## Documentation Index
> Fetch the complete documentation index at: https://docs.laravelshopper.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Product Types

> TypeScript type definitions for Product, ProductVariant, and ProductTag models.

## Product

The `Product` interface represents a product in your catalog.

```typescript theme={null}
import type { Product } from '@shopperlabs/shopper-types'
```

### Definition

```typescript theme={null}
interface Product extends Entity, SEOFields, ShippingFields {
  name: string
  slug: string
  sku?: string | null
  barcode: string | null
  summary: string | null
  description: string | null
  security_stock: number | null
  featured: boolean
  is_visible: boolean
  type: ProductType | null
  published_at: DateEntity | null
  external_id: string | null
  stock: number
  variants_stock?: number
  supplier_id?: number | null
  brand_id: number | null
  metadata: Metadata

  // Relationships
  supplier?: Supplier
  brand?: Brand
  channels?: Channel[]
  categories?: Category[]
  options?: Attribute[]
  collections?: Collection[]
  tags?: ProductTag[]
  variants?: ProductVariant[]
  relatedProducts?: Product[]
  images?: Media[] | null
  reviews?: Review[]
  prices?: Price[]
}
```

### ProductType Enum

```typescript theme={null}
enum ProductType {
  EXTERNAL = 'external',
  VIRTUAL = 'virtual',
  STANDARD = 'standard',
  VARIANT = 'variant',
}
```

### Usage Examples

```typescript theme={null}
import type { Product } from '@shopperlabs/shopper-types'
import { ProductType } from '@shopperlabs/shopper-types'

function isVariantProduct(product: Product): boolean {
  return product.type === ProductType.VARIANT
}

async function fetchProduct(slug: string): Promise<Product> {
  const response = await fetch(`/api/products/${slug}`)
  return response.json()
}

function isInStock(product: Product): boolean {
  if (product.type === ProductType.VARIANT) {
    return (product.variants_stock ?? 0) > 0
  }
  return product.stock > 0
}

function getProductImage(product: Product): string | null {
  return product.images?.[0]?.url ?? null
}
```

***

## ProductVariant

The `ProductVariant` interface represents a variant of a product (size, color, etc.).

```typescript theme={null}
import type { ProductVariant } from '@shopperlabs/shopper-types'
```

### Definition

```typescript theme={null}
interface ProductVariant extends Entity, ShippingFields {
  name: string
  sku?: string | null
  barcode: string | null
  ean: string | null
  upc: string | null
  position: number
  allow_backorder: boolean
  product_id: number
  stock: number
  metadata: Metadata

  // Relationships
  product?: Product
  values?: AttributeValue[]
  images?: Media[] | null
  prices?: Price[]
}
```

### Usage Examples

```typescript theme={null}
import type { Product, ProductVariant } from '@shopperlabs/shopper-types'

function findVariant(
  product: Product,
  attributes: Record<string, string>
): ProductVariant | undefined {
  return product.variants?.find((variant) => {
    return variant.values?.every((value) => {
      const attr = value.attribute
      return attr && attributes[attr.slug] === value.key
    })
  })
}

function isVariantAvailable(variant: ProductVariant): boolean {
  return variant.stock > 0 || variant.allow_backorder
}

function getVariantPrice(variant: ProductVariant): number | null {
  return variant.prices?.[0]?.amount ?? null
}
```

***

## ProductTag

The `ProductTag` interface represents a simple label attached to products.

```typescript theme={null}
import type { ProductTag } from '@shopperlabs/shopper-types'
```

### Definition

```typescript theme={null}
interface ProductTag extends Entity {
  name: string
  slug: string | null

  // Relationships
  products?: Product[]
}
```

### Usage Examples

```typescript theme={null}
import type { Product, ProductTag } from '@shopperlabs/shopper-types'

function getProductsByTag(products: Product[], tagSlug: string): Product[] {
  return products.filter((product) =>
    product.tags?.some((tag) => tag.slug === tagSlug)
  )
}

function getTagNames(product: Product): string[] {
  return product.tags?.map((tag) => tag.name) ?? []
}
```
