Claude Code Plugins

Community-maintained marketplace

Feedback

Comprehensive Symfony 6.4 development skill for creating web applications, APIs, and microservices. Provides workflows, best practices, and tools for efficient Symfony development including controllers, routing, database operations with Doctrine, forms, security, testing, and deployment.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name symfony-framework
description Comprehensive Symfony 6.4 development skill for creating web applications, APIs, and microservices. Provides workflows, best practices, and tools for efficient Symfony development including controllers, routing, database operations with Doctrine, forms, security, testing, and deployment.
license MIT
version 1.0.0

Symfony Framework Development Skill

This skill provides comprehensive guidance for developing applications with Symfony 6.4, the leading PHP framework for web applications and APIs.

Quick Start Workflow

1. Project Initialization

For a new Symfony project:

# Full web application
symfony new project_name --version="6.4.*" --webapp

# API/Microservice
symfony new project_name --version="6.4.*"

# If Symfony CLI not available, use Composer
composer create-project symfony/skeleton:"6.4.*" project_name

2. Development Server

symfony server:start
# Access at http://localhost:8000

Core Development Patterns

Controller Creation

Always extend AbstractController and use attributes for routing:

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class ProductController extends AbstractController
{
    #[Route('/products', name: 'product_list')]
    public function list(): Response
    {
        return $this->render('product/list.html.twig', [
            'products' => $products,
        ]);
    }
    
    #[Route('/products/{id}', name: 'product_show')]
    public function show(Product $product): Response
    {
        // Automatic entity parameter conversion
        return $this->render('product/show.html.twig', [
            'product' => $product,
        ]);
    }
}

Service Configuration

Use autowiring by default in config/services.yaml:

services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false
    
    App\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Kernel.php'

Database Operations with Doctrine

Entity Creation

php bin/console make:entity Product

Migration Workflow

# Generate migration
php bin/console make:migration

# Execute migration
php bin/console doctrine:migrations:migrate

Repository Pattern

// In controller
public function index(ProductRepository $repository): Response
{
    $products = $repository->findBy(['active' => true]);
    // Custom repository methods
    $featured = $repository->findFeaturedProducts();
}

Form Handling

Build forms in dedicated classes:

namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ProductType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name')
            ->add('price')
            ->add('description')
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Product::class,
        ]);
    }
}

Controller handling:

#[Route('/product/new', name: 'product_new')]
public function new(Request $request, EntityManagerInterface $em): Response
{
    $product = new Product();
    $form = $this->createForm(ProductType::class, $product);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $em->persist($product);
        $em->flush();
        
        return $this->redirectToRoute('product_show', ['id' => $product->getId()]);
    }

    return $this->render('product/new.html.twig', [
        'form' => $form,
    ]);
}

Security Implementation

User Authentication

# config/packages/security.yaml
security:
    password_hashers:
        App\Entity\User:
            algorithm: auto
    
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    
    firewalls:
        main:
            form_login:
                login_path: app_login
                check_path: app_login
            logout:
                path: app_logout

Authorization with Voters

namespace App\Security\Voter;

use Symfony\Component\Security\Core\Authorization\Voter\Voter;

class ProductVoter extends Voter
{
    public const EDIT = 'PRODUCT_EDIT';
    public const VIEW = 'PRODUCT_VIEW';

    protected function supports(string $attribute, mixed $subject): bool
    {
        return in_array($attribute, [self::EDIT, self::VIEW])
            && $subject instanceof Product;
    }

    protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
    {
        $user = $token->getUser();
        
        return match($attribute) {
            self::VIEW => true,
            self::EDIT => $user && $subject->getOwner() === $user,
            default => false,
        };
    }
}

API Development

JSON Responses

#[Route('/api/products', name: 'api_products')]
public function apiList(ProductRepository $repository): JsonResponse
{
    $products = $repository->findAll();
    
    return $this->json($products, 200, [], [
        'groups' => ['product:read']
    ]);
}

Request Validation

#[Route('/api/product', methods: ['POST'])]
public function create(
    #[MapRequestPayload] ProductDto $dto,
    ValidatorInterface $validator
): JsonResponse {
    $errors = $validator->validate($dto);
    
    if (count($errors) > 0) {
        return $this->json($errors, 400);
    }
    
    // Process valid data
}

Essential Commands

Development

# Clear cache
php bin/console cache:clear

# Show routes
php bin/console debug:router

# Show services
php bin/console debug:container

# Show configuration
php bin/console debug:config framework

# Create controller
php bin/console make:controller

# Create CRUD
php bin/console make:crud Product

Database

# Create database
php bin/console doctrine:database:create

