Installation Run:
composer require dk/calculator-bundle:dev-master
Register the bundle in config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 3-):
DK\CalculatorBundle\DKCalculatorBundle::class => ['all' => true],
Define a Calculated Property
Annotate a Doctrine entity property with @DK\CalculatorBundle\Annotation\Calculator:
use DK\CalculatorBundle\Annotation\Calculator;
/**
* @Entity
*/
class User
{
/**
* @var float
* @Calculator(query="SELECT SUM(t.amount) FROM App\Entity\Transaction t WHERE t.user = user")
*/
private $balance;
}
First Use Case
Fetch a User entity via Doctrine (e.g., UserRepository::find(1)). The balance property will be populated automatically on hydration.
Dynamic Calculations Use DQL expressions to compute properties dynamically:
/**
* @Calculator(query="SELECT AVG(r.rating) FROM App\Entity\Review r WHERE r.product = product")
*/
private $averageRating;
Conditional Logic
Filter calculations with WHERE clauses:
/**
* @Calculator(query="SELECT COUNT(t) FROM App\Entity\Transaction t WHERE t.user = user AND t.status = 'completed'")
*/
private $completedTransactions;
Aggregations
Support for SUM, AVG, COUNT, etc.:
/**
* @Calculator(query="SELECT COUNT(DISTINCT t.category) FROM App\Entity\Transaction t WHERE t.user = user")
*/
private $transactionCategoriesCount;
Joins and Subqueries Complex relationships via joins:
/**
* @Calculator(query="SELECT SUM(o.total) FROM App\Entity\Order o JOIN o.items i WHERE i.product = product")
*/
private $totalSales;
dk_calculator.cache in config/packages/dk_calculator.yaml).Calculator annotation or using a custom CalculatorListener.# config/serializer/Entity.User.yaml
App\Entity\User:
attributes:
balance:
groups: ['api']
use DK\CalculatorBundle\Event\CalculatorEvent;
public function onFlush(CalculatorEvent $event) {
$entity = $event->getEntity();
if ($entity instanceof User) {
$event->markDirty('balance'); // Force recalculation
}
}
N+1 Queries
JOIN FETCH or batch loading.Circular Dependencies
balance depending on totalIncome which depends on balance).Write Operations
Database-Specific DQL
Annotation Parsing
config/packages/framework.yaml:
framework:
annotations:
cache: true
// config/packages/dev/doctrine.yaml
doctrine:
dbal:
logging: true
profiling: true
CalculatorListener in tests to log queries:
$listener = new CalculatorListener($entityManager);
$listener->setDebug(true); // Hypothetical method
Cache Configuration
Enable caching in config/packages/dk_calculator.yaml:
dk_calculator:
cache: true
cache_lifetime: 3600 # Cache for 1 hour
Performance Tuning
INDEX hints in DQL for large datasets:
/**
* @Calculator(query="SELECT /*+ INDEX(t user_idx) */ SUM(t.amount) FROM App\Entity\Transaction t WHERE t.user = user")
*/
private $balance;
Custom Calculators
Extend the bundle by creating a custom Calculator type:
use DK\CalculatorBundle\Calculator\CalculatorInterface;
class CustomCalculator implements CalculatorInterface {
public function calculate(EntityManagerInterface $em, object $entity, string $property) {
// Custom logic
}
}
Register via dependency injection.
Event-Driven Recalculations
Listen to prePersist, preUpdate, or preRemove to invalidate cache:
$entityManager->getEventManager()->addEventListener(
[PrePersist::class, PreUpdate::class],
function (LifecycleEventArgs $args) {
$entity = $args->getObject();
if ($entity instanceof User) {
$entityManager->getCache()->delete('dk_calculator.User.balance.' . $entity->getId());
}
}
);
Bulk Calculations For performance-critical bulk operations, bypass the listener and use raw DQL:
$query = $em->createQuery('UPDATE App\Entity\User u SET u.balance = (
SELECT SUM(t.amount) FROM App\Entity\Transaction t WHERE t.user = u
)');
$query->execute();
How can I help you explore Laravel packages today?