xavierleune / ting
Ting : a lightweight datamapper
Requires
- php: >=8.2
- aura/sqlquery: ^2.6 || ^3.0
- symfony/cache-contracts: ^3.0
- symfony/property-access: ^7.0 || ^8.0
- symfony/property-info: ^6.4 || ^7.0 || ^8.0
Requires (Dev)
- ext-mysqli: *
- ext-pgsql: *
- brick/geo: ^0.5.1
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.5 || ^12.0
- rector/rector: ^2.1
- symfony/cache: ^7.0 || ^8.0
- symfony/uid: ^7.0 || ^8.0
- tomasvotruba/type-coverage: ^2.0
Suggests
- brick/geo: Allow support of MariaDB Geometry type
- symfony/cache: Cache pools (Redis, Memcached, APCu...) for cached queries.
- symfony/uid: To work with Uuid.
Provides
None
Conflicts
None
Replaces
- ccmbenchmark/ting: 4.0.0
- dev-main
- 4.0.0
- 4.0.0-rc.1
- 3.x-dev
- 3.15.0
- 3.14.0
- 3.13.0
- 3.12.1
- 3.12.0
- 3.11.0
- 3.10.6
- 3.10.5
- 3.10.4
- 3.10.3
- 3.10.2
- 3.10.1
- 3.10.0
- 3.9.1
- 3.9.0
- 3.8.1
- 3.8.0
- 3.7.3
- 3.7.2
- 3.7.1
- 3.7.0
- 3.6.2
- 3.6.1
- 3.6.0
- 3.5.8
- 3.5.7
- 3.5.6
- 3.5.5
- 3.5.4
- 3.5.3
- 3.5.2
- 3.5.1
- 3.5.0
- 3.5.0-beta.2
- 3.5.0-beta
- 3.4.4
- 3.4.3
- 3.4.2
- 3.4.1
- 3.4.0
- 3.3.0
- 3.2.1
- 3.2.0
- 3.1.1
- 3.1.0
- 3.0.0
- 2.7.0
- 2.6.2
- 2.6.1
- 2.6.0
- 2.5.1
- 2.5.0
- 2.4.0
- 2.3.0
- 2.2.0
- 2.1.0
- 2.0.0
- 1.1.5
- 1.1.4
- 1.1.3
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.0
- 1.0-beta
- 1.0-alpha
- dev-docs/readme-polish
- dev-perf/hydration
- dev-fix/transactional-immediately-invoked
- dev-feat/dx-lot4
- dev-feat/dx-lot3
- dev-feat/dx-lot2
- dev-feat/dx-lot1
- dev-docs/review13
- dev-fix/review12-drivers
- dev-fix/property-accessor-lowest
- dev-fix/review12-uow
- dev-fix/review12-relational
- dev-master
This package is auto-updated.
Last update: 2026-10-16 15:52:10 UTC
README
A data mapper for PHP built around your SQL. Plain PHP objects, explicit mapping and a unit of work for writes: you write the SQL, Ting maps the rows.
$cities = $cityRepository->getBy(['status' => CityStatus::Active], order: ['name' => 'ASC'], limit: 10);
Why Ting
- Fast and light. Rows are hydrated straight into plain PHP objects, prepared statements are reused, and only the properties you change are written.
- Your SQL, your database. Write MySQL, PostgreSQL or SphinxQL as you would in a console, and get entities back.
- Plain PHP objects. Your entities are your own classes with typed properties: dates, enums and JSON columns arrive as PHP values.
- Everyday reads in one line.
get,getBy,getOneBy,countBy,existsBy, with criteria, order and pagination. - Writes you control. The unit of work writes the changes your entities notify;
transactional()wraps them in a transaction. - Joins your way. Write the join, and the hydrators give you every object of the row.
- Typed parameters. Dates, backed enums and arrays (
INlists) are passed as they are.
Ting needs PHP 8.2 or later, with the mysqli or pgsql extension.
Installation
composer require xavierleune/ting
With Symfony, install ting_bundle: it wires Ting in the container and lets you map your entities with attributes, as below: follow its getting started for the configuration. Without a framework, see Getting started, which maps them in PHP.
Quick tour
Map an entity
With ting_bundle, an entity is a class with attributes, and the type of each column comes from the type of its
property. Ting writes the properties whose changes are notified: each mapped property calls propertyChanged(), here
in a PHP 8.4 property hook (a setter works too), see Unit of work.
use CCMBenchmark\Ting\Entity\NotifyProperty; use CCMBenchmark\Ting\Entity\NotifyPropertyInterface; use CCMBenchmark\Ting\Repository\Repository; use CCMBenchmark\TingBundle\Schema\Column; use CCMBenchmark\TingBundle\Schema\Table; enum CityStatus: string { case Active = 'active'; case Archived = 'archived'; } #[Table(name: 'city', repository: CityRepository::class)] class City implements NotifyPropertyInterface { use NotifyProperty; #[Column(autoIncrement: true, primary: true)] public ?int $id = null; #[Column] public string $name = '' { set(string $name) { $this->propertyChanged('name', $this->name, $name); $this->name = $name; } } #[Column] public CityStatus $status = CityStatus::Active { set(CityStatus $status) { $this->propertyChanged('status', $this->status, $status); $this->status = $status; } } } /** @extends Repository<City> */ class CityRepository extends Repository { }
Read
$city = $cityRepository->get(3); // by primary key, or null $paris = $cityRepository->getOneBy(['name' => 'Paris']); $page = $cityRepository->getBy(['status' => CityStatus::Active], order: ['name' => 'ASC'], limit: 20, offset: 40); $active = $cityRepository->countBy(['status' => CityStatus::Active]); $hasParis = $cityRepository->existsBy(['name' => 'Paris']);
Write
$city = new City(); $city->name = 'Lyon'; $cityRepository->save($city); // INSERT, then $city->id holds the generated id $city->name = 'Lyon 2e'; $cityRepository->save($city); // UPDATE city SET name = ... WHERE id = ...: only the changed column $cityRepository->delete($city); // Committed when the callback returns, rolled back when it throws $cityRepository->transactional(function () use ($cityRepository, $cities): void { foreach ($cities as $city) { $city->status = CityStatus::Archived; $cityRepository->save($city); } });
Write your own SQL
// Entities of the repository from a query of your own: dates, enums and arrays are converted $cities = $cityRepository->getBySql( 'SELECT id, name, status FROM city WHERE id IN (:ids) AND status = :status AND created_at > :since', ['ids' => [3, 7, 12], 'status' => CityStatus::Active, 'since' => new \DateTimeImmutable('-7 days')] ); // A single value $lastCreation = $cityRepository->getQuery('SELECT MAX(created_at) FROM city WHERE status = :status') ->setParams(['status' => CityStatus::Active]) ->queryValue();
Joins returning several entities, computed columns and aggregates go through a hydrator. When the SQL has to be built dynamically, an aura/sqlquery query builder is there: see Queries.
Documentation
- Documentation: entities, repositories, queries, hydrators, unit of work, cache
- Getting started without a framework
- ting_bundle for Symfony
- Samples
- Changelog
- Issues
- Contributing
Coming from ccmbenchmark/ting
Ting was created at CCM Benchmark Group, where it is no longer maintained
publicly; development continues here as xavierleune/ting. The PHP namespaces (CCMBenchmark\Ting\...) are
unchanged: to upgrade from 3.x, change the package name in your composer.json and follow
UPGRADE-4.0.md.