# Update schema (dev only)
php bin/console doctrine:schema:update --force

# Load fixtures
php bin/console doctrine:fixtures:load

Testing

# Run all tests
php bin/phpunit

# Specific test file
php bin/phpunit tests/Controller/ProductControllerTest.php

# With coverage
php bin/phpunit --coverage-html coverage/

Directory Structure

project/
├── assets/           # Frontend assets (JS, CSS)
├── bin/              # Executables (console, phpunit)
├── config/           # Configuration files
│   ├── packages/     # Package-specific config
│   ├── routes/       # Routing configuration
│   └── services.yaml # Service definitions
├── migrations/       # Database migrations
├── public/           # Web root
│   └── index.php     # Front controller
├── src/              # Application code
│   ├── Controller/   # Controllers
│   ├── Entity/       # Doctrine entities
│   ├── Form/         # Form types
│   ├── Repository/   # Doctrine repositories
│   └── Service/      # Business logic
├── templates/        # Twig templates
├── tests/            # Test suites
├── translations/     # Translation files
├── var/              # Generated files (cache, logs)
└── vendor/           # Dependencies

Performance Optimization

Caching Strategy

# config/packages/cache.yaml
framework:
    cache:
        pools:
            cache.product:
                adapter: cache.adapter.redis
                default_lifetime: 3600

Usage:

public function __construct(private CacheInterface $productCache) {}

public function getProduct(int $id): ?Product
{
    return $this->productCache->get(
        'product_' . $id,
        function (ItemInterface $item) use ($id) {
            $item->expiresAfter(3600);
            return $this->repository->find($id);
        }
    );
}

Query Optimization

// Eager loading with Doctrine
$products = $repository->createQueryBuilder('p')
    ->leftJoin('p.category', 'c')
    ->addSelect('c')
    ->leftJoin('p.tags', 't')
    ->addSelect('t')
    ->getQuery()
    ->getResult();

Error Handling

// In controller
if (!$product) {
    throw $this->createNotFoundException('Product not found');
}

// Custom exception
throw new BadRequestHttpException('Invalid product data');

// API error response
return $this->json([
    'error' => 'Invalid request',
    'details' => $errors
], Response::HTTP_BAD_REQUEST);

Deployment Checklist

  1. Environment setup

    APP_ENV=prod
    APP_DEBUG=0
    
  2. Optimize autoloader

    composer install --no-dev --optimize-autoloader
    
  3. Clear and warm cache

    php bin/console cache:clear --env=prod
    php bin/console cache:warmup --env=prod
    
  4. Compile assets

    npm run build
    
  5. Run migrations

    php bin/console doctrine:migrations:migrate --env=prod
    

Testing Patterns

Functional Tests

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class ProductControllerTest extends WebTestCase
{
    public function testListProducts(): void
    {
        $client = static::createClient();
        $client->request('GET', '/products');
        
        $this->assertResponseIsSuccessful();
        $this->assertSelectorTextContains('h1', 'Products');
    }
    
    public function testCreateProduct(): void
    {
        $client = static::createClient();
        $client->request('POST', '/api/products', [], [], [
            'CONTENT_TYPE' => 'application/json'
        ], json_encode(['name' => 'Test Product']));
        
        $this->assertResponseStatusCodeSame(201);
    }
}

Common Patterns to Follow

  1. Always use dependency injection - Never instantiate services manually
  2. Prefer composition over inheritance - Use services and traits
  3. Keep controllers thin - Move business logic to services
  4. Use DTOs for API input/output - Decouple API from entities
  5. Implement repository pattern - Keep database queries in repositories
  6. Use voters for authorization - Centralize access control logic
  7. Cache expensive operations - Use Symfony's cache component
  8. Write tests first - TDD approach for critical features

Troubleshooting Guide

Common Issues

Issue: Services not autowiring

php bin/console debug:container ServiceName
# Check if service is properly registered

Issue: Route not found

php bin/console debug:router | grep pattern
# Verify route registration

Issue: Database connection errors

php bin/console doctrine:database:create
# Verify database credentials in .env

Issue: Template not found

  • Check template path relative to templates/
  • Verify file extension is .html.twig

When to Use Scripts

Refer to bundled scripts in scripts/ for:

  • Complex entity generation
  • Database migration helpers
  • Deployment automation
  • Performance profiling

Additional Resources

For detailed documentation on specific topics, load the appropriate reference file from references/:

  • references/doctrine-advanced.md - Complex ORM patterns
  • references/security-detailed.md - Advanced security configurations
  • references/api-platform.md - API Platform integration
  • references/testing-complete.md - Comprehensive testing strategies
  • references/performance-tuning.md - Performance optimization techniques