Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Doctrine Clock Bundle Laravel Package

chamber-orchestra/doctrine-clock-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Bundle Add the package to your project via Composer:

    composer require chamber-orchestra/doctrine-clock-bundle
    

    Enable the bundle in your config/bundles.php:

    return [
        // ...
        ChamberOrchestra\DoctrineClockBundle\DoctrineClockBundle::class => ['all' => true],
    ];
    
  2. Apply Attributes to Entities Use the provided attributes (#[CreateTimestamp] and #[UpdateTimestamp]) on any DateTimeInterface property in your Doctrine entities:

    use ChamberOrchestra\DoctrineClockBundle\Attribute\CreateTimestamp;
    use ChamberOrchestra\DoctrineClockBundle\Attribute\UpdateTimestamp;
    
    #[ORM\Entity]
    class Post
    {
        #[ORM\Column(type: 'datetime_immutable')]
        #[CreateTimestamp]
        private DateTimeImmutable $createdAt;
    
        #[ORM\Column(type: 'datetime_immutable')]
        #[UpdateTimestamp]
        private DateTimeImmutable $updatedAt;
    }
    
  3. Configure Symfony Clock (Optional) The bundle integrates with Symfony’s ClockInterface. If you’re using a custom clock (e.g., for testing), bind it to the ClockInterface service:

    # config/services.yaml
    services:
        Symfony\Component\Clock\ClockInterface: '@your_custom_clock_service'
    
  4. Verify Functionality Run migrations and test CRUD operations. Timestamps should auto-populate on entity creation/updates.


Implementation Patterns

Workflow: Entity Development

  1. New Entity Creation

    • Add #[CreateTimestamp] and #[UpdateTimestamp] to DateTimeImmutable properties.
    • Ensure the property type matches Doctrine’s datetime_immutable (recommended) or datetime.
  2. Existing Entity Migration

    • Add attributes to existing entities without altering database columns.
    • Run php bin/console doctrine:schema:validate to confirm no conflicts.
  3. Custom Timestamp Logic

    • Override default behavior by implementing ChamberOrchestra\DoctrineClockBundle\Contract\TimestampStrategyInterface and tagging it as a service:
      services:
          App\Service\CustomTimestampStrategy:
              tags: ['doctrine_clock.timestamp_strategy']
      
  4. Testing Entities

    • Use Symfony’s ClockInterface mock in tests to control timestamps:
      $this->clock = $this->createMock(ClockInterface::class);
      $this->clock->method('now')->willReturn(new DateTimeImmutable('2023-01-01'));
      $container->set(ClockInterface::class, $this->clock);
      

Integration Tips

  • Event Listeners: Combine with Doctrine lifecycle events (e.g., prePersist, preUpdate) for side effects:

    #[ORM\PrePersist]
    public function setCreatedAt(): void {
        $this->createdAt = $this->clock->now();
    }
    

    Note: Avoid redundancy—let the bundle handle timestamps unless custom logic is needed.

  • Precision Control: Use #[CreateTimestamp(precision: 6)] to set microsecond precision (default: 0).

  • Soft Deletes: Pair with gedmo/doctrine-extensions for deletedAt timestamps:

    #[ORM\Column(type: 'datetime_immutable', nullable: true)]
    #[UpdateTimestamp]
    #[Gedmo\SoftDeleteable(fieldName: 'deletedAt')]
    private ?DateTimeImmutable $deletedAt = null;
    

Gotchas and Tips

Pitfalls

  1. Attribute Targeting

    • Attributes must be applied to DateTimeInterface properties (e.g., DateTimeImmutable, DateTime). Using strings or other types will silently fail.
    • Fix: Validate property types during development with:
      assert($property->getType() instanceof DateTimeInterface);
      
  2. Clock Dependency

    • The bundle requires a ClockInterface service. If none is configured, it defaults to Symfony\Component\Clock\SystemClock.
    • Debug: Check for ClockNotFoundException if timestamps are missing.
  3. Doctrine Event Conflicts

    • Other Doctrine listeners (e.g., LifecycleCallbacks) may override timestamp behavior. Ensure no duplicate prePersist/preUpdate logic sets the same fields.
    • Tip: Use #[ORM\HasLifecycleCallbacks] sparingly when using this bundle.
  4. Database Migrations

    • Adding attributes to existing entities does not alter the database schema. If timestamps were previously NULL, ensure your app handles NULL checks:
      if ($entity->getCreatedAt() === null) {
          $entity->setCreatedAt($this->clock->now());
      }
      

Debugging

  • Log Timestamp Events: Enable Doctrine event logging in config/packages/doctrine.yaml:

    doctrine:
        orm:
            eventmanager: '@doctrine.eventmanager'
            logging: true
    

    Check logs for doctrine_clock entries.

  • Verify Service Binding: Dump the ClockInterface service to confirm it’s bound:

    php bin/console debug:container ClockInterface
    

Extension Points

  1. Custom Strategies

    • Implement TimestampStrategyInterface to modify default behavior (e.g., timezone conversion):
      class UTCTimestampStrategy implements TimestampStrategyInterface {
          public function getTimestamp(ClockInterface $clock): DateTimeImmutable {
              return $clock->now()->setTimezone(new DateTimeZone('UTC'));
          }
      }
      
    • Tag the service as doctrine_clock.timestamp_strategy.
  2. Attribute Customization

    • Extend attributes to add metadata (e.g., #[CreateTimestamp(precision: 6, timezone: 'Europe/Paris')]).
    • Note: Requires modifying the bundle’s Type/CreateTimestampType.php and Type/UpdateTimestampType.php.
  3. Testing Utilities

    • Create a test trait to mock the clock:
      trait DoctrineClockTestTrait {
          protected function mockClock(DateTimeImmutable $time): ClockInterface {
              $clock = $this->createMock(ClockInterface::class);
              $clock->method('now')->willReturn($time);
              $this->container->set(ClockInterface::class, $clock);
              return $clock;
          }
      }
      

Configuration Quirks

  • Symfony 6.4+: The bundle assumes Symfony’s ClockInterface is available. If using older versions, install symfony/clock via Composer.
  • Doctrine DBAL: The bundle works with both ORM and DBAL, but DBAL requires manual event listener setup (see EventSubscriber/DoctrineClockSubscriber).
  • Precision Handling: Microsecond precision (precision: 6) may require database columns like datetime(6) (MySQL) or TIMESTAMP(6) (PostgreSQL).

Performance

  • Batch Operations: The bundle processes timestamps per entity, not in bulk. For high-throughput operations (e.g., bulk inserts), consider disabling attributes and setting timestamps manually.
$entityManager->getEventManager()->removeEventListeners('prePersist');
$entityManager->getEventManager()->removeEventListeners('preUpdate');
// ... bulk operations ...
$entityManager->getEventManager()->addEventSubscriber(new DoctrineClockSubscriber($clock));
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky