Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: CI

on:
push:
branches: [ "main", "develop" ]
pull_request:
branches: [ "main", "develop" ]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['8.2', '8.4', '8.5']
symfony-version: ['5.4.*', '6.4.*', '7.0.*', '8.0.*']
doctrine-orm-version: ['2.20.*', '3.0.*']
exclude:
# Doctrine ORM 3.0 requires PHP 8.4+
- php-version: '8.2'
doctrine-orm-version: '3.0.*'
# Symfony 8.0 requires PHP 8.4+
- php-version: '8.2'
symfony-version: '8.0.*'
# Symfony 8 only works with doctrine-bundle 3.1+, which conflicts with ORM 2.x
- symfony-version: '8.0.*'
doctrine-orm-version: '2.20.*'

name: PHP ${{ matrix.php-version }} · SF ${{ matrix.symfony-version }} · Doctrine ORM ${{ matrix.doctrine-orm-version }}

steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: mbstring, xml, ctype, iconv, intl, json, pdo_sqlite, sqlite3
coverage: ${{ matrix.php-version == '8.5' && (matrix.symfony-version == '7.0.*' || matrix.symfony-version == '8.0.*') && matrix.doctrine-orm-version == '3.0.*' && 'pcov' || 'none' }}

- name: Constrain Symfony version
run: |
sed -ri 's/"symfony\/([^"]+)": "[^"]+"/"symfony\/\1": "${{ matrix.symfony-version }}"/g' composer.json
# symfony/clock does not exist for 5.4 (added in 6.2); keep it installable on 5.4 jobs
if [ "${{ matrix.symfony-version }}" = "5.4.*" ]; then
sed -ri 's/"symfony\/clock": "[^"]+"/"symfony\/clock": "^6.2 | ^7.0"/' composer.json
fi

- name: Constrain Doctrine ORM version
run: |
sed -ri 's/"doctrine\/orm": "[^"]+"/"doctrine\/orm": "${{ matrix.doctrine-orm-version }}"/' composer.json
# Symfony 8 requires doctrine-bundle 3.1+, which requires doctrine/persistence ^4. ORM 3.0/3.1 only support persistence ^3. Use ORM 3.5.* (supports ^3.3.1 || ^4) for SF 8.
if [ "${{ matrix.symfony-version }}" = "8.0.*" ] && [ "${{ matrix.doctrine-orm-version }}" = "3.0.*" ]; then
sed -ri 's/"doctrine\/orm": "[^"]+"/"doctrine\/orm": "3.5.*"/' composer.json
fi

- name: Install Composer dependencies
uses: ramsey/composer-install@v3
with:
composer-options: "--prefer-dist --no-progress --no-interaction --optimize-autoloader"

- name: Run PHP-CS-Fixer
if: matrix.php-version == '8.2' && matrix.symfony-version == '5.4.*' && matrix.doctrine-orm-version == '2.20.*'
run: vendor/bin/php-cs-fixer fix --dry-run --stop-on-violation --using-cache=no

- name: Run PHPStan
if: matrix.php-version == '8.5' && (matrix.symfony-version == '7.0.*' || matrix.symfony-version == '8.0.*') && matrix.doctrine-orm-version == '3.0.*'
run: vendor/bin/phpstan analyse src tests --configuration=phpstan.neon --memory-limit=1G

- name: Run PHPUnit tests
run: vendor/bin/phpunit ${{ matrix.php-version == '8.5' && (matrix.symfony-version == '7.0.*' || matrix.symfony-version == '8.0.*') && matrix.doctrine-orm-version == '3.0.*' && '--coverage-clover coverage.xml' || '' }}

- name: Upload coverage to Codecov
if: matrix.php-version == '8.5' && (matrix.symfony-version == '7.0.*' || matrix.symfony-version == '8.0.*') && matrix.doctrine-orm-version == '3.0.*'
uses: codecov/codecov-action@v4
with:
files: ./coverage.xml
fail_ci_if_error: false
62 changes: 0 additions & 62 deletions .github/workflows/workflow.yml

This file was deleted.

