# Examples and Tutorials

## 📚 Examples and Tutorials

### Complete Example: Blog Application

```typescript
// Models: User, Post, Comment, Tag

@Injectable()
export class BlogService {
    constructor(private readonly repository: AppRepository) {}

    // Get popular posts with authors and comment counts
    async getPopularPosts(limit: number = 10) {
        const posts = await this.repository.post
            .Include({
                user: { select: { id: true, name: true } },
                comments: { select: { id: true } },
            })
            .Where({ published: true })
            .ToEnumerable();

        return posts
            .Select((post) => ({
                id: post.id,
                title: post.title,
                author: post.user.name,
                commentCount: post.comments.length,
                publishedAt: post.publishedAt,
            }))
            .OrderByDescending((post) => post.commentCount)
            .ThenByDescending((post) => post.publishedAt)
            .Take(limit)
            .ToArray();
    }

    // Advanced: Get user engagement analytics (Prisma ORM with transactions)
    async getUserEngagementAnalytics(userId: number) {
        return await this.repository.user.Transaction(async (tx) => {
            const user = await tx.Where({ id: userId }).First();
            if (!user) throw new Error('User not found');

            const posts = await this.repository.post
                .Where({ userId })
                .Include({ comments: true })
                .ToEnumerable();

            const analytics = posts
                .GroupBy((post) => post.createdAt.getMonth())
                .Select((monthGroup) => ({
                    month: monthGroup.Key,
                    postCount: monthGroup.Count(),
                    totalComments: monthGroup.Sum(
                        (post) => post.comments.length,
                    ),
                    averageCommentsPerPost: monthGroup.Average(
                        (post) => post.comments.length,
                    ),
                }))
                .OrderBy((stat) => stat.month)
                .ToArray();

            const totalEngagement = posts.Sum((post) => post.comments.length);

            return {
                user: { id: user.id, name: user.name },
                monthlyStats: analytics,
                totalEngagement,
                postsPublished: posts.Count(),
                averageEngagement: totalEngagement / posts.Count(),
            };
        });
    }
}
```

### Real-World Performance Example

```typescript
@Injectable()
export class AnalyticsService {
    async generateMonthlyReport(year: number, month: number) {
        // Start benchmark
        const benchmark = BenchmarkUtils.createBenchmark('Monthly Report');

        try {
            const startDate = new Date(year, month - 1, 1);
            const endDate = new Date(year, month, 0);

            // Parallel data fetching
            const [users, posts, comments] = await Promise.all([
                this.repository.user
                    .Where({
                        createdAt: {
                            gte: startDate,
                            lte: endDate,
                        },
                    })
                    .ToEnumerable(),

                this.repository.post
                    .Where({
                        publishedAt: {
                            gte: startDate,
                            lte: endDate,
                        },
                    })
                    .Include({ comments: true })
                    .ToEnumerable(),

                this.repository.comment
                    .Where({
                        createdAt: {
                            gte: startDate,
                            lte: endDate,
                        },
                    })
                    .ToEnumerable(),
            ]);

            // Use AsyncEnumerable for heavy processing
            const userStats = await AsyncEnumerable.from(users.ToArray())
                .Select(async (user) => ({
                    id: user.id,
                    name: user.name,
                    postsCount: await this.repository.post
                        .Where({ userId: user.id })
                        .Count(),
                    engagementScore: await this.calculateEngagementScore(
                        user.id,
                    ),
                }))
                .Where(async (stat) => stat.postsCount > 0)
                .OrderByDescending((stat) => stat.engagementScore)
                .ToArrayAsync();

            const report = {
                period: { year, month },
                userStats,
                totalUsers: users.Count(),
                totalPosts: posts.Count(),
                totalComments: comments.Count(),
                topPerformers: userStats.take(10),
            };

            benchmark.end();
            console.log(`Report generated in ${benchmark.duration}ms`);

            return report;
        } catch (error) {
            benchmark.error(error);
            throw error;
        }
    }
}
```

### E-commerce Example

```typescript
@Injectable()
export class EcommerceService {
    constructor(private readonly repository: AppRepository) {}

    // Get product recommendations based on user behavior
    async getProductRecommendations(userId: number, limit: number = 10) {
        // Get user's order history
        const userOrders = await this.repository.order
            .Where({ userId })
            .Include({ orderItems: { include: { product: true } } })
            .ToEnumerable();

        // Extract categories and brands user has purchased
        const purchasedCategories = userOrders
            .SelectMany((order) => order.orderItems)
            .Select((item) => item.product.categoryId)
            .Distinct()
            .ToArray();

        const purchasedBrands = userOrders
            .SelectMany((order) => order.orderItems)
            .Select((item) => item.product.brandId)
            .Distinct()
            .ToArray();

        // Get products from similar categories/brands that user hasn't bought
        const purchasedProductIds = userOrders
            .SelectMany((order) => order.orderItems)
            .Select((item) => item.productId)
            .Distinct()
            .ToArray();

        const recommendations = await this.repository.product
            .Where({
                OR: [
                    { categoryId: { in: purchasedCategories } },
                    { brandId: { in: purchasedBrands } },
                ],
                id: { notIn: purchasedProductIds },
                isActive: true,
            })
            .Include({ reviews: true })
            .ToEnumerable();

        return recommendations
            .Select((product) => ({
                id: product.id,
                name: product.name,
                price: product.price,
                rating:
                    product.reviews.length > 0
                        ? product.reviews.reduce(
                              (sum, r) => sum + r.rating,
                              0,
                          ) / product.reviews.length
                        : 0,
                reviewCount: product.reviews.length,
                recommendationScore: this.calculateRecommendationScore(
                    product,
                    purchasedCategories,
                    purchasedBrands,
                ),
            }))
            .Where((p) => p.rating >= 3.5) // Only recommend well-rated products
            .OrderByDescending((p) => p.recommendationScore)
            .ThenByDescending((p) => p.rating)
            .Take(limit)
            .ToArray();
    }

    private calculateRecommendationScore(
        product: any,
        userCategories: number[],
        userBrands: number[],
    ): number {
        let score = 0;

        if (userCategories.includes(product.categoryId)) score += 10;
        if (userBrands.includes(product.brandId)) score += 8;

        // Add points based on popularity
        score += Math.min(product.reviews.length / 10, 5);

        return score;
    }

    // Generate sales analytics
    async generateSalesAnalytics(startDate: Date, endDate: Date) {
        const orders = await this.repository.order
            .Where({
                createdAt: { gte: startDate, lte: endDate },
                status: 'COMPLETED',
            })
            .Include({
                orderItems: { include: { product: true } },
                user: true,
            })
            .ToEnumerable();

        // Sales by category
        const salesByCategory = orders
            .SelectMany((order) => order.orderItems)
            .GroupBy((item) => item.product.categoryId)
            .Select((group) => ({
                categoryId: group.Key,
                totalSales: group.Sum((item) => item.quantity * item.price),
                totalQuantity: group.Sum((item) => item.quantity),
                orderCount: group.Count(),
            }))
            .OrderByDescending((cat) => cat.totalSales)
            .ToArray();

        // Top customers
        const topCustomers = orders
            .GroupBy((order) => order.userId)
            .Select((group) => ({
                userId: group.Key,
                userName: group.First().user.name,
                totalSpent: group.Sum((order) => order.total),
                orderCount: group.Count(),
                averageOrderValue: group.Average((order) => order.total),
            }))
            .OrderByDescending((customer) => customer.totalSpent)
            .Take(20)
            .ToArray();

        // Daily sales trend
        const dailySales = orders
            .GroupBy((order) => order.createdAt.toDateString())
            .Select((group) => ({
                date: group.Key,
                totalSales: group.Sum((order) => order.total),
                orderCount: group.Count(),
                averageOrderValue: group.Average((order) => order.total),
            }))
            .OrderBy((day) => day.date)
            .ToArray();

        return {
            summary: {
                totalRevenue: orders.Sum((order) => order.total),
                totalOrders: orders.Count(),
                averageOrderValue: orders.Average((order) => order.total),
                uniqueCustomers: orders
                    .Select((order) => order.userId)
                    .Distinct()
                    .Count(),
            },
            salesByCategory,
            topCustomers,
            dailySales,
        };
    }
}
```

### GraphQL Integration Example

```typescript
@Injectable()
export class GraphQLExampleService {
    constructor(private readonly repository: AppRepository) {}

    // Example using GraphQL data source
    async getUserProfilesFromGraphQL() {
        // Fetch user data from GraphQL endpoint
        const users = await this.repository.user
            .Where({ isActive: true })
            .Select({
                id: true,
                name: true,
                email: true,
                profile: {
                    select: {
                        bio: true,
                        avatar: true,
                        location: true,
                    },
                },
                posts: {
                    select: {
                        id: true,
                        title: true,
                        createdAt: true,
                    },
                },
            })
            .ToArrayAsync();

        // Process with LINQ operations
        const processedProfiles = await AsyncEnumerable.from(users)
            .Where((user) => user.profile != null)
            .Select(async (user) => ({
                id: user.id,
                name: user.name,
                email: user.email,
                profileComplete: this.calculateProfileCompleteness(
                    user.profile,
                ),
                recentPostsCount: user.posts.filter(
                    (post) =>
                        new Date(post.createdAt) >
                        new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
                ).length,
                engagementLevel: await this.calculateEngagementLevel(user.id),
            }))
            .Where((profile) => profile.profileComplete > 50)
            .OrderByDescending((profile) => profile.engagementLevel)
            .ToArrayAsync();

        return processedProfiles;
    }

    private calculateProfileCompleteness(profile: any): number {
        let completeness = 0;
        if (profile.bio) completeness += 30;
        if (profile.avatar) completeness += 30;
        if (profile.location) completeness += 40;
        return completeness;
    }

    private async calculateEngagementLevel(userId: number): Promise<number> {
        // This could be another GraphQL query or calculation
        return Math.random() * 100; // Placeholder
    }
}
```

