ccmbenchmark / ting_bundle
Symfony bundle for ccmbenchmark/ting. Provides an interface to use a lightweight datamapper in symfony.
Installs: 88 112
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Watchers: 5
Forks: 3
Open Issues: 3
Type:symfony-bundle
Requires
- php: >=8.1
- ccmbenchmark/ting: ^3.10
- doctrine/cache: ^1.10
- symfony/config: ^4.4 || ^5.0 || ^6.0 || ^7.0
- symfony/dependency-injection: ^4.4 || ^5.0 || ^6.0 || ^7.0
- symfony/http-kernel: ^4.4 || ^5.0 || ^6.0 || ^7.0
- symfony/stopwatch: ^4.4 || ^5.0 || ^6.0 || ^7.0
- symfony/validator: ^4.4 || ^5.0 || ^6.0 || ^7.0
Requires (Dev)
- atoum/atoum: ^4.2
- atoum/stubs: ^2.2
- brick/geo: >=0.5 <=1.0
- symfony/expression-language: ^6.3 || ^7.2
- symfony/security-bundle: ^6.0 || ^7.0
- symfony/uid: ^6.0 || ^7.0
- dev-master
- 3.9.0
- 3.8.2
- 3.8.1
- 3.8.0
- 3.7.0
- 3.6.1
- 3.6.0
- 3.5.0
- 3.4.2
- 3.4.1
- 3.4.0
- 3.3.2
- 3.3.1
- 3.3.0
- 3.2.3
- 3.2.2
- 3.2.1
- 3.2.0
- 3.1.0
- 3.0.1
- 3.0.0
- 2.2.0
- 2.1.0
- 2.0.5
- 2.0.4
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- 1.2.0
- 1.1.x-dev
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.0
- dev-feature/support-sf7
- dev-fix/fix-deprecated
- dev-release/3.6.1
- dev-release/3.6.0
- dev-fix/dynamic-property
- dev-feature/repository-configuration
- dev-feature/cache-clearer
- dev-ci/php8
- dev-fix/symfony5
- dev-feature/implement-LateDataCollectorInterface
- dev-feature/tests-workflows
- dev-fix-srm-library-type
- dev-feature/jv/test-ting-clean-1
This package is auto-updated.
Last update: 2025-01-17 13:19:00 UTC
README
- Require Ting Bundle with
composer require ccmbenchmark/ting_bundle
- Load Bundles in AppKernel.php
new CCMBenchmark\TingBundle\TingBundle(),
Table of contents
- Configuration
- Using Ting as a User Provider
- Declare a unique constraint
- Using Ting as a Value Resolver
Configuration
Main configuration
#!yaml
ting:
repositories: # Unnecessary if entities registered with attributes
Acme:
namespace: Acme\DemoBundle\Entity
directory: "@DemoBundle/Entity"
options:
#pass options to your repository
Acme\DemoBundle\BazRepository:
extra:
bar: hello
foo: world
Acme\DemoBundle\FooRepository:
extra:
bar: hello
foo: world
default:
connection: main
database: baz
connections:
main:
namespace: CCMBenchmark\Ting\Driver\Mysqli
master:
host: localhost
user: world_sample
password: world_sample
port: 3306
slaves:
slave1:
host: 127.0.0.1
user: world_sample_ro
password: world_sample_ro
port: 3306
slave2:
host: 127.0.1.1
user: world_sample_ro
password: world_sample_ro
port: 3306
databases_options:
baz:
timezone: 'Europe/Paris'
About public properties
Public properties can be used in your entities, however for PHP < 8.4, you should declare a setter to notify the property change.
PHP < 8.4:
<?php namespace App\Entity; use CCMBenchmark\Ting\Entity\NotifyProperty; use CCMBenchmark\Ting\Entity\NotifyPropertyInterface; class City implements NotifyPropertyInterface { use NotifyProperty; public string $name; public function setName(string $name): void { $this->propertyChanged('name', $this->name ?? null, $name); $this->name = $name; } }
For PHP >= 8.4, you may use a property hook instead. This hook will be bypassed by Ting for hydratation.
<?php namespace App\Entity; use CCMBenchmark\Ting\Entity\NotifyProperty; use CCMBenchmark\Ting\Entity\NotifyPropertyInterface; class City implements NotifyPropertyInterface { use NotifyProperty; public string $name { set(string $name) { $this->propertyChanged('name', $this->name ?? null, $name); $this->name = $name; } }; }
A note about uninitialized typed properties
- When persisting an entity with uninitialized typed property, the property will be ignored ; a default value must be defined in your database for this column to prevent a failure
- You cannot access an uninitialized typed property, PHP will trigger an error
Declare metadata with attributes
Attributes are provided to declare an entity. Relevant attributes are available in CCMBenchmark\TingBundle\Schema
.
Table
- Full name:
CCMBenchmark\TingBundle\Schema\Table
- This attribute must be added to your class, with all relevant options (table, connection, etc.).
Column
- Full name:
CCMBenchmark\TingBundle\Schema\Column
- This attribute must be added to every property mapped to the database. Serialization is inferred from the type, if available.
Full example
// src/Entity/City.php <?php namespace App\Entity; namespace tests\fixtures; use App\Repository\CityRepository; use Brick\Geo\Point; use CCMBenchmark\Ting\Entity\NotifyProperty; use CCMBenchmark\Ting\Entity\NotifyPropertyInterface; use CCMBenchmark\TingBundle\Schema; use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\UuidV4; #[Schema\Table('city_table', 'connectionName', '%env(DATABASE_NAME)%', CityRepository::class)] class City implements NotifyPropertyInterface { use NotifyProperty; #[Schema\Column(autoIncrement: true, primary: true)] public int $id { set(int $id) { $this->propertyChanged('id', $this->id ?? null, $id); $this->id = $id; } }; #[Schema\Column(column: 'field')] public string $fieldWithSpecifiedColumnName { set (string $fieldWithSpecifiedColumnName) { $this->propertyChanged('fieldWithSpecifiedColumnName', $this->fieldWithSpecifiedColumnName ?? null, $fieldWithSpecifiedColumnName); $this->fieldWithSpecifiedColumnName = $fieldWithSpecifiedColumnName; } }; }
// src/Repository/CityRepository.php <?php namespace App\Repository; class CityRepository extends CCMBenchmark\Ting\Repository\Repository { }
Using Ting as a User Provider
User providers (re)load users from a storage based on a "user identifier" (extract from symfony documentation).
Ting can be used as a User Provider, it's automatically registered by the bundle as the provider ting
. To do so, update your security configuration.
security: # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider providers: app_user_provider: ting: class: App\Entity\User property: email
Your entity will have to implements the following interfaces: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface
(for password authenticated users) and Symfony\Component\Security\Core\User\UserInterface
(common to all kind of users).
It needs to implement __serialize
too.
Declare a Unique constraint in a table
If you use the component symfony/validator
, you may need to ensure that a value (or a combination of them) is unique in your table.
You can use the Constraint CCMBenchmark\TingBundle\Validator\Constraints\UniqueEntity
to do so. In can be used as an annotation, or as an attribute.
Example:
namespace App\Entity; use App\Repository\UserRepository; use CCMBenchmark\Ting\Entity\NotifyProperty; use CCMBenchmark\Ting\Entity\NotifyPropertyInterface; use CCMBenchmark\TingBundle\Schema\Column; use CCMBenchmark\TingBundle\Schema\Table; use CCMBenchmark\TingBundle\Validator\Constraints\UniqueEntity; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Serializer\Attribute\Groups; use Symfony\Component\Validator\Constraints as Assert; #[Table(name: 'users', connection: 'main', database: '%env(DATABASE_DB_NAME)%', repository: UserRepository::class)] #[UniqueEntity(options:['repository' => UserRepository::class, 'fields' => ['email']], groups: ['create'])] class User implements UserInterface, NotifyPropertyInterface { #[Column(autoIncrement: true, primary: true)] public int $id { set(int $id) { $this->propertyChanged('id', $this->id ?? null, $id); $this->id = $id; }} #[Column] #[Groups(['default', 'create', 'update', 'service_account'])] #[Assert\NotBlank(groups: ["default", "create", "update"])] #[Assert\Email(groups: ["default", "create", "update"])] public string $email { set(string $email) { $this->propertyChanged('email', $this->email ?? null, $email); $this->email = $email; } } public function getRoles(): array { return ['ROLE_USER']; } public function eraseCredentials(): void { } public function getUserIdentifier(): string { return $this->email; } public function __serialize(): array { return [ 'id' => $this->id, 'email' => $this->email, ]; } }
With that example you can assert, when creating a new user, that the email address is unique:
<?php namespace App\Controller; use App\Entity\User; use App\Repository\UserRepository; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Validator\Validator\ValidatorInterface; #[Route('/users', methods:['POST'], format: 'json')] class createUserController extends AbstractController { public function __construct (private readonly ValidatorInterface $validator, private readonly UserRepository $userRepository) { } public function __invoke( #[MapRequestPayload(serializationContext: ['groups' => ['create']])] User $user ) :JsonResponse { $violations = $this->validator->validate($user, groups: ['create']); if ($violations->count() > 0) { return new JsonResponse(['message' => 'Errors...'], 422); } $this->userRepository->save($user); return new JsonResponse(['message' => 'User registered'], 201); } }
Using Ting as a Value Resolver
This bundle automatically registers a Value Resolver.
You can automatically map request parameters to entities:
- Declare a parameter in your route (i.e:
/api/users/{userId}
), using the property in your entity you'll use to fetch data (in this case:userId
) - Map it to your action parameters:
- Add it to your signature:
public function getUser(User $user)
- Update the route to do the mapping:
/api/users/{userId:user}
(in this case: theUser
havinguserId
matching the request will be fetched and injected to your action with the argument$user
)
- Add it to your signature:
<?php namespace App\Controller; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; class UserController { #[Route('/api/users/{userId:myUser}', name: 'get_user', methods: ['GET'], format: 'json' )] #[IsGranted('ROLE_USER')] public function getUser(User $user): JsonResponse { return new JsonResponse($this->serializer->serialize($user, 'json', ['groups' => 'default']), json: true); } }
For more advanced use cases, you can leverage:
- The Expression Language component
- The
CCMBenchmark\TingBundle\Attribute\MapEntity
attribute
Example:
<?php namespace App\Controller; use CCMBenchmark\TingBundle\Attribute\MapEntity; use Symfony\Component\Security\Http\Attribute\IsGranted; class UserController { #[Route('/api/users/{firstname}/{lastname}', name: 'get_user', methods: ['GET'], format: 'json' )] #[IsGranted('ROLE_USER')] public function getUser(#[MapEntity(expr: 'repository.getOneBy({"firstname": firstname, "lastname": lastname}')] User $user): JsonResponse { return new JsonResponse($this->serializer->serialize($user, 'json', ['groups' => 'default']), json: true); } }