Skip to content

Feature Implementation Plan: Add Slugs to Entities

📋 Todo Checklist

  • [ ] Create a new database migration to add slug columns.
  • [ ] Update CoffeeBean and Roaster entities with the new slug field.
  • [ ] Create a SlugService to generate unique slugs.
  • [ ] Integrate SlugService into entities or listeners to generate slugs on create/update.
  • [ ] Create new API endpoints to fetch entities by slug.
  • [ ] Update existing API responses to include the slug.
  • [ ] Write tests for the new functionality.
  • [ ] Final Review and Testing

🔍 Analysis & Investigation

Codebase Structure

The project is a Symfony application. Key directories are: - src/Entity: Contains Doctrine entities (e.g., CoffeeBean.php, Roaster.php). - src/Repository: Contains Doctrine repositories for data access. - src/Controller/Api: Contains API controllers. - migrations: Contains Doctrine database migrations.

Current Architecture

The application uses a standard Symfony architecture with Doctrine for the ORM and API Platform (or custom controllers) for the API. Entities are mapped to the database, and repositories are used to query data. Controllers handle HTTP requests and responses. There is no existing slug generation library in composer.json, so a custom solution will be needed.

Dependencies & Integration Points

  • Doctrine: The CoffeeBean and Roaster entities will be modified to include the slug field. A new migration will be required.
  • Symfony Serializer: API responses will need to be updated to include the slug.
  • Symfony Routing: New routes will be added for the slug-based endpoints.

Considerations & Challenges

  • Uniqueness: The slug generation logic must handle non-unique slugs by appending a suffix (e.g., -2, -3).
  • Backfilling: Existing entities in the database will need to have slugs generated for them. This can be done in the migration or with a separate command.
  • Performance: The by-slug endpoints should be fast. Adding a unique index to the slug column is crucial.

📝 Implementation Plan

Prerequisites

  • Ensure you have a local development environment set up with a database.
  • Make sure you can run Doctrine migrations.

Step-by-Step Implementation

  1. Create a new Doctrine migration:
  2. Files to modify: a new file in migrations/
  3. Changes needed:

    • Run php bin/console make:migration to create a new migration file.
    • In the generated migration file, add SQL to create the slug column on the coffee_beans and roasters tables.
    • The columns should be VARCHAR(255) and have a UNIQUE index.
    • Add a post-migration step to generate slugs for existing records.
  4. Update Entities:

  5. Files to modify: src/Entity/CoffeeBean.php, src/Entity/Roaster.php
  6. Changes needed:

    • Add a private $slug property to both entities.
    • Add #[ORM\Column(type: 'string', length: 255, unique: true)] attribute to the $slug property.
    • Add getter and setter methods for the slug.
  7. Create a Slug Service:

  8. Files to modify: src/Service/SlugService.php (new file)
  9. Changes needed:

    • Create a new service class SlugService.
    • Implement a generate method that takes a string and returns a URL-friendly slug.
    • Implement a createUniqueSlug method that takes an entity and a repository, generates a slug, and checks for uniqueness, appending a suffix if necessary.
  10. Integrate Slug Generation:

  11. Files to modify: src/Entity/CoffeeBean.php, src/Entity/Roaster.php
  12. Changes needed:

    • Add a #[ORM\PrePersist] and #[ORM\PreUpdate] lifecycle callback method to both entities.
    • In this method, use the SlugService to generate and set the slug before the entity is saved.
    • Alternatively, create a Doctrine event listener to handle this logic.
  13. Create New API Endpoints:

  14. Files to modify: src/Controller/Api/CoffeeBeanController.php, src/Controller/Api/RoasterController.php
  15. Changes needed:

    • In CoffeeBeanController, add a new method getBySlug(string $slug) with a route GET /api/coffee-beans/by-slug/{slug}.
    • In this method, use the CoffeeBeanRepository to find the entity by slug and return it.
    • In RoasterController, add a similar getBySlug method.
  16. Update Existing API Responses:

  17. Files to modify: src/Controller/Api/CoffeeBeanController.php, src/Controller/Api/RoasterController.php
  18. Changes needed:
    • Ensure the slug field is included in the JSON responses for all existing endpoints that return CoffeeBean or Roaster objects. This might involve updating serialization groups if they are used.

Testing Strategy

  • Unit Tests:
  • Write unit tests for the SlugService to ensure it generates slugs correctly and handles uniqueness.
  • Integration Tests:
  • Write integration tests for the new API endpoints to ensure they return the correct entities.
  • Write tests to verify that slugs are generated and saved correctly when creating and updating entities.
  • Test the uniqueness constraint by trying to create entities with the same name.

🎯 Success Criteria

  • The slug columns are added to the coffee_beans and roasters tables with unique indexes.
  • Slugs are automatically generated and saved when a CoffeeBean or Roaster is created or updated.
  • The new GET /api/coffee-beans/by-slug/{slug} and GET /api/roasters/by-slug/{slug} endpoints work correctly.
  • The slug field is present in all API responses that include CoffeeBean or Roaster data.
  • All tests pass.