2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ composer.lock
!bin/symfony_requirements
/vendor/
/phpunit.xml
docker-compose.override.yml
coverage.xml
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.PHONY: setup php cs-fixer phpstan tests ci-local

setup:
rm -f composer.lock
docker-compose up --build -d php
docker-compose exec php composer install

php:
docker-compose exec php sh

cs-fixer:
docker-compose exec php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes

phpstan:
docker-compose exec php vendor/bin/phpstan analyse src tests --configuration=phpstan.neon --memory-limit=1G

tests:
rm -rf var/cache/test
mkdir -p var/cache/test
docker-compose exec php vendor/bin/phpunit

ci-local:
act -j build
85 changes: 41 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
# Timestampable Bundle
#### Symfony Bundle - [AndanteProject](https://github.com/andanteproject)
[![Latest Version](https://img.shields.io/github/release/andanteproject/timestampable-bundle.svg)](https://github.com/andanteproject/timestampable-bundle/releases)
![Github actions](https://github.com/andanteproject/timestampable-bundle/actions/workflows/workflow.yml/badge.svg?branch=main)
![Framework](https://img.shields.io/badge/Symfony-4.x|5.x|6.x|7.x-informational?Style=flat&logo=symfony)
![Github actions](https://github.com/andanteproject/timestampable-bundle/actions/workflows/ci.yml/badge.svg?branch=main)
![Framework](https://img.shields.io/badge/Symfony-5.x|6.x|7.x|8.x-informational?Style=flat&logo=symfony)
![Php8](https://img.shields.io/badge/PHP-%208.x-informational?style=flat&logo=php)
![PhpStan](https://img.shields.io/badge/PHPStan-Level%208-syccess?style=flat&logo=php)
![PhpStan](https://img.shields.io/badge/PHPStan-Level%208-success?style=flat&logo=php)

A Symfony Bundle to handle entities createdAt and updatedAt dates with Doctrine. 🕰
A Symfony Bundle to handle entity `createdAt` and `updatedAt` dates with Doctrine. 🕰

## Requirements
Symfony 4.x-7.x and PHP 8.2.
Symfony 5.x–8.x and PHP 8.2.

## Install
Via [Composer](https://getcomposer.org/):
Expand All @@ -19,27 +19,26 @@ $ composer require andanteproject/timestampable-bundle
```

## Features
- No configuration required to be ready to go but fully customizabile;
- No configuration required to get started; fully customizable;
- `createdAt` and `updatedAt` properties are `?\DateTimeImmutable`;
- Uses [Symfony Clock](https://symfony.com/doc/current/components/clock.html);
- Does not override your `createdAt` and `updatedAt` values when you set them explicitly;
- No annotation/attributes required;
- No annotations or attributes required;
- Works like magic ✨.

## Basic usage
After [install](#install), make sure you have the bundle registered in your symfony bundles list (`config/bundles.php`):
After [install](#install), ensure the bundle is registered in your Symfony bundles list (`config/bundles.php`):
```php
return [
/// bundles...
// bundles...
Andante\TimestampableBundle\AndanteTimestampableBundle::class => ['all' => true],
/// bundles...
// bundles...
];
```
This should have been done automagically if you are using [Symfony Flex](https://flex.symfony.com). Otherwise, just register it by yourself.
This is done automatically if you use [Symfony Flex](https://flex.symfony.com). Otherwise, register it manually.


Let's suppose we have a `App\Entity\Article` doctrine entity we want to track created and update dates.
All you have to do is to implement `Andante\TimestampableBundle\Timestampable\TimestampableInterface` and use `Andante\TimestampableBundle\Timestampable\TimestampableTrait` trait.
Suppose you have an `App\Entity\Article` Doctrine entity and want to track created and updated dates.
All you need to do is implement `Andante\TimestampableBundle\Timestampable\TimestampableInterface` and use the `Andante\TimestampableBundle\Timestampable\TimestampableTrait` trait.

```php
<?php
Expand Down Expand Up @@ -75,26 +74,25 @@ class Article implements TimestampableInterface // <-- implement this
}

// ...
// Some others beautiful properties and methods ...
// Other properties and methods ...
// ...
}
```
Make sure to update you database schema following your doctrine workflow (`bin/console doctrine:schema:update --force` if you are a badass devil guy or with a [migration](https://www.doctrine-project.org/projects/doctrine-migrations/en/3.0/reference/introduction.html) if you choosed the be a better developer!).
Update your database schema using your usual Doctrine workflow (e.g. `bin/console doctrine:schema:update --force`, or use [migrations](https://www.doctrine-project.org/projects/doctrine-migrations/en/3.0/reference/introduction.html) for a safer approach).

You shoud see a new columns named `created_at` and `updated_at` ([can i change this?](#configuration-completely-optional)) or something similar based on your [doctrine naming strategy](https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/reference/namingstrategy.html).
You should see new columns named `created_at` and `updated_at` ([can I change this?](#configuration-completely-optional)), or similar names depending on your [Doctrine naming strategy](https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/reference/namingstrategy.html).

#### Congrats! You're done! 🎉
#### You're done! 🎉

Remember that `TimestampableInterface` and `TimestampableTrait` are shortcut to use `CreatedAtTimestampableInterface`+`CreatedAtTimestampableTrait` and `UpdatedAtTimestampableInterface`+`UpdatedAtTimestampableTrait` at the same time!
If you need to track only **create date** or **update date** you can use these more specific interfaces!
`TimestampableInterface` and `TimestampableTrait` are shortcuts that combine `CreatedAtTimestampableInterface` + `CreatedAtTimestampableTrait` and `UpdatedAtTimestampableInterface` + `UpdatedAtTimestampableTrait`. To track only **created** or **updated** dates, use the more specific interfaces and traits below.

| To keep track of | Implement interface | Use this Trait |
| To track | Implement | Use trait |
| --- | --- | --- |
| **Create date** | `Andante\TimestampableBundle\Timestampable\CreatedAtTimestampableInterface` | `Andante\TimestampableBundle\Timestampable\CreatedAtTimestampableTrait` |
| **Update date** | `Andante\TimestampableBundle\Timestampable\UpdatedAtTimestampableInterface` | `Andante\TimestampableBundle\Timestampable\UpdatedAtTimestampableTrait` |
| **Both create and update dates** | `Andante\TimestampableBundle\Timestampable\TimestampableInterface` | `Andante\TimestampableBundle\Timestampable\TimestampableTrait` |
| **Created date only** | `Andante\TimestampableBundle\Timestampable\CreatedAtTimestampableInterface` | `Andante\TimestampableBundle\Timestampable\CreatedAtTimestampableTrait` |
| **Updated date only** | `Andante\TimestampableBundle\Timestampable\UpdatedAtTimestampableInterface` | `Andante\TimestampableBundle\Timestampable\UpdatedAtTimestampableTrait` |
| **Both** | `Andante\TimestampableBundle\Timestampable\TimestampableInterface` | `Andante\TimestampableBundle\Timestampable\TimestampableTrait` |

## Usage with no trait
## Usage without the trait
```php
<?php

Expand Down Expand Up @@ -122,7 +120,7 @@ class Article implements TimestampableInterface // <-- implement this
*/
private string $title;

// DO NOT use ORM annotations to map these properties. See bundle configuration section for more info
// DO NOT use ORM annotations to map these properties. See the configuration section for details.
private ?\DateTimeImmutable $createdAt = null;
private ?\DateTimeImmutable $updatedAt = null;

Expand Down Expand Up @@ -152,32 +150,31 @@ class Article implements TimestampableInterface // <-- implement this
}
}
```
This allows you to, for instance, to have **a different name** for your properties (E.g. `created` instead of `createdAt` and `updated` instead of `updatedAt`).
But you will need to explicit this in [bundle configuration](#configuration-completely-optional).
This lets you use different property names (e.g. `created` and `updated` instead of `createdAt` and `updatedAt`). You must specify these in the [bundle configuration](#configuration-completely-optional).

## Configuration (completely optional)
This bundle is build thinking how to save you time and follow best practices as close as possible.
This bundle is built to save you time and follow best practices out of the box.

This means you can even ignore to have a `andante_timestampable.yml` config file in your application.
You do not need an `andante_timestampable.yml` config file in your application.

However, for whatever reason (legacy code?), use the bundle configuration to change most of the behaviors as your needs.
If you need to customize it (e.g. for legacy code), you can change most behavior via the bundle configuration:
```yaml
andante_timestampable:
default:
created_at_property_name: createdAt # default: createdAt
# The property to be used by default as createdAt date inside entities
# implementing CreatedAtTimestampableInterface or TimestampableInterface
# Default property for createdAt in entities implementing
# CreatedAtTimestampableInterface or TimestampableInterface
updated_at_property_name: updatedAt # default: updatedAt
# The property to be used by default as updatedAt date inside entities
# implementing UpdatedAtTimestampableInterface or TimestampableInterface

created_at_column_name: created_at # default: null
# Column name to be used on database for create date.
# If set to NULL will use your default doctrine naming strategy
updated_at_column_name: updated_at # default: null
# Column name to be used on database for update date.
# If set to NULL will use your default doctrine naming strategy
entity: # You can use per-entity configuration to override default config
# Default property for updatedAt in entities implementing
# UpdatedAtTimestampableInterface or TimestampableInterface

created_at_column_name: created_at # default: null
# Database column name for the created date.
# If null, your Doctrine naming strategy is used
updated_at_column_name: updated_at # default: null
# Database column name for the updated date.
# If null, your Doctrine naming strategy is used
entity: # Per-entity overrides
Andante\TimestampableBundle\Tests\Fixtures\Entity\Organization:
created_at_property_name: createdAt
Andante\TimestampableBundle\Tests\Fixtures\Entity\Address:
Expand All @@ -187,4 +184,4 @@ andante_timestampable:
updated_at_column_name: updated_date
```

Built with love ❤️ by [AndanteProject](https://github.com/andanteproject) team.
Built with ❤️ by the [Andante Project](https://github.com/andanteproject) team.
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@
],
"require": {
"php": "^8.2",
"symfony/framework-bundle": "^5.0 | ^6.0 | ^7.0",
"symfony/framework-bundle": "^5.0 | ^6.0 | ^7.0 | ^8.0",
"doctrine/common": "^2.13 || ^3.0",
"doctrine/doctrine-bundle": "^2.10 || ^3.0",
"doctrine/event-manager": "^1.2 | ^2.0",
"symfony/clock": "^6.2 | ^7.0"
"doctrine/orm": "^2.15.3 || ^3.0",
"symfony/clock": "^6.2 | ^7.0 | ^8.0"
},
"require-dev": {
"ext-json": "*",
"roave/security-advisories": "dev-master",
"doctrine/orm": "^2.15.3",
"phpunit/phpunit": "^9.5",
"doctrine/doctrine-bundle": "^2.10",
"phpstan/phpstan": "^1.2",
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/extension-installer": "^1.1",
Expand Down
8 changes: 8 additions & 0 deletions docker-compose.override.yml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
services:
php:
environment:
XDEBUG_MODE: develop,debug
XDEBUG_CLIENT_HOST: host.docker.internal
XDEBUG_CLIENT_PORT: 9090
PHP_IDE_CONFIG: serverName=andanteproject-timestampable-bundle
XDEBUG_START_WITH_REQUEST: yes
13 changes: 13 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
services:
php:
build:
context: .
dockerfile: docker/Dockerfile.php82
volumes:
- .:/var/www/html
environment:
XDEBUG_MODE: ${XDEBUG_MODE:-develop,debug}
XDEBUG_CLIENT_HOST: ${XDEBUG_CLIENT_HOST:-host.docker.internal}
XDEBUG_CLIENT_PORT: ${XDEBUG_CLIENT_PORT:-9090}
PHP_IDE_CONFIG: ${PHP_IDE_CONFIG:-serverName=andanteproject-timestampable-bundle}
XDEBUG_START_WITH_REQUEST: ${XDEBUG_START_WITH_REQUEST:-yes}
Loading