### Data Migration Example

```typescript
@Injectable()
export class DataMigrationService {
    constructor(private readonly repository: AppRepository) {}

    // Migrate data with validation and error handling
    async migrateUserData(sourceData: any[]) {
        const migrationResults = {
            successful: 0,
            failed: 0,
            errors: [] as string[],
        };

        // Process in batches for better performance
        const batchSize = 100;
        const batches = Enumerable.from(sourceData)
            .Select((item, index) => ({ item, index }))
            .GroupBy((x) => Math.floor(x.index / batchSize))
            .Select((group) => group.Select((x) => x.item).ToArray())
            .ToArray();

        for (const batch of batches) {
            try {
                await this.repository.user.Transaction(async (tx) => {
                    const processedBatch = await AsyncEnumerable.from(batch)
                        .Where(
                            async (user) => await this.validateUserData(user),
                        )
                        .Select(
                            async (user) => await this.transformUserData(user),
                        )
                        .ToArrayAsync();

                    // Create users in batch
                    const results = await tx.CreateMany(processedBatch);
                    migrationResults.successful += results.count;
                });
            } catch (error) {
                migrationResults.failed += batch.length;
                migrationResults.errors.push(`Batch failed: ${error.message}`);
            }
        }

        return migrationResults;
    }

    private async validateUserData(userData: any): Promise<boolean> {
        // Validate required fields
        if (!userData.email || !userData.name) {
            return false;
        }

        // Check for duplicates
        const existingUser = await this.repository.user
            .Where({ email: userData.email })
            .First();

        return existingUser === null;
    }

    private async transformUserData(userData: any): Promise<any> {
        return {
            email: userData.email.toLowerCase(),
            name: userData.name.trim(),
            isActive: userData.status === 'active',
            createdAt: new Date(userData.created_date || Date.now()),
            profile: userData.profile
                ? {
                      create: {
                          bio: userData.profile.bio,
                          avatar: userData.profile.avatar_url,
                      },
                  }
                : undefined,
        };
    }
}
```

### Testing Examples

```typescript
describe('PrismaCoreService', () => {
    let service: TestService;
    let repository: AppRepository;

    beforeEach(async () => {
        // Setup test module with in-memory database
        const module = await Test.createTestModule({
            imports: [
                PrismaCoreModule.forRoot({
                    prismaClientProvider: {
                        provide: 'PRISMA_CLIENT',
                        useFactory: () =>
                            new PrismaClient({
                                datasources: {
                                    db: { url: 'file:./test.db' },
                                },
                            }),
                    },
                }),
            ],
            providers: [TestService, AppRepository],
        }).compile();

        service = module.get<TestService>(TestService);
        repository = module.get<AppRepository>(AppRepository);
    });

    it('should filter and transform data correctly', async () => {
        // Arrange
        const testData = await repository.test.CreateMany([
            { name: 'Test 1', description: 'Description 1', isActive: true },
            { name: 'Test 2', description: null, isActive: true },
            { name: 'Test 3', description: 'Description 3', isActive: false },
        ]);

        // Act
        const results = await repository.test
            .Where({ isActive: true })
            .ToEnumerable()
            .Where((test) => test.description != null)
            .Select((test) => ({
                id: test.id,
                displayName: test.name.toUpperCase(),
                hasDescription: true,
            }))
            .ToArray();

        // Assert
        expect(results).toHaveLength(1);
        expect(results[0].displayName).toBe('TEST 1');
        expect(results[0].hasDescription).toBe(true);
    });

    it('should handle async operations correctly', async () => {
        // Test with AsyncEnumerable
        const data = [1, 2, 3, 4, 5];

        const results = await AsyncEnumerable.from(data)
            .Where(async (x) => x % 2 === 0)
            .Select(async (x) => x * 2)
            .ToArrayAsync();

        expect(results).toEqual([4, 8]);
    });

    afterEach(async () => {
        // Clean up test data
        await repository.test.DeleteMany({});
    });
});
```

## Next Steps

- [Learn about contributing](./contributing.md)
- [Review the license](./LICENSE.md)
- [Check the API reference](./api-reference.